aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock87
-rw-r--r--crates/hir/src/code_model.rs2095
-rw-r--r--crates/hir/src/from_id.rs5
-rw-r--r--crates/hir/src/lib.rs2127
-rw-r--r--crates/hir/src/semantics.rs5
-rw-r--r--crates/hir/src/source_analyzer.rs5
-rw-r--r--crates/hir_def/Cargo.toml3
-rw-r--r--crates/hir_def/src/adt.rs21
-rw-r--r--crates/hir_def/src/attr.rs7
-rw-r--r--crates/hir_def/src/body.rs18
-rw-r--r--crates/hir_def/src/body/lower.rs149
-rw-r--r--crates/hir_def/src/body/scope.rs4
-rw-r--r--crates/hir_def/src/body/tests.rs3
-rw-r--r--crates/hir_def/src/body/tests/block.rs10
-rw-r--r--crates/hir_def/src/child_by_source.rs54
-rw-r--r--crates/hir_def/src/data.rs4
-rw-r--r--crates/hir_def/src/db.rs2
-rw-r--r--crates/hir_def/src/find_path.rs22
-rw-r--r--crates/hir_def/src/generics.rs4
-rw-r--r--crates/hir_def/src/import_map.rs101
-rw-r--r--crates/hir_def/src/item_scope.rs34
-rw-r--r--crates/hir_def/src/item_tree.rs1
-rw-r--r--crates/hir_def/src/item_tree/lower.rs2
-rw-r--r--crates/hir_def/src/lib.rs56
-rw-r--r--crates/hir_def/src/nameres/collector.rs46
-rw-r--r--crates/hir_def/src/nameres/mod_resolution.rs3
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs11
-rw-r--r--crates/hir_def/src/nameres/tests.rs9
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs5
-rw-r--r--crates/hir_def/src/nameres/tests/globs.rs8
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs10
-rw-r--r--crates/hir_def/src/nameres/tests/mod_resolution.rs4
-rw-r--r--crates/hir_def/src/path/lower/lower_use.rs3
-rw-r--r--crates/hir_def/src/resolver.rs53
-rw-r--r--crates/hir_expand/Cargo.toml4
-rw-r--r--crates/hir_ty/Cargo.toml9
-rw-r--r--crates/hir_ty/src/db.rs2
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs22
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs14
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs6
-rw-r--r--crates/hir_ty/src/display.rs25
-rw-r--r--crates/hir_ty/src/infer/coerce.rs5
-rw-r--r--crates/hir_ty/src/infer/expr.rs27
-rw-r--r--crates/hir_ty/src/infer/pat.rs5
-rw-r--r--crates/hir_ty/src/infer/path.rs2
-rw-r--r--crates/hir_ty/src/infer/unify.rs8
-rw-r--r--crates/hir_ty/src/lib.rs22
-rw-r--r--crates/hir_ty/src/lower.rs11
-rw-r--r--crates/hir_ty/src/method_resolution.rs20
-rw-r--r--crates/hir_ty/src/tests.rs20
-rw-r--r--crates/hir_ty/src/tests/coercion.rs5
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs2
-rw-r--r--crates/hir_ty/src/tests/patterns.rs3
-rw-r--r--crates/hir_ty/src/tests/regression.rs7
-rw-r--r--crates/hir_ty/src/tests/simple.rs3
-rw-r--r--crates/hir_ty/src/tests/traits.rs38
-rw-r--r--crates/hir_ty/src/traits/chalk.rs18
-rw-r--r--crates/hir_ty/src/traits/chalk/mapping.rs16
-rw-r--r--crates/hir_ty/src/utils.rs2
-rw-r--r--crates/ide/Cargo.toml3
-rw-r--r--crates/ide/src/display/short_label.rs6
-rw-r--r--crates/ide/src/hover.rs71
-rw-r--r--crates/ide/src/join_lines.rs8
-rw-r--r--crates/ide/src/lib.rs2
-rw-r--r--crates/ide/src/matching_brace.rs5
-rw-r--r--crates/ide/src/parent_module.rs6
-rw-r--r--crates/ide/src/references/rename.rs58
-rw-r--r--crates/ide/src/runnables.rs10
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs5
-rw-r--r--crates/ide/src/typing/on_enter.rs8
-rw-r--r--crates/ide_assists/Cargo.toml3
-rw-r--r--crates/ide_assists/src/handlers/add_turbo_fish.rs22
-rw-r--r--crates/ide_assists/src/handlers/apply_demorgan.rs10
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs51
-rw-r--r--crates/ide_assists/src/handlers/change_visibility.rs7
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs31
-rw-r--r--crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs6
-rw-r--r--crates/ide_assists/src/handlers/extract_variable.rs15
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs6
-rw-r--r--crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs11
-rw-r--r--crates/ide_assists/src/handlers/generate_default_from_new.rs373
-rw-r--r--crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs7
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs129
-rw-r--r--crates/ide_assists/src/handlers/infer_function_return_type.rs37
-rw-r--r--crates/ide_assists/src/handlers/inline_function.rs5
-rw-r--r--crates/ide_assists/src/handlers/inline_local_variable.rs19
-rw-r--r--crates/ide_assists/src/handlers/move_module_to_file.rs5
-rw-r--r--crates/ide_assists/src/handlers/pull_assignment_up.rs52
-rw-r--r--crates/ide_assists/src/handlers/qualify_path.rs63
-rw-r--r--crates/ide_assists/src/handlers/raw_string.rs7
-rw-r--r--crates/ide_assists/src/handlers/remove_unused_param.rs6
-rw-r--r--crates/ide_assists/src/handlers/reorder_fields.rs7
-rw-r--r--crates/ide_assists/src/handlers/reorder_impl.rs7
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs33
-rw-r--r--crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs5
-rw-r--r--crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs7
-rw-r--r--crates/ide_assists/src/handlers/replace_string_with_char.rs136
-rw-r--r--crates/ide_assists/src/handlers/unmerge_use.rs5
-rw-r--r--crates/ide_assists/src/handlers/wrap_return_type_in_result.rs5
-rw-r--r--crates/ide_assists/src/lib.rs2
-rw-r--r--crates/ide_assists/src/tests.rs1
-rw-r--r--crates/ide_assists/src/tests/generated.rs31
-rw-r--r--crates/ide_completion/Cargo.toml3
-rw-r--r--crates/ide_completion/src/completions/dot.rs6
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs280
-rw-r--r--crates/ide_completion/src/completions/keyword.rs14
-rw-r--r--crates/ide_completion/src/completions/postfix.rs14
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs6
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs12
-rw-r--r--crates/ide_completion/src/context.rs4
-rw-r--r--crates/ide_completion/src/item.rs134
-rw-r--r--crates/ide_completion/src/lib.rs33
-rw-r--r--crates/ide_completion/src/render.rs218
-rw-r--r--crates/ide_completion/src/render/builder_ext.rs9
-rw-r--r--crates/ide_completion/src/render/enum_variant.rs7
-rw-r--r--crates/ide_completion/src/render/function.rs11
-rw-r--r--crates/ide_completion/src/render/macro_.rs7
-rw-r--r--crates/ide_completion/src/test_utils.rs3
-rw-r--r--crates/ide_db/Cargo.toml3
-rw-r--r--crates/ide_db/src/call_info.rs5
-rw-r--r--crates/ide_db/src/call_info/tests.rs4
-rw-r--r--crates/ide_db/src/helpers.rs10
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs578
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs24
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs44
-rw-r--r--crates/ide_db/src/items_locator.rs (renamed from crates/ide_db/src/imports_locator.rs)79
-rw-r--r--crates/ide_db/src/lib.rs2
-rw-r--r--crates/ide_ssr/Cargo.toml3
-rw-r--r--crates/ide_ssr/src/matching.rs3
-rw-r--r--crates/ide_ssr/src/parsing.rs3
-rw-r--r--crates/ide_ssr/src/replacing.rs4
-rw-r--r--crates/ide_ssr/src/resolving.rs9
-rw-r--r--crates/ide_ssr/src/search.rs5
-rw-r--r--crates/ide_ssr/src/tests.rs20
-rw-r--r--crates/mbe/Cargo.toml3
-rw-r--r--crates/mbe/src/lib.rs5
-rw-r--r--crates/mbe/src/tests.rs9
-rw-r--r--crates/parser/src/grammar.rs3
-rw-r--r--crates/proc_macro_srv/Cargo.toml2
-rw-r--r--crates/proc_macro_srv/src/dylib.rs6
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/client.rs2
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/mod.rs2
-rw-r--r--crates/proc_macro_srv/src/rustc_server.rs221
-rw-r--r--crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt2
-rw-r--r--crates/proc_macro_srv/src/tests/utils.rs2
-rw-r--r--crates/project_model/src/cargo_workspace.rs23
-rw-r--r--crates/project_model/src/workspace.rs138
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/bin/flags.rs15
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs6
-rw-r--r--crates/rust-analyzer/src/config.rs81
-rw-r--r--crates/rust-analyzer/src/handlers.rs31
-rw-r--r--crates/rust-analyzer/src/main_loop.rs2
-rw-r--r--crates/rust-analyzer/src/reload.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs84
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs97
-rw-r--r--crates/syntax/Cargo.toml3
-rw-r--r--crates/syntax/src/algo.rs26
-rw-r--r--crates/syntax/src/ast/make.rs4
-rw-r--r--crates/syntax/src/lib.rs1
-rw-r--r--crates/syntax/src/tests.rs6
-rw-r--r--crates/syntax/src/utils.rs43
-rw-r--r--crates/test_utils/Cargo.toml1
-rw-r--r--crates/test_utils/src/bench_fixture.rs6
-rw-r--r--crates/test_utils/src/lib.rs142
-rw-r--r--crates/test_utils/src/mark.rs78
-rw-r--r--docs/dev/architecture.md5
-rw-r--r--docs/dev/lsp-extensions.md20
-rw-r--r--docs/dev/style.md2
-rw-r--r--docs/user/generated_config.adoc324
-rw-r--r--docs/user/manual.adoc2
-rw-r--r--editors/code/package-lock.json17
-rw-r--r--editors/code/package.json62
-rw-r--r--editors/code/src/config.ts8
-rw-r--r--editors/code/src/main.ts6
-rw-r--r--editors/code/src/net.ts25
-rw-r--r--xtask/Cargo.toml2
-rw-r--r--xtask/src/codegen.rs78
-rw-r--r--xtask/src/codegen/gen_assists_docs.rs20
-rw-r--r--xtask/src/codegen/gen_diagnostic_docs.rs8
-rw-r--r--xtask/src/codegen/gen_feature_docs.rs8
-rw-r--r--xtask/src/codegen/gen_lint_completions.rs12
-rw-r--r--xtask/src/codegen/gen_parser_tests.rs12
-rw-r--r--xtask/src/codegen/gen_syntax.rs10
-rw-r--r--xtask/src/flags.rs51
-rw-r--r--xtask/src/install.rs120
-rw-r--r--xtask/src/main.rs45
-rw-r--r--xtask/src/release.rs5
-rw-r--r--xtask/src/tidy.rs50
189 files changed, 5636 insertions, 4414 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 39f43ba17..87cf1bf27 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -113,9 +113,9 @@ checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
113 113
114[[package]] 114[[package]]
115name = "camino" 115name = "camino"
116version = "1.0.1" 116version = "1.0.2"
117source = "registry+https://github.com/rust-lang/crates.io-index" 117source = "registry+https://github.com/rust-lang/crates.io-index"
118checksum = "9bb47ab72bdba43021afa16dc1ef4d80c980d366b17ed37ea8d2ebe2087075b9" 118checksum = "cd065703998b183ed0b348a22555691373a9345a1431141e5778b48bb17e4703"
119dependencies = [ 119dependencies = [
120 "serde", 120 "serde",
121] 121]
@@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
168 168
169[[package]] 169[[package]]
170name = "chalk-derive" 170name = "chalk-derive"
171version = "0.59.0" 171version = "0.60.0"
172source = "registry+https://github.com/rust-lang/crates.io-index" 172source = "registry+https://github.com/rust-lang/crates.io-index"
173checksum = "4b9000fbcb67353dc8973ab9fd136277d321d85b79bd36b8756bb3ae0979a94a" 173checksum = "ab0f74445d4fbeaf0217bc1d23978cc73b95b28e8a738b81894580dd646822d2"
174dependencies = [ 174dependencies = [
175 "proc-macro2", 175 "proc-macro2",
176 "quote", 176 "quote",
@@ -180,9 +180,9 @@ dependencies = [
180 180
181[[package]] 181[[package]]
182name = "chalk-ir" 182name = "chalk-ir"
183version = "0.59.0" 183version = "0.60.0"
184source = "registry+https://github.com/rust-lang/crates.io-index" 184source = "registry+https://github.com/rust-lang/crates.io-index"
185checksum = "b23528d61b3557c676eccf508fa0771a38453b379f0b780154eaa7f70afe8dfc" 185checksum = "294b1fc6210a5b3bd06c1d01dda48a581e2cafec80b8d659139ce45456644be2"
186dependencies = [ 186dependencies = [
187 "bitflags", 187 "bitflags",
188 "chalk-derive", 188 "chalk-derive",
@@ -191,9 +191,9 @@ dependencies = [
191 191
192[[package]] 192[[package]]
193name = "chalk-recursive" 193name = "chalk-recursive"
194version = "0.59.0" 194version = "0.60.0"
195source = "registry+https://github.com/rust-lang/crates.io-index" 195source = "registry+https://github.com/rust-lang/crates.io-index"
196checksum = "a8bdd37afc666b771de8b4429fe014363d0e74aae5cc26f320f60a3eab34d744" 196checksum = "1b9386936070be4545bfa22b094b7065af79aa2aeaccc945438f1c5ffe74c30a"
197dependencies = [ 197dependencies = [
198 "chalk-derive", 198 "chalk-derive",
199 "chalk-ir", 199 "chalk-ir",
@@ -204,9 +204,9 @@ dependencies = [
204 204
205[[package]] 205[[package]]
206name = "chalk-solve" 206name = "chalk-solve"
207version = "0.59.0" 207version = "0.60.0"
208source = "registry+https://github.com/rust-lang/crates.io-index" 208source = "registry+https://github.com/rust-lang/crates.io-index"
209checksum = "4182c42ca319cb71c89898ebc3d2671d1fa7d928123b171b66f1797a2000b9c8" 209checksum = "7c12a1ec7e850b50a049f27ef9cf5df3056bbd1acbb3eeb44d024e501a641f3a"
210dependencies = [ 210dependencies = [
211 "chalk-derive", 211 "chalk-derive",
212 "chalk-ir", 212 "chalk-ir",
@@ -232,15 +232,6 @@ dependencies = [
232] 232]
233 233
234[[package]] 234[[package]]
235name = "cmake"
236version = "0.1.45"
237source = "registry+https://github.com/rust-lang/crates.io-index"
238checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855"
239dependencies = [
240 "cc",
241]
242
243[[package]]
244name = "countme" 235name = "countme"
245version = "2.0.4" 236version = "2.0.4"
246source = "registry+https://github.com/rust-lang/crates.io-index" 237source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -252,6 +243,12 @@ dependencies = [
252] 243]
253 244
254[[package]] 245[[package]]
246name = "cov-mark"
247version = "1.1.0"
248source = "registry+https://github.com/rust-lang/crates.io-index"
249checksum = "9ffa3d3e0138386cd4361f63537765cac7ee40698028844635a54495a92f67f3"
250
251[[package]]
255name = "crc32fast" 252name = "crc32fast"
256version = "1.2.1" 253version = "1.2.1"
257source = "registry+https://github.com/rust-lang/crates.io-index" 254source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -501,6 +498,7 @@ dependencies = [
501 "anymap", 498 "anymap",
502 "base_db", 499 "base_db",
503 "cfg", 500 "cfg",
501 "cov-mark",
504 "drop_bomb", 502 "drop_bomb",
505 "either", 503 "either",
506 "expect-test", 504 "expect-test",
@@ -547,6 +545,7 @@ dependencies = [
547 "chalk-ir", 545 "chalk-ir",
548 "chalk-recursive", 546 "chalk-recursive",
549 "chalk-solve", 547 "chalk-solve",
548 "cov-mark",
550 "ena", 549 "ena",
551 "expect-test", 550 "expect-test",
552 "hir_def", 551 "hir_def",
@@ -581,6 +580,7 @@ name = "ide"
581version = "0.0.0" 580version = "0.0.0"
582dependencies = [ 581dependencies = [
583 "cfg", 582 "cfg",
583 "cov-mark",
584 "either", 584 "either",
585 "expect-test", 585 "expect-test",
586 "hir", 586 "hir",
@@ -607,6 +607,7 @@ dependencies = [
607name = "ide_assists" 607name = "ide_assists"
608version = "0.0.0" 608version = "0.0.0"
609dependencies = [ 609dependencies = [
610 "cov-mark",
610 "either", 611 "either",
611 "expect-test", 612 "expect-test",
612 "hir", 613 "hir",
@@ -625,6 +626,7 @@ name = "ide_completion"
625version = "0.0.0" 626version = "0.0.0"
626dependencies = [ 627dependencies = [
627 "base_db", 628 "base_db",
629 "cov-mark",
628 "either", 630 "either",
629 "expect-test", 631 "expect-test",
630 "hir", 632 "hir",
@@ -644,6 +646,7 @@ name = "ide_db"
644version = "0.0.0" 646version = "0.0.0"
645dependencies = [ 647dependencies = [
646 "base_db", 648 "base_db",
649 "cov-mark",
647 "either", 650 "either",
648 "expect-test", 651 "expect-test",
649 "fst", 652 "fst",
@@ -664,6 +667,7 @@ dependencies = [
664name = "ide_ssr" 667name = "ide_ssr"
665version = "0.0.0" 668version = "0.0.0"
666dependencies = [ 669dependencies = [
670 "cov-mark",
667 "expect-test", 671 "expect-test",
668 "hir", 672 "hir",
669 "ide_db", 673 "ide_db",
@@ -687,9 +691,9 @@ dependencies = [
687 691
688[[package]] 692[[package]]
689name = "indexmap" 693name = "indexmap"
690version = "1.6.1" 694version = "1.6.2"
691source = "registry+https://github.com/rust-lang/crates.io-index" 695source = "registry+https://github.com/rust-lang/crates.io-index"
692checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" 696checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
693dependencies = [ 697dependencies = [
694 "autocfg", 698 "autocfg",
695 "hashbrown", 699 "hashbrown",
@@ -789,9 +793,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
789 793
790[[package]] 794[[package]]
791name = "libc" 795name = "libc"
792version = "0.2.87" 796version = "0.2.88"
793source = "registry+https://github.com/rust-lang/crates.io-index" 797source = "registry+https://github.com/rust-lang/crates.io-index"
794checksum = "265d751d31d6780a3f956bb5b8022feba2d94eeee5a84ba64f4212eedca42213" 798checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a"
795 799
796[[package]] 800[[package]]
797name = "libloading" 801name = "libloading"
@@ -805,11 +809,11 @@ dependencies = [
805 809
806[[package]] 810[[package]]
807name = "libmimalloc-sys" 811name = "libmimalloc-sys"
808version = "0.1.20" 812version = "0.1.21"
809source = "registry+https://github.com/rust-lang/crates.io-index" 813source = "registry+https://github.com/rust-lang/crates.io-index"
810checksum = "e58f42b6424a0ed536678c65fd97cd64b4344bcf86251e284f7c0ce9eee40e64" 814checksum = "2396cf99d2f58611cd69f0efeee4af3d2e2c7b61bed433515029163aa567e65c"
811dependencies = [ 815dependencies = [
812 "cmake", 816 "cc",
813] 817]
814 818
815[[package]] 819[[package]]
@@ -874,6 +878,7 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
874name = "mbe" 878name = "mbe"
875version = "0.0.0" 879version = "0.0.0"
876dependencies = [ 880dependencies = [
881 "cov-mark",
877 "log", 882 "log",
878 "parser", 883 "parser",
879 "profile", 884 "profile",
@@ -921,9 +926,9 @@ dependencies = [
921 926
922[[package]] 927[[package]]
923name = "mimalloc" 928name = "mimalloc"
924version = "0.1.24" 929version = "0.1.25"
925source = "registry+https://github.com/rust-lang/crates.io-index" 930source = "registry+https://github.com/rust-lang/crates.io-index"
926checksum = "757efec188b3d2088949d912e01ea2fe87164ed6376b6c5d7dd4f3ce1668a93d" 931checksum = "1e7c6b11afd1e5e689ac96b6d18b1fc763398fe3d7eed99e8773426bc2033dfb"
927dependencies = [ 932dependencies = [
928 "libmimalloc-sys", 933 "libmimalloc-sys",
929] 934]
@@ -1137,9 +1142,9 @@ dependencies = [
1137 1142
1138[[package]] 1143[[package]]
1139name = "pin-project-lite" 1144name = "pin-project-lite"
1140version = "0.2.5" 1145version = "0.2.6"
1141source = "registry+https://github.com/rust-lang/crates.io-index" 1146source = "registry+https://github.com/rust-lang/crates.io-index"
1142checksum = "0cf491442e4b033ed1c722cb9f0df5fcfcf4de682466c46469c36bc47dc5548a" 1147checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905"
1143 1148
1144[[package]] 1149[[package]]
1145name = "proc-macro-hack" 1150name = "proc-macro-hack"
@@ -1479,18 +1484,18 @@ dependencies = [
1479 1484
1480[[package]] 1485[[package]]
1481name = "serde" 1486name = "serde"
1482version = "1.0.123" 1487version = "1.0.124"
1483source = "registry+https://github.com/rust-lang/crates.io-index" 1488source = "registry+https://github.com/rust-lang/crates.io-index"
1484checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" 1489checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f"
1485dependencies = [ 1490dependencies = [
1486 "serde_derive", 1491 "serde_derive",
1487] 1492]
1488 1493
1489[[package]] 1494[[package]]
1490name = "serde_derive" 1495name = "serde_derive"
1491version = "1.0.123" 1496version = "1.0.124"
1492source = "registry+https://github.com/rust-lang/crates.io-index" 1497source = "registry+https://github.com/rust-lang/crates.io-index"
1493checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" 1498checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b"
1494dependencies = [ 1499dependencies = [
1495 "proc-macro2", 1500 "proc-macro2",
1496 "quote", 1501 "quote",
@@ -1580,9 +1585,9 @@ dependencies = [
1580 1585
1581[[package]] 1586[[package]]
1582name = "syn" 1587name = "syn"
1583version = "1.0.60" 1588version = "1.0.62"
1584source = "registry+https://github.com/rust-lang/crates.io-index" 1589source = "registry+https://github.com/rust-lang/crates.io-index"
1585checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" 1590checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512"
1586dependencies = [ 1591dependencies = [
1587 "proc-macro2", 1592 "proc-macro2",
1588 "quote", 1593 "quote",
@@ -1606,6 +1611,7 @@ name = "syntax"
1606version = "0.0.0" 1611version = "0.0.0"
1607dependencies = [ 1612dependencies = [
1608 "arrayvec", 1613 "arrayvec",
1614 "cov-mark",
1609 "expect-test", 1615 "expect-test",
1610 "indexmap", 1616 "indexmap",
1611 "itertools", 1617 "itertools",
@@ -1640,7 +1646,6 @@ dependencies = [
1640 "dissimilar", 1646 "dissimilar",
1641 "profile", 1647 "profile",
1642 "rustc-hash", 1648 "rustc-hash",
1643 "serde_json",
1644 "stdx", 1649 "stdx",
1645 "text-size", 1650 "text-size",
1646] 1651]
@@ -1938,18 +1943,18 @@ checksum = "06069a848f95fceae3e5e03c0ddc8cb78452b56654ee0c8e68f938cf790fb9e3"
1938 1943
1939[[package]] 1944[[package]]
1940name = "xflags" 1945name = "xflags"
1941version = "0.1.3" 1946version = "0.2.1"
1942source = "registry+https://github.com/rust-lang/crates.io-index" 1947source = "registry+https://github.com/rust-lang/crates.io-index"
1943checksum = "ddb4b07c0db813f8e2b5e1b2189ef56fcddb27a6f9ef71314dbf8cc50096a5db" 1948checksum = "59ad6ce6a0b7224130015b4ebac796478ac04e0079f5d222a690efea06a9208a"
1944dependencies = [ 1949dependencies = [
1945 "xflags-macros", 1950 "xflags-macros",
1946] 1951]
1947 1952
1948[[package]] 1953[[package]]
1949name = "xflags-macros" 1954name = "xflags-macros"
1950version = "0.1.3" 1955version = "0.2.1"
1951source = "registry+https://github.com/rust-lang/crates.io-index" 1956source = "registry+https://github.com/rust-lang/crates.io-index"
1952checksum = "f8e168a99d6ce9d5dd0d0913f1bded279377843952dd8ff83f81b862a1dad0e1" 1957checksum = "c8037d3ca14996158b03c0fa905d0834906ef0fc7044df72c1f5ff690e5e62c9"
1953dependencies = [ 1958dependencies = [
1954 "proc-macro2", 1959 "proc-macro2",
1955] 1960]
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
deleted file mode 100644
index fc1a74641..000000000
--- a/crates/hir/src/code_model.rs
+++ /dev/null
@@ -1,2095 +0,0 @@
1//! FIXME: write short doc here
2use std::{iter, sync::Arc};
3
4use arrayvec::ArrayVec;
5use base_db::{CrateDisplayName, CrateId, Edition, FileId};
6use either::Either;
7use hir_def::{
8 adt::{ReprKind, StructKind, VariantData},
9 expr::{BindingAnnotation, LabelId, Pat, PatId},
10 import_map,
11 item_tree::ItemTreeNode,
12 lang_item::LangItemTarget,
13 path::ModPath,
14 per_ns::PerNs,
15 resolver::{HasResolver, Resolver},
16 src::HasSource as _,
17 type_ref::TypeRef,
18 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId,
19 DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
20 LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
21 TypeParamId, UnionId,
22};
23use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility};
24use hir_expand::{
25 diagnostics::DiagnosticSink,
26 name::{name, AsName},
27 MacroDefId, MacroDefKind,
28};
29use hir_ty::{
30 autoderef,
31 display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter},
32 method_resolution,
33 traits::{FnTrait, Solution, SolutionVariables},
34 AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate,
35 InEnvironment, Mutability, Obligation, ProjectionPredicate, ProjectionTy, Scalar, Substs,
36 TraitEnvironment, Ty, TyDefId, TyVariableKind,
37};
38use rustc_hash::FxHashSet;
39use stdx::{format_to, impl_from};
40use syntax::{
41 ast::{self, AttrsOwner, NameOwner},
42 AstNode, SmolStr,
43};
44use tt::{Ident, Leaf, Literal, TokenTree};
45
46use crate::{
47 db::{DefDatabase, HirDatabase},
48 has_source::HasSource,
49 HirDisplay, InFile, Name,
50};
51
52/// hir::Crate describes a single crate. It's the main interface with which
53/// a crate's dependencies interact. Mostly, it should be just a proxy for the
54/// root module.
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
56pub struct Crate {
57 pub(crate) id: CrateId,
58}
59
60#[derive(Debug)]
61pub struct CrateDependency {
62 pub krate: Crate,
63 pub name: Name,
64}
65
66impl Crate {
67 pub fn dependencies(self, db: &dyn HirDatabase) -> Vec<CrateDependency> {
68 db.crate_graph()[self.id]
69 .dependencies
70 .iter()
71 .map(|dep| {
72 let krate = Crate { id: dep.crate_id };
73 let name = dep.as_name();
74 CrateDependency { krate, name }
75 })
76 .collect()
77 }
78
79 // FIXME: add `transitive_reverse_dependencies`.
80 pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> {
81 let crate_graph = db.crate_graph();
82 crate_graph
83 .iter()
84 .filter(|&krate| {
85 crate_graph[krate].dependencies.iter().any(|it| it.crate_id == self.id)
86 })
87 .map(|id| Crate { id })
88 .collect()
89 }
90
91 pub fn root_module(self, db: &dyn HirDatabase) -> Module {
92 let def_map = db.crate_def_map(self.id);
93 Module { id: def_map.module_id(def_map.root()) }
94 }
95
96 pub fn root_file(self, db: &dyn HirDatabase) -> FileId {
97 db.crate_graph()[self.id].root_file_id
98 }
99
100 pub fn edition(self, db: &dyn HirDatabase) -> Edition {
101 db.crate_graph()[self.id].edition
102 }
103
104 pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateDisplayName> {
105 db.crate_graph()[self.id].display_name.clone()
106 }
107
108 pub fn query_external_importables(
109 self,
110 db: &dyn DefDatabase,
111 query: import_map::Query,
112 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
113 import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item {
114 ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()),
115 ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()),
116 })
117 }
118
119 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
120 db.crate_graph().iter().map(|id| Crate { id }).collect()
121 }
122
123 /// Try to get the root URL of the documentation of a crate.
124 pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> {
125 // Look for #![doc(html_root_url = "...")]
126 let attrs = db.attrs(AttrDefId::ModuleId(self.root_module(db).into()));
127 let doc_attr_q = attrs.by_key("doc");
128
129 if !doc_attr_q.exists() {
130 return None;
131 }
132
133 let doc_url = doc_attr_q.tt_values().map(|tt| {
134 let name = tt.token_trees.iter()
135 .skip_while(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident, ..})) if ident == "html_root_url"))
136 .skip(2)
137 .next();
138
139 match name {
140 Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text),
141 _ => None
142 }
143 }).flat_map(|t| t).next();
144
145 doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/")
146 }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
150pub struct Module {
151 pub(crate) id: ModuleId,
152}
153
154/// The defs which can be visible in the module.
155#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
156pub enum ModuleDef {
157 Module(Module),
158 Function(Function),
159 Adt(Adt),
160 // Can't be directly declared, but can be imported.
161 Variant(Variant),
162 Const(Const),
163 Static(Static),
164 Trait(Trait),
165 TypeAlias(TypeAlias),
166 BuiltinType(BuiltinType),
167}
168impl_from!(
169 Module,
170 Function,
171 Adt(Struct, Enum, Union),
172 Variant,
173 Const,
174 Static,
175 Trait,
176 TypeAlias,
177 BuiltinType
178 for ModuleDef
179);
180
181impl From<VariantDef> for ModuleDef {
182 fn from(var: VariantDef) -> Self {
183 match var {
184 VariantDef::Struct(t) => Adt::from(t).into(),
185 VariantDef::Union(t) => Adt::from(t).into(),
186 VariantDef::Variant(t) => t.into(),
187 }
188 }
189}
190
191impl ModuleDef {
192 pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
193 match self {
194 ModuleDef::Module(it) => it.parent(db),
195 ModuleDef::Function(it) => Some(it.module(db)),
196 ModuleDef::Adt(it) => Some(it.module(db)),
197 ModuleDef::Variant(it) => Some(it.module(db)),
198 ModuleDef::Const(it) => Some(it.module(db)),
199 ModuleDef::Static(it) => Some(it.module(db)),
200 ModuleDef::Trait(it) => Some(it.module(db)),
201 ModuleDef::TypeAlias(it) => Some(it.module(db)),
202 ModuleDef::BuiltinType(_) => None,
203 }
204 }
205
206 pub fn canonical_path(&self, db: &dyn HirDatabase) -> Option<String> {
207 let mut segments = Vec::new();
208 segments.push(self.name(db)?.to_string());
209 for m in self.module(db)?.path_to_root(db) {
210 segments.extend(m.name(db).map(|it| it.to_string()))
211 }
212 segments.reverse();
213 Some(segments.join("::"))
214 }
215
216 pub fn definition_visibility(&self, db: &dyn HirDatabase) -> Option<Visibility> {
217 let module = match self {
218 ModuleDef::Module(it) => it.parent(db)?,
219 ModuleDef::Function(it) => return Some(it.visibility(db)),
220 ModuleDef::Adt(it) => it.module(db),
221 ModuleDef::Variant(it) => {
222 let parent = it.parent_enum(db);
223 let module = it.module(db);
224 return module.visibility_of(db, &ModuleDef::Adt(Adt::Enum(parent)));
225 }
226 ModuleDef::Const(it) => return Some(it.visibility(db)),
227 ModuleDef::Static(it) => it.module(db),
228 ModuleDef::Trait(it) => it.module(db),
229 ModuleDef::TypeAlias(it) => return Some(it.visibility(db)),
230 ModuleDef::BuiltinType(_) => return None,
231 };
232
233 module.visibility_of(db, self)
234 }
235
236 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
237 match self {
238 ModuleDef::Adt(it) => Some(it.name(db)),
239 ModuleDef::Trait(it) => Some(it.name(db)),
240 ModuleDef::Function(it) => Some(it.name(db)),
241 ModuleDef::Variant(it) => Some(it.name(db)),
242 ModuleDef::TypeAlias(it) => Some(it.name(db)),
243 ModuleDef::Module(it) => it.name(db),
244 ModuleDef::Const(it) => it.name(db),
245 ModuleDef::Static(it) => it.name(db),
246
247 ModuleDef::BuiltinType(it) => Some(it.name()),
248 }
249 }
250
251 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
252 let id = match self {
253 ModuleDef::Adt(it) => match it {
254 Adt::Struct(it) => it.id.into(),
255 Adt::Enum(it) => it.id.into(),
256 Adt::Union(it) => it.id.into(),
257 },
258 ModuleDef::Trait(it) => it.id.into(),
259 ModuleDef::Function(it) => it.id.into(),
260 ModuleDef::TypeAlias(it) => it.id.into(),
261 ModuleDef::Module(it) => it.id.into(),
262 ModuleDef::Const(it) => it.id.into(),
263 ModuleDef::Static(it) => it.id.into(),
264 _ => return,
265 };
266
267 let module = match self.module(db) {
268 Some(it) => it,
269 None => return,
270 };
271
272 hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink)
273 }
274}
275
276impl Module {
277 /// Name of this module.
278 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
279 let def_map = self.id.def_map(db.upcast());
280 let parent = def_map[self.id.local_id].parent?;
281 def_map[parent].children.iter().find_map(|(name, module_id)| {
282 if *module_id == self.id.local_id {
283 Some(name.clone())
284 } else {
285 None
286 }
287 })
288 }
289
290 /// Returns the crate this module is part of.
291 pub fn krate(self) -> Crate {
292 Crate { id: self.id.krate() }
293 }
294
295 /// Topmost parent of this module. Every module has a `crate_root`, but some
296 /// might be missing `krate`. This can happen if a module's file is not included
297 /// in the module tree of any target in `Cargo.toml`.
298 pub fn crate_root(self, db: &dyn HirDatabase) -> Module {
299 let def_map = db.crate_def_map(self.id.krate());
300 Module { id: def_map.module_id(def_map.root()) }
301 }
302
303 /// Iterates over all child modules.
304 pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> {
305 let def_map = self.id.def_map(db.upcast());
306 let children = def_map[self.id.local_id]
307 .children
308 .iter()
309 .map(|(_, module_id)| Module { id: def_map.module_id(*module_id) })
310 .collect::<Vec<_>>();
311 children.into_iter()
312 }
313
314 /// Finds a parent module.
315 pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> {
316 // FIXME: handle block expressions as modules (their parent is in a different DefMap)
317 let def_map = self.id.def_map(db.upcast());
318 let parent_id = def_map[self.id.local_id].parent?;
319 Some(Module { id: def_map.module_id(parent_id) })
320 }
321
322 pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> {
323 let mut res = vec![self];
324 let mut curr = self;
325 while let Some(next) = curr.parent(db) {
326 res.push(next);
327 curr = next
328 }
329 res
330 }
331
332 /// Returns a `ModuleScope`: a set of items, visible in this module.
333 pub fn scope(
334 self,
335 db: &dyn HirDatabase,
336 visible_from: Option<Module>,
337 ) -> Vec<(Name, ScopeDef)> {
338 self.id.def_map(db.upcast())[self.id.local_id]
339 .scope
340 .entries()
341 .filter_map(|(name, def)| {
342 if let Some(m) = visible_from {
343 let filtered =
344 def.filter_visibility(|vis| vis.is_visible_from(db.upcast(), m.id));
345 if filtered.is_none() && !def.is_none() {
346 None
347 } else {
348 Some((name, filtered))
349 }
350 } else {
351 Some((name, def))
352 }
353 })
354 .flat_map(|(name, def)| {
355 ScopeDef::all_items(def).into_iter().map(move |item| (name.clone(), item))
356 })
357 .collect()
358 }
359
360 pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> {
361 self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into())
362 }
363
364 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
365 let _p = profile::span("Module::diagnostics").detail(|| {
366 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
367 });
368 let crate_def_map = self.id.def_map(db.upcast());
369 crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink);
370 for decl in self.declarations(db) {
371 match decl {
372 crate::ModuleDef::Function(f) => f.diagnostics(db, sink),
373 crate::ModuleDef::Module(m) => {
374 // Only add diagnostics from inline modules
375 if crate_def_map[m.id.local_id].origin.is_inline() {
376 m.diagnostics(db, sink)
377 }
378 }
379 _ => {
380 decl.diagnostics(db, sink);
381 }
382 }
383 }
384
385 for impl_def in self.impl_defs(db) {
386 for item in impl_def.items(db) {
387 if let AssocItem::Function(f) = item {
388 f.diagnostics(db, sink);
389 }
390 }
391 }
392 }
393
394 pub fn declarations(self, db: &dyn HirDatabase) -> Vec<ModuleDef> {
395 let def_map = self.id.def_map(db.upcast());
396 def_map[self.id.local_id].scope.declarations().map(ModuleDef::from).collect()
397 }
398
399 pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> {
400 let def_map = self.id.def_map(db.upcast());
401 def_map[self.id.local_id].scope.impls().map(Impl::from).collect()
402 }
403
404 /// Finds a path that can be used to refer to the given item from within
405 /// this module, if possible.
406 pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> {
407 hir_def::find_path::find_path(db, item.into(), self.into())
408 }
409
410 /// Finds a path that can be used to refer to the given item from within
411 /// this module, if possible. This is used for returning import paths for use-statements.
412 pub fn find_use_path_prefixed(
413 self,
414 db: &dyn DefDatabase,
415 item: impl Into<ItemInNs>,
416 prefix_kind: PrefixKind,
417 ) -> Option<ModPath> {
418 hir_def::find_path::find_path_prefixed(db, item.into(), self.into(), prefix_kind)
419 }
420}
421
422#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
423pub struct Field {
424 pub(crate) parent: VariantDef,
425 pub(crate) id: LocalFieldId,
426}
427
428#[derive(Debug, PartialEq, Eq)]
429pub enum FieldSource {
430 Named(ast::RecordField),
431 Pos(ast::TupleField),
432}
433
434impl Field {
435 pub fn name(&self, db: &dyn HirDatabase) -> Name {
436 self.parent.variant_data(db).fields()[self.id].name.clone()
437 }
438
439 /// Returns the type as in the signature of the struct (i.e., with
440 /// placeholder types for type parameters). This is good for showing
441 /// signature help, but not so good to actually get the type of the field
442 /// when you actually have a variable of the struct.
443 pub fn signature_ty(&self, db: &dyn HirDatabase) -> Type {
444 let var_id = self.parent.into();
445 let generic_def_id: GenericDefId = match self.parent {
446 VariantDef::Struct(it) => it.id.into(),
447 VariantDef::Union(it) => it.id.into(),
448 VariantDef::Variant(it) => it.parent.id.into(),
449 };
450 let substs = Substs::type_params(db, generic_def_id);
451 let ty = db.field_types(var_id)[self.id].clone().subst(&substs);
452 Type::new(db, self.parent.module(db).id.krate(), var_id, ty)
453 }
454
455 pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef {
456 self.parent
457 }
458}
459
460impl HasVisibility for Field {
461 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
462 let variant_data = self.parent.variant_data(db);
463 let visibility = &variant_data.fields()[self.id].visibility;
464 let parent_id: hir_def::VariantId = self.parent.into();
465 visibility.resolve(db.upcast(), &parent_id.resolver(db.upcast()))
466 }
467}
468
469#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
470pub struct Struct {
471 pub(crate) id: StructId,
472}
473
474impl Struct {
475 pub fn module(self, db: &dyn HirDatabase) -> Module {
476 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) }
477 }
478
479 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
480 Some(self.module(db).krate())
481 }
482
483 pub fn name(self, db: &dyn HirDatabase) -> Name {
484 db.struct_data(self.id).name.clone()
485 }
486
487 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
488 db.struct_data(self.id)
489 .variant_data
490 .fields()
491 .iter()
492 .map(|(id, _)| Field { parent: self.into(), id })
493 .collect()
494 }
495
496 pub fn ty(self, db: &dyn HirDatabase) -> Type {
497 Type::from_def(
498 db,
499 self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
500 self.id,
501 )
502 }
503
504 pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> {
505 db.struct_data(self.id).repr.clone()
506 }
507
508 pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
509 self.variant_data(db).kind()
510 }
511
512 fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
513 db.struct_data(self.id).variant_data.clone()
514 }
515}
516
517#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
518pub struct Union {
519 pub(crate) id: UnionId,
520}
521
522impl Union {
523 pub fn name(self, db: &dyn HirDatabase) -> Name {
524 db.union_data(self.id).name.clone()
525 }
526
527 pub fn module(self, db: &dyn HirDatabase) -> Module {
528 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) }
529 }
530
531 pub fn ty(self, db: &dyn HirDatabase) -> Type {
532 Type::from_def(
533 db,
534 self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
535 self.id,
536 )
537 }
538
539 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
540 db.union_data(self.id)
541 .variant_data
542 .fields()
543 .iter()
544 .map(|(id, _)| Field { parent: self.into(), id })
545 .collect()
546 }
547
548 fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
549 db.union_data(self.id).variant_data.clone()
550 }
551}
552
553#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
554pub struct Enum {
555 pub(crate) id: EnumId,
556}
557
558impl Enum {
559 pub fn module(self, db: &dyn HirDatabase) -> Module {
560 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) }
561 }
562
563 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
564 Some(self.module(db).krate())
565 }
566
567 pub fn name(self, db: &dyn HirDatabase) -> Name {
568 db.enum_data(self.id).name.clone()
569 }
570
571 pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> {
572 db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect()
573 }
574
575 pub fn ty(self, db: &dyn HirDatabase) -> Type {
576 Type::from_def(
577 db,
578 self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
579 self.id,
580 )
581 }
582}
583
584#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
585pub struct Variant {
586 pub(crate) parent: Enum,
587 pub(crate) id: LocalEnumVariantId,
588}
589
590impl Variant {
591 pub fn module(self, db: &dyn HirDatabase) -> Module {
592 self.parent.module(db)
593 }
594 pub fn parent_enum(self, _db: &dyn HirDatabase) -> Enum {
595 self.parent
596 }
597
598 pub fn name(self, db: &dyn HirDatabase) -> Name {
599 db.enum_data(self.parent.id).variants[self.id].name.clone()
600 }
601
602 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
603 self.variant_data(db)
604 .fields()
605 .iter()
606 .map(|(id, _)| Field { parent: self.into(), id })
607 .collect()
608 }
609
610 pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
611 self.variant_data(db).kind()
612 }
613
614 pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
615 db.enum_data(self.parent.id).variants[self.id].variant_data.clone()
616 }
617}
618
619/// A Data Type
620#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
621pub enum Adt {
622 Struct(Struct),
623 Union(Union),
624 Enum(Enum),
625}
626impl_from!(Struct, Union, Enum for Adt);
627
628impl Adt {
629 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
630 let subst = db.generic_defaults(self.into());
631 subst.iter().any(|ty| &ty.value == &Ty::Unknown)
632 }
633
634 /// Turns this ADT into a type. Any type parameters of the ADT will be
635 /// turned into unknown types, which is good for e.g. finding the most
636 /// general set of completions, but will not look very nice when printed.
637 pub fn ty(self, db: &dyn HirDatabase) -> Type {
638 let id = AdtId::from(self);
639 Type::from_def(db, id.module(db.upcast()).krate(), id)
640 }
641
642 pub fn module(self, db: &dyn HirDatabase) -> Module {
643 match self {
644 Adt::Struct(s) => s.module(db),
645 Adt::Union(s) => s.module(db),
646 Adt::Enum(e) => e.module(db),
647 }
648 }
649
650 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
651 Some(self.module(db).krate())
652 }
653
654 pub fn name(self, db: &dyn HirDatabase) -> Name {
655 match self {
656 Adt::Struct(s) => s.name(db),
657 Adt::Union(u) => u.name(db),
658 Adt::Enum(e) => e.name(db),
659 }
660 }
661}
662
663#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
664pub enum VariantDef {
665 Struct(Struct),
666 Union(Union),
667 Variant(Variant),
668}
669impl_from!(Struct, Union, Variant for VariantDef);
670
671impl VariantDef {
672 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
673 match self {
674 VariantDef::Struct(it) => it.fields(db),
675 VariantDef::Union(it) => it.fields(db),
676 VariantDef::Variant(it) => it.fields(db),
677 }
678 }
679
680 pub fn module(self, db: &dyn HirDatabase) -> Module {
681 match self {
682 VariantDef::Struct(it) => it.module(db),
683 VariantDef::Union(it) => it.module(db),
684 VariantDef::Variant(it) => it.module(db),
685 }
686 }
687
688 pub fn name(&self, db: &dyn HirDatabase) -> Name {
689 match self {
690 VariantDef::Struct(s) => s.name(db),
691 VariantDef::Union(u) => u.name(db),
692 VariantDef::Variant(e) => e.name(db),
693 }
694 }
695
696 pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
697 match self {
698 VariantDef::Struct(it) => it.variant_data(db),
699 VariantDef::Union(it) => it.variant_data(db),
700 VariantDef::Variant(it) => it.variant_data(db),
701 }
702 }
703}
704
705/// The defs which have a body.
706#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
707pub enum DefWithBody {
708 Function(Function),
709 Static(Static),
710 Const(Const),
711}
712impl_from!(Function, Const, Static for DefWithBody);
713
714impl DefWithBody {
715 pub fn module(self, db: &dyn HirDatabase) -> Module {
716 match self {
717 DefWithBody::Const(c) => c.module(db),
718 DefWithBody::Function(f) => f.module(db),
719 DefWithBody::Static(s) => s.module(db),
720 }
721 }
722
723 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
724 match self {
725 DefWithBody::Function(f) => Some(f.name(db)),
726 DefWithBody::Static(s) => s.name(db),
727 DefWithBody::Const(c) => c.name(db),
728 }
729 }
730}
731
732#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
733pub struct Function {
734 pub(crate) id: FunctionId,
735}
736
737impl Function {
738 pub fn module(self, db: &dyn HirDatabase) -> Module {
739 self.id.lookup(db.upcast()).module(db.upcast()).into()
740 }
741
742 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
743 Some(self.module(db).krate())
744 }
745
746 pub fn name(self, db: &dyn HirDatabase) -> Name {
747 db.function_data(self.id).name.clone()
748 }
749
750 /// Get this function's return type
751 pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
752 let resolver = self.id.resolver(db.upcast());
753 let ret_type = &db.function_data(self.id).ret_type;
754 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
755 let environment = TraitEnvironment::lower(db, &resolver);
756 Type {
757 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
758 ty: InEnvironment { value: Ty::from_hir_ext(&ctx, ret_type).0, environment },
759 }
760 }
761
762 pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
763 if !db.function_data(self.id).has_self_param {
764 return None;
765 }
766 Some(SelfParam { func: self.id })
767 }
768
769 pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> {
770 let resolver = self.id.resolver(db.upcast());
771 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
772 let environment = TraitEnvironment::lower(db, &resolver);
773 db.function_data(self.id)
774 .params
775 .iter()
776 .map(|type_ref| {
777 let ty = Type {
778 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
779 ty: InEnvironment {
780 value: Ty::from_hir_ext(&ctx, type_ref).0,
781 environment: environment.clone(),
782 },
783 };
784 Param { ty }
785 })
786 .collect()
787 }
788 pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
789 if self.self_param(db).is_none() {
790 return None;
791 }
792 let mut res = self.assoc_fn_params(db);
793 res.remove(0);
794 Some(res)
795 }
796
797 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
798 db.function_data(self.id).is_unsafe
799 }
800
801 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
802 let krate = self.module(db).id.krate();
803 hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink);
804 hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink);
805 hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
806 }
807
808 /// Whether this function declaration has a definition.
809 ///
810 /// This is false in the case of required (not provided) trait methods.
811 pub fn has_body(self, db: &dyn HirDatabase) -> bool {
812 db.function_data(self.id).has_body
813 }
814
815 /// A textual representation of the HIR of this function for debugging purposes.
816 pub fn debug_hir(self, db: &dyn HirDatabase) -> String {
817 let body = db.body(self.id.into());
818
819 let mut result = String::new();
820 format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db));
821 for (id, expr) in body.exprs.iter() {
822 format_to!(result, "{:?}: {:?}\n", id, expr);
823 }
824
825 result
826 }
827}
828
829// Note: logically, this belongs to `hir_ty`, but we are not using it there yet.
830pub enum Access {
831 Shared,
832 Exclusive,
833 Owned,
834}
835
836impl From<Mutability> for Access {
837 fn from(mutability: Mutability) -> Access {
838 match mutability {
839 Mutability::Not => Access::Shared,
840 Mutability::Mut => Access::Exclusive,
841 }
842 }
843}
844
845#[derive(Debug)]
846pub struct Param {
847 ty: Type,
848}
849
850impl Param {
851 pub fn ty(&self) -> &Type {
852 &self.ty
853 }
854}
855
856#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
857pub struct SelfParam {
858 func: FunctionId,
859}
860
861impl SelfParam {
862 pub fn access(self, db: &dyn HirDatabase) -> Access {
863 let func_data = db.function_data(self.func);
864 func_data
865 .params
866 .first()
867 .map(|param| match *param {
868 TypeRef::Reference(.., mutability) => match mutability {
869 hir_def::type_ref::Mutability::Shared => Access::Shared,
870 hir_def::type_ref::Mutability::Mut => Access::Exclusive,
871 },
872 _ => Access::Owned,
873 })
874 .unwrap_or(Access::Owned)
875 }
876}
877
878impl HasVisibility for Function {
879 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
880 let function_data = db.function_data(self.id);
881 let visibility = &function_data.visibility;
882 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
883 }
884}
885
886#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
887pub struct Const {
888 pub(crate) id: ConstId,
889}
890
891impl Const {
892 pub fn module(self, db: &dyn HirDatabase) -> Module {
893 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
894 }
895
896 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
897 Some(self.module(db).krate())
898 }
899
900 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
901 db.const_data(self.id).name.clone()
902 }
903}
904
905impl HasVisibility for Const {
906 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
907 let function_data = db.const_data(self.id);
908 let visibility = &function_data.visibility;
909 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
910 }
911}
912
913#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
914pub struct Static {
915 pub(crate) id: StaticId,
916}
917
918impl Static {
919 pub fn module(self, db: &dyn HirDatabase) -> Module {
920 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
921 }
922
923 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
924 Some(self.module(db).krate())
925 }
926
927 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
928 db.static_data(self.id).name.clone()
929 }
930
931 pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
932 db.static_data(self.id).mutable
933 }
934}
935
936#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
937pub struct Trait {
938 pub(crate) id: TraitId,
939}
940
941impl Trait {
942 pub fn module(self, db: &dyn HirDatabase) -> Module {
943 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) }
944 }
945
946 pub fn name(self, db: &dyn HirDatabase) -> Name {
947 db.trait_data(self.id).name.clone()
948 }
949
950 pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
951 db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect()
952 }
953
954 pub fn is_auto(self, db: &dyn HirDatabase) -> bool {
955 db.trait_data(self.id).auto
956 }
957}
958
959#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
960pub struct TypeAlias {
961 pub(crate) id: TypeAliasId,
962}
963
964impl TypeAlias {
965 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
966 let subst = db.generic_defaults(self.id.into());
967 subst.iter().any(|ty| &ty.value == &Ty::Unknown)
968 }
969
970 pub fn module(self, db: &dyn HirDatabase) -> Module {
971 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
972 }
973
974 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
975 Some(self.module(db).krate())
976 }
977
978 pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> {
979 db.type_alias_data(self.id).type_ref.clone()
980 }
981
982 pub fn ty(self, db: &dyn HirDatabase) -> Type {
983 Type::from_def(db, self.id.lookup(db.upcast()).module(db.upcast()).krate(), self.id)
984 }
985
986 pub fn name(self, db: &dyn HirDatabase) -> Name {
987 db.type_alias_data(self.id).name.clone()
988 }
989}
990
991impl HasVisibility for TypeAlias {
992 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
993 let function_data = db.type_alias_data(self.id);
994 let visibility = &function_data.visibility;
995 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
996 }
997}
998
999#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1000pub struct BuiltinType {
1001 pub(crate) inner: hir_def::builtin_type::BuiltinType,
1002}
1003
1004impl BuiltinType {
1005 pub fn ty(self, db: &dyn HirDatabase, module: Module) -> Type {
1006 let resolver = module.id.resolver(db.upcast());
1007 Type::new_with_resolver(db, &resolver, Ty::builtin(self.inner))
1008 .expect("crate not present in resolver")
1009 }
1010
1011 pub fn name(self) -> Name {
1012 self.inner.as_name()
1013 }
1014}
1015
1016#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1017pub struct MacroDef {
1018 pub(crate) id: MacroDefId,
1019}
1020
1021impl MacroDef {
1022 /// FIXME: right now, this just returns the root module of the crate that
1023 /// defines this macro. The reasons for this is that macros are expanded
1024 /// early, in `hir_expand`, where modules simply do not exist yet.
1025 pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
1026 let krate = self.id.krate;
1027 let def_map = db.crate_def_map(krate);
1028 let module_id = def_map.root();
1029 Some(Module { id: def_map.module_id(module_id) })
1030 }
1031
1032 /// XXX: this parses the file
1033 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1034 self.source(db)?.value.name().map(|it| it.as_name())
1035 }
1036
1037 /// Indicate it is a proc-macro
1038 pub fn is_proc_macro(&self) -> bool {
1039 matches!(self.id.kind, MacroDefKind::ProcMacro(_))
1040 }
1041
1042 /// Indicate it is a derive macro
1043 pub fn is_derive_macro(&self) -> bool {
1044 matches!(self.id.kind, MacroDefKind::ProcMacro(_) | MacroDefKind::BuiltInDerive(_))
1045 }
1046}
1047
1048/// Invariant: `inner.as_assoc_item(db).is_some()`
1049/// We do not actively enforce this invariant.
1050#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1051pub enum AssocItem {
1052 Function(Function),
1053 Const(Const),
1054 TypeAlias(TypeAlias),
1055}
1056pub enum AssocItemContainer {
1057 Trait(Trait),
1058 Impl(Impl),
1059}
1060pub trait AsAssocItem {
1061 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem>;
1062}
1063
1064impl AsAssocItem for Function {
1065 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1066 as_assoc_item(db, AssocItem::Function, self.id)
1067 }
1068}
1069impl AsAssocItem for Const {
1070 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1071 as_assoc_item(db, AssocItem::Const, self.id)
1072 }
1073}
1074impl AsAssocItem for TypeAlias {
1075 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1076 as_assoc_item(db, AssocItem::TypeAlias, self.id)
1077 }
1078}
1079impl AsAssocItem for ModuleDef {
1080 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1081 match self {
1082 ModuleDef::Function(it) => it.as_assoc_item(db),
1083 ModuleDef::Const(it) => it.as_assoc_item(db),
1084 ModuleDef::TypeAlias(it) => it.as_assoc_item(db),
1085 _ => None,
1086 }
1087 }
1088}
1089fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem>
1090where
1091 ID: Lookup<Data = AssocItemLoc<AST>>,
1092 DEF: From<ID>,
1093 CTOR: FnOnce(DEF) -> AssocItem,
1094 AST: ItemTreeNode,
1095{
1096 match id.lookup(db.upcast()).container {
1097 AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
1098 AssocContainerId::ContainerId(_) => None,
1099 }
1100}
1101
1102impl AssocItem {
1103 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1104 match self {
1105 AssocItem::Function(it) => Some(it.name(db)),
1106 AssocItem::Const(it) => it.name(db),
1107 AssocItem::TypeAlias(it) => Some(it.name(db)),
1108 }
1109 }
1110 pub fn module(self, db: &dyn HirDatabase) -> Module {
1111 match self {
1112 AssocItem::Function(f) => f.module(db),
1113 AssocItem::Const(c) => c.module(db),
1114 AssocItem::TypeAlias(t) => t.module(db),
1115 }
1116 }
1117 pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer {
1118 let container = match self {
1119 AssocItem::Function(it) => it.id.lookup(db.upcast()).container,
1120 AssocItem::Const(it) => it.id.lookup(db.upcast()).container,
1121 AssocItem::TypeAlias(it) => it.id.lookup(db.upcast()).container,
1122 };
1123 match container {
1124 AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()),
1125 AssocContainerId::ImplId(id) => AssocItemContainer::Impl(id.into()),
1126 AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"),
1127 }
1128 }
1129
1130 pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> {
1131 match self.container(db) {
1132 AssocItemContainer::Trait(t) => Some(t),
1133 _ => None,
1134 }
1135 }
1136}
1137
1138impl HasVisibility for AssocItem {
1139 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
1140 match self {
1141 AssocItem::Function(f) => f.visibility(db),
1142 AssocItem::Const(c) => c.visibility(db),
1143 AssocItem::TypeAlias(t) => t.visibility(db),
1144 }
1145 }
1146}
1147
1148#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
1149pub enum GenericDef {
1150 Function(Function),
1151 Adt(Adt),
1152 Trait(Trait),
1153 TypeAlias(TypeAlias),
1154 Impl(Impl),
1155 // enum variants cannot have generics themselves, but their parent enums
1156 // can, and this makes some code easier to write
1157 Variant(Variant),
1158 // consts can have type parameters from their parents (i.e. associated consts of traits)
1159 Const(Const),
1160}
1161impl_from!(
1162 Function,
1163 Adt(Struct, Enum, Union),
1164 Trait,
1165 TypeAlias,
1166 Impl,
1167 Variant,
1168 Const
1169 for GenericDef
1170);
1171
1172impl GenericDef {
1173 pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> {
1174 let generics = db.generic_params(self.into());
1175 let ty_params = generics
1176 .types
1177 .iter()
1178 .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } })
1179 .map(GenericParam::TypeParam);
1180 let lt_params = generics
1181 .lifetimes
1182 .iter()
1183 .map(|(local_id, _)| LifetimeParam {
1184 id: LifetimeParamId { parent: self.into(), local_id },
1185 })
1186 .map(GenericParam::LifetimeParam);
1187 let const_params = generics
1188 .consts
1189 .iter()
1190 .map(|(local_id, _)| ConstParam { id: ConstParamId { parent: self.into(), local_id } })
1191 .map(GenericParam::ConstParam);
1192 ty_params.chain(lt_params).chain(const_params).collect()
1193 }
1194
1195 pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> {
1196 let generics = db.generic_params(self.into());
1197 generics
1198 .types
1199 .iter()
1200 .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } })
1201 .collect()
1202 }
1203}
1204
1205#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1206pub struct Local {
1207 pub(crate) parent: DefWithBodyId,
1208 pub(crate) pat_id: PatId,
1209}
1210
1211impl Local {
1212 pub fn is_param(self, db: &dyn HirDatabase) -> bool {
1213 let src = self.source(db);
1214 match src.value {
1215 Either::Left(bind_pat) => {
1216 bind_pat.syntax().ancestors().any(|it| ast::Param::can_cast(it.kind()))
1217 }
1218 Either::Right(_self_param) => true,
1219 }
1220 }
1221
1222 // FIXME: why is this an option? It shouldn't be?
1223 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1224 let body = db.body(self.parent.into());
1225 match &body[self.pat_id] {
1226 Pat::Bind { name, .. } => Some(name.clone()),
1227 _ => None,
1228 }
1229 }
1230
1231 pub fn is_self(self, db: &dyn HirDatabase) -> bool {
1232 self.name(db) == Some(name![self])
1233 }
1234
1235 pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
1236 let body = db.body(self.parent.into());
1237 match &body[self.pat_id] {
1238 Pat::Bind { mode, .. } => match mode {
1239 BindingAnnotation::Mutable | BindingAnnotation::RefMut => true,
1240 _ => false,
1241 },
1242 _ => false,
1243 }
1244 }
1245
1246 pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
1247 self.parent.into()
1248 }
1249
1250 pub fn module(self, db: &dyn HirDatabase) -> Module {
1251 self.parent(db).module(db)
1252 }
1253
1254 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1255 let def = DefWithBodyId::from(self.parent);
1256 let infer = db.infer(def);
1257 let ty = infer[self.pat_id].clone();
1258 let krate = def.module(db.upcast()).krate();
1259 Type::new(db, krate, def, ty)
1260 }
1261
1262 pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> {
1263 let (_body, source_map) = db.body_with_source_map(self.parent.into());
1264 let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
1265 let root = src.file_syntax(db.upcast());
1266 src.map(|ast| {
1267 ast.map_left(|it| it.cast().unwrap().to_node(&root)).map_right(|it| it.to_node(&root))
1268 })
1269 }
1270}
1271
1272#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1273pub struct Label {
1274 pub(crate) parent: DefWithBodyId,
1275 pub(crate) label_id: LabelId,
1276}
1277
1278impl Label {
1279 pub fn module(self, db: &dyn HirDatabase) -> Module {
1280 self.parent(db).module(db)
1281 }
1282
1283 pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
1284 self.parent.into()
1285 }
1286
1287 pub fn name(self, db: &dyn HirDatabase) -> Name {
1288 let body = db.body(self.parent.into());
1289 body[self.label_id].name.clone()
1290 }
1291
1292 pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> {
1293 let (_body, source_map) = db.body_with_source_map(self.parent.into());
1294 let src = source_map.label_syntax(self.label_id);
1295 let root = src.file_syntax(db.upcast());
1296 src.map(|ast| ast.to_node(&root))
1297 }
1298}
1299
1300#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1301pub enum GenericParam {
1302 TypeParam(TypeParam),
1303 LifetimeParam(LifetimeParam),
1304 ConstParam(ConstParam),
1305}
1306impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam);
1307
1308impl GenericParam {
1309 pub fn module(self, db: &dyn HirDatabase) -> Module {
1310 match self {
1311 GenericParam::TypeParam(it) => it.module(db),
1312 GenericParam::LifetimeParam(it) => it.module(db),
1313 GenericParam::ConstParam(it) => it.module(db),
1314 }
1315 }
1316
1317 pub fn name(self, db: &dyn HirDatabase) -> Name {
1318 match self {
1319 GenericParam::TypeParam(it) => it.name(db),
1320 GenericParam::LifetimeParam(it) => it.name(db),
1321 GenericParam::ConstParam(it) => it.name(db),
1322 }
1323 }
1324}
1325
1326#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1327pub struct TypeParam {
1328 pub(crate) id: TypeParamId,
1329}
1330
1331impl TypeParam {
1332 pub fn name(self, db: &dyn HirDatabase) -> Name {
1333 let params = db.generic_params(self.id.parent);
1334 params.types[self.id.local_id].name.clone().unwrap_or_else(Name::missing)
1335 }
1336
1337 pub fn module(self, db: &dyn HirDatabase) -> Module {
1338 self.id.parent.module(db.upcast()).into()
1339 }
1340
1341 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1342 let resolver = self.id.parent.resolver(db.upcast());
1343 let environment = TraitEnvironment::lower(db, &resolver);
1344 let ty = Ty::Placeholder(self.id);
1345 Type {
1346 krate: self.id.parent.module(db.upcast()).krate(),
1347 ty: InEnvironment { value: ty, environment },
1348 }
1349 }
1350
1351 pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
1352 db.generic_predicates_for_param(self.id)
1353 .into_iter()
1354 .filter_map(|pred| match &pred.value {
1355 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1356 Some(Trait::from(trait_ref.trait_))
1357 }
1358 _ => None,
1359 })
1360 .collect()
1361 }
1362
1363 pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
1364 let params = db.generic_defaults(self.id.parent);
1365 let local_idx = hir_ty::param_idx(db, self.id)?;
1366 let resolver = self.id.parent.resolver(db.upcast());
1367 let environment = TraitEnvironment::lower(db, &resolver);
1368 let ty = params.get(local_idx)?.clone();
1369 let subst = Substs::type_params(db, self.id.parent);
1370 let ty = ty.subst(&subst.prefix(local_idx));
1371 Some(Type {
1372 krate: self.id.parent.module(db.upcast()).krate(),
1373 ty: InEnvironment { value: ty, environment },
1374 })
1375 }
1376}
1377
1378impl HirDisplay for TypeParam {
1379 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
1380 write!(f, "{}", self.name(f.db))?;
1381 let bounds = f.db.generic_predicates_for_param(self.id);
1382 let substs = Substs::type_params(f.db, self.id.parent);
1383 let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>();
1384 if !(predicates.is_empty() || f.omit_verbose_types()) {
1385 write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?;
1386 }
1387 Ok(())
1388 }
1389}
1390
1391#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1392pub struct LifetimeParam {
1393 pub(crate) id: LifetimeParamId,
1394}
1395
1396impl LifetimeParam {
1397 pub fn name(self, db: &dyn HirDatabase) -> Name {
1398 let params = db.generic_params(self.id.parent);
1399 params.lifetimes[self.id.local_id].name.clone()
1400 }
1401
1402 pub fn module(self, db: &dyn HirDatabase) -> Module {
1403 self.id.parent.module(db.upcast()).into()
1404 }
1405
1406 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1407 self.id.parent.into()
1408 }
1409}
1410
1411#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1412pub struct ConstParam {
1413 pub(crate) id: ConstParamId,
1414}
1415
1416impl ConstParam {
1417 pub fn name(self, db: &dyn HirDatabase) -> Name {
1418 let params = db.generic_params(self.id.parent);
1419 params.consts[self.id.local_id].name.clone()
1420 }
1421
1422 pub fn module(self, db: &dyn HirDatabase) -> Module {
1423 self.id.parent.module(db.upcast()).into()
1424 }
1425
1426 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1427 self.id.parent.into()
1428 }
1429
1430 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1431 let def = self.id.parent;
1432 let krate = def.module(db.upcast()).krate();
1433 Type::new(db, krate, def, db.const_param_ty(self.id))
1434 }
1435}
1436
1437#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1438pub struct Impl {
1439 pub(crate) id: ImplId,
1440}
1441
1442impl Impl {
1443 pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<Impl> {
1444 let inherent = db.inherent_impls_in_crate(krate.id);
1445 let trait_ = db.trait_impls_in_crate(krate.id);
1446
1447 inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect()
1448 }
1449 pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<Impl> {
1450 let impls = db.trait_impls_in_crate(krate.id);
1451 impls.for_trait(trait_.id).map(Self::from).collect()
1452 }
1453
1454 // FIXME: the return type is wrong. This should be a hir version of
1455 // `TraitRef` (ie, resolved `TypeRef`).
1456 pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> {
1457 db.impl_data(self.id).target_trait.clone()
1458 }
1459
1460 pub fn target_ty(self, db: &dyn HirDatabase) -> Type {
1461 let impl_data = db.impl_data(self.id);
1462 let resolver = self.id.resolver(db.upcast());
1463 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
1464 let environment = TraitEnvironment::lower(db, &resolver);
1465 let ty = Ty::from_hir(&ctx, &impl_data.target_type);
1466 Type {
1467 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
1468 ty: InEnvironment { value: ty, environment },
1469 }
1470 }
1471
1472 pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
1473 db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect()
1474 }
1475
1476 pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
1477 db.impl_data(self.id).is_negative
1478 }
1479
1480 pub fn module(self, db: &dyn HirDatabase) -> Module {
1481 self.id.lookup(db.upcast()).container.module(db.upcast()).into()
1482 }
1483
1484 pub fn krate(self, db: &dyn HirDatabase) -> Crate {
1485 Crate { id: self.module(db).id.krate() }
1486 }
1487
1488 pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> {
1489 let src = self.source(db)?;
1490 let item = src.file_id.is_builtin_derive(db.upcast())?;
1491 let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id);
1492
1493 // FIXME: handle `cfg_attr`
1494 let attr = item
1495 .value
1496 .attrs()
1497 .filter_map(|it| {
1498 let path = ModPath::from_src(it.path()?, &hygenic)?;
1499 if path.as_ident()?.to_string() == "derive" {
1500 Some(it)
1501 } else {
1502 None
1503 }
1504 })
1505 .last()?;
1506
1507 Some(item.with_value(attr))
1508 }
1509}
1510
1511#[derive(Clone, PartialEq, Eq, Debug)]
1512pub struct Type {
1513 krate: CrateId,
1514 ty: InEnvironment<Ty>,
1515}
1516
1517impl Type {
1518 pub(crate) fn new_with_resolver(
1519 db: &dyn HirDatabase,
1520 resolver: &Resolver,
1521 ty: Ty,
1522 ) -> Option<Type> {
1523 let krate = resolver.krate()?;
1524 Some(Type::new_with_resolver_inner(db, krate, resolver, ty))
1525 }
1526 pub(crate) fn new_with_resolver_inner(
1527 db: &dyn HirDatabase,
1528 krate: CrateId,
1529 resolver: &Resolver,
1530 ty: Ty,
1531 ) -> Type {
1532 let environment = TraitEnvironment::lower(db, &resolver);
1533 Type { krate, ty: InEnvironment { value: ty, environment } }
1534 }
1535
1536 fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type {
1537 let resolver = lexical_env.resolver(db.upcast());
1538 let environment = TraitEnvironment::lower(db, &resolver);
1539 Type { krate, ty: InEnvironment { value: ty, environment } }
1540 }
1541
1542 fn from_def(
1543 db: &dyn HirDatabase,
1544 krate: CrateId,
1545 def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>,
1546 ) -> Type {
1547 let substs = Substs::build_for_def(db, def).fill_with_unknown().build();
1548 let ty = db.ty(def.into()).subst(&substs);
1549 Type::new(db, krate, def, ty)
1550 }
1551
1552 pub fn is_unit(&self) -> bool {
1553 matches!(self.ty.value, Ty::Tuple(0, ..))
1554 }
1555 pub fn is_bool(&self) -> bool {
1556 matches!(self.ty.value, Ty::Scalar(Scalar::Bool))
1557 }
1558
1559 pub fn is_mutable_reference(&self) -> bool {
1560 matches!(self.ty.value, Ty::Ref(Mutability::Mut, ..))
1561 }
1562
1563 pub fn remove_ref(&self) -> Option<Type> {
1564 if let Ty::Ref(.., substs) = &self.ty.value {
1565 Some(self.derived(substs[0].clone()))
1566 } else {
1567 None
1568 }
1569 }
1570
1571 pub fn is_unknown(&self) -> bool {
1572 matches!(self.ty.value, Ty::Unknown)
1573 }
1574
1575 /// Checks that particular type `ty` implements `std::future::Future`.
1576 /// This function is used in `.await` syntax completion.
1577 pub fn impls_future(&self, db: &dyn HirDatabase) -> bool {
1578 // No special case for the type of async block, since Chalk can figure it out.
1579
1580 let krate = self.krate;
1581
1582 let std_future_trait =
1583 db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait());
1584 let std_future_trait = match std_future_trait {
1585 Some(it) => it,
1586 None => return false,
1587 };
1588
1589 let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1590 method_resolution::implements_trait(
1591 &canonical_ty,
1592 db,
1593 self.ty.environment.clone(),
1594 krate,
1595 std_future_trait,
1596 )
1597 }
1598
1599 /// Checks that particular type `ty` implements `std::ops::FnOnce`.
1600 ///
1601 /// This function can be used to check if a particular type is callable, since FnOnce is a
1602 /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce.
1603 pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool {
1604 let krate = self.krate;
1605
1606 let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) {
1607 Some(it) => it,
1608 None => return false,
1609 };
1610
1611 let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1612 method_resolution::implements_trait_unique(
1613 &canonical_ty,
1614 db,
1615 self.ty.environment.clone(),
1616 krate,
1617 fnonce_trait,
1618 )
1619 }
1620
1621 pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool {
1622 let trait_ref = hir_ty::TraitRef {
1623 trait_: trait_.id,
1624 substs: Substs::build_for_def(db, trait_.id)
1625 .push(self.ty.value.clone())
1626 .fill(args.iter().map(|t| t.ty.value.clone()))
1627 .build(),
1628 };
1629
1630 let goal = Canonical {
1631 value: hir_ty::InEnvironment::new(
1632 self.ty.environment.clone(),
1633 hir_ty::Obligation::Trait(trait_ref),
1634 ),
1635 kinds: Arc::new([]),
1636 };
1637
1638 db.trait_solve(self.krate, goal).is_some()
1639 }
1640
1641 pub fn normalize_trait_assoc_type(
1642 &self,
1643 db: &dyn HirDatabase,
1644 trait_: Trait,
1645 args: &[Type],
1646 alias: TypeAlias,
1647 ) -> Option<Type> {
1648 let subst = Substs::build_for_def(db, trait_.id)
1649 .push(self.ty.value.clone())
1650 .fill(args.iter().map(|t| t.ty.value.clone()))
1651 .build();
1652 let predicate = ProjectionPredicate {
1653 projection_ty: ProjectionTy { associated_ty: alias.id, parameters: subst },
1654 ty: Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)),
1655 };
1656 let goal = Canonical {
1657 value: InEnvironment::new(
1658 self.ty.environment.clone(),
1659 Obligation::Projection(predicate),
1660 ),
1661 kinds: Arc::new([TyVariableKind::General]),
1662 };
1663
1664 match db.trait_solve(self.krate, goal)? {
1665 Solution::Unique(SolutionVariables(subst)) => subst.value.first().cloned(),
1666 Solution::Ambig(_) => None,
1667 }
1668 .map(|ty| Type {
1669 krate: self.krate,
1670 ty: InEnvironment { value: ty, environment: Arc::clone(&self.ty.environment) },
1671 })
1672 }
1673
1674 pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
1675 let lang_item = db.lang_item(self.krate, SmolStr::new("copy"));
1676 let copy_trait = match lang_item {
1677 Some(LangItemTarget::TraitId(it)) => it,
1678 _ => return false,
1679 };
1680 self.impls_trait(db, copy_trait.into(), &[])
1681 }
1682
1683 pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
1684 let def = match self.ty.value {
1685 Ty::FnDef(def, _) => Some(def),
1686 _ => None,
1687 };
1688
1689 let sig = self.ty.value.callable_sig(db)?;
1690 Some(Callable { ty: self.clone(), sig, def, is_bound_method: false })
1691 }
1692
1693 pub fn is_closure(&self) -> bool {
1694 matches!(&self.ty.value, Ty::Closure { .. })
1695 }
1696
1697 pub fn is_fn(&self) -> bool {
1698 matches!(&self.ty.value, Ty::FnDef(..) | Ty::Function { .. })
1699 }
1700
1701 pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
1702 let adt_id = match self.ty.value {
1703 Ty::Adt(adt_id, ..) => adt_id,
1704 _ => return false,
1705 };
1706
1707 let adt = adt_id.into();
1708 match adt {
1709 Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)),
1710 _ => false,
1711 }
1712 }
1713
1714 pub fn is_raw_ptr(&self) -> bool {
1715 matches!(&self.ty.value, Ty::Raw(..))
1716 }
1717
1718 pub fn contains_unknown(&self) -> bool {
1719 return go(&self.ty.value);
1720
1721 fn go(ty: &Ty) -> bool {
1722 match ty {
1723 Ty::Unknown => true,
1724 _ => ty.substs().map_or(false, |substs| substs.iter().any(go)),
1725 }
1726 }
1727 }
1728
1729 pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> {
1730 let (variant_id, substs) = match self.ty.value {
1731 Ty::Adt(AdtId::StructId(s), ref substs) => (s.into(), substs),
1732 Ty::Adt(AdtId::UnionId(u), ref substs) => (u.into(), substs),
1733 _ => return Vec::new(),
1734 };
1735
1736 db.field_types(variant_id)
1737 .iter()
1738 .map(|(local_id, ty)| {
1739 let def = Field { parent: variant_id.into(), id: local_id };
1740 let ty = ty.clone().subst(substs);
1741 (def, self.derived(ty))
1742 })
1743 .collect()
1744 }
1745
1746 pub fn tuple_fields(&self, _db: &dyn HirDatabase) -> Vec<Type> {
1747 if let Ty::Tuple(_, substs) = &self.ty.value {
1748 substs.iter().map(|ty| self.derived(ty.clone())).collect()
1749 } else {
1750 Vec::new()
1751 }
1752 }
1753
1754 pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
1755 // There should be no inference vars in types passed here
1756 // FIXME check that?
1757 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1758 let environment = self.ty.environment.clone();
1759 let ty = InEnvironment { value: canonical, environment };
1760 autoderef(db, Some(self.krate), ty)
1761 .map(|canonical| canonical.value)
1762 .map(move |ty| self.derived(ty))
1763 }
1764
1765 // This would be nicer if it just returned an iterator, but that runs into
1766 // lifetime problems, because we need to borrow temp `CrateImplDefs`.
1767 pub fn iterate_assoc_items<T>(
1768 self,
1769 db: &dyn HirDatabase,
1770 krate: Crate,
1771 mut callback: impl FnMut(AssocItem) -> Option<T>,
1772 ) -> Option<T> {
1773 for krate in self.ty.value.def_crates(db, krate.id)? {
1774 let impls = db.inherent_impls_in_crate(krate);
1775
1776 for impl_def in impls.for_self_ty(&self.ty.value) {
1777 for &item in db.impl_data(*impl_def).items.iter() {
1778 if let Some(result) = callback(item.into()) {
1779 return Some(result);
1780 }
1781 }
1782 }
1783 }
1784 None
1785 }
1786
1787 pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ {
1788 self.ty
1789 .value
1790 .strip_references()
1791 .substs()
1792 .into_iter()
1793 .flat_map(|substs| substs.iter())
1794 .map(move |ty| self.derived(ty.clone()))
1795 }
1796
1797 pub fn iterate_method_candidates<T>(
1798 &self,
1799 db: &dyn HirDatabase,
1800 krate: Crate,
1801 traits_in_scope: &FxHashSet<TraitId>,
1802 name: Option<&Name>,
1803 mut callback: impl FnMut(&Ty, Function) -> Option<T>,
1804 ) -> Option<T> {
1805 // There should be no inference vars in types passed here
1806 // FIXME check that?
1807 // FIXME replace Unknown by bound vars here
1808 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1809
1810 let env = self.ty.environment.clone();
1811 let krate = krate.id;
1812
1813 method_resolution::iterate_method_candidates(
1814 &canonical,
1815 db,
1816 env,
1817 krate,
1818 traits_in_scope,
1819 name,
1820 method_resolution::LookupMode::MethodCall,
1821 |ty, it| match it {
1822 AssocItemId::FunctionId(f) => callback(ty, f.into()),
1823 _ => None,
1824 },
1825 )
1826 }
1827
1828 pub fn iterate_path_candidates<T>(
1829 &self,
1830 db: &dyn HirDatabase,
1831 krate: Crate,
1832 traits_in_scope: &FxHashSet<TraitId>,
1833 name: Option<&Name>,
1834 mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
1835 ) -> Option<T> {
1836 // There should be no inference vars in types passed here
1837 // FIXME check that?
1838 // FIXME replace Unknown by bound vars here
1839 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1840
1841 let env = self.ty.environment.clone();
1842 let krate = krate.id;
1843
1844 method_resolution::iterate_method_candidates(
1845 &canonical,
1846 db,
1847 env,
1848 krate,
1849 traits_in_scope,
1850 name,
1851 method_resolution::LookupMode::Path,
1852 |ty, it| callback(ty, it.into()),
1853 )
1854 }
1855
1856 pub fn as_adt(&self) -> Option<Adt> {
1857 let (adt, _subst) = self.ty.value.as_adt()?;
1858 Some(adt.into())
1859 }
1860
1861 pub fn as_dyn_trait(&self) -> Option<Trait> {
1862 self.ty.value.dyn_trait().map(Into::into)
1863 }
1864
1865 pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> {
1866 self.ty.value.impl_trait_bounds(db).map(|it| {
1867 it.into_iter()
1868 .filter_map(|pred| match pred {
1869 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1870 Some(Trait::from(trait_ref.trait_))
1871 }
1872 _ => None,
1873 })
1874 .collect()
1875 })
1876 }
1877
1878 pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> {
1879 self.ty.value.associated_type_parent_trait(db).map(Into::into)
1880 }
1881
1882 // FIXME: provide required accessors such that it becomes implementable from outside.
1883 pub fn is_equal_for_find_impls(&self, other: &Type) -> bool {
1884 let rref = other.remove_ref();
1885 self.ty.value.equals_ctor(rref.as_ref().map_or(&other.ty.value, |it| &it.ty.value))
1886 }
1887
1888 fn derived(&self, ty: Ty) -> Type {
1889 Type {
1890 krate: self.krate,
1891 ty: InEnvironment { value: ty, environment: self.ty.environment.clone() },
1892 }
1893 }
1894
1895 pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) {
1896 // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself.
1897 // We need a different order here.
1898
1899 fn walk_substs(
1900 db: &dyn HirDatabase,
1901 type_: &Type,
1902 substs: &Substs,
1903 cb: &mut impl FnMut(Type),
1904 ) {
1905 for ty in substs.iter() {
1906 walk_type(db, &type_.derived(ty.clone()), cb);
1907 }
1908 }
1909
1910 fn walk_bounds(
1911 db: &dyn HirDatabase,
1912 type_: &Type,
1913 bounds: &[GenericPredicate],
1914 cb: &mut impl FnMut(Type),
1915 ) {
1916 for pred in bounds {
1917 match pred {
1918 GenericPredicate::Implemented(trait_ref) => {
1919 cb(type_.clone());
1920 walk_substs(db, type_, &trait_ref.substs, cb);
1921 }
1922 _ => (),
1923 }
1924 }
1925 }
1926
1927 fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
1928 let ty = type_.ty.value.strip_references();
1929 match ty {
1930 Ty::Adt(..) => {
1931 cb(type_.derived(ty.clone()));
1932 }
1933 Ty::AssociatedType(..) => {
1934 if let Some(_) = ty.associated_type_parent_trait(db) {
1935 cb(type_.derived(ty.clone()));
1936 }
1937 }
1938 Ty::OpaqueType(..) => {
1939 if let Some(bounds) = ty.impl_trait_bounds(db) {
1940 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1941 }
1942 }
1943 Ty::Alias(AliasTy::Opaque(opaque_ty)) => {
1944 if let Some(bounds) = ty.impl_trait_bounds(db) {
1945 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1946 }
1947
1948 walk_substs(db, type_, &opaque_ty.parameters, cb);
1949 }
1950 Ty::Placeholder(_) => {
1951 if let Some(bounds) = ty.impl_trait_bounds(db) {
1952 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1953 }
1954 }
1955 Ty::Dyn(bounds) => {
1956 walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb);
1957 }
1958
1959 _ => {}
1960 }
1961 if let Some(substs) = ty.substs() {
1962 walk_substs(db, type_, &substs, cb);
1963 }
1964 }
1965
1966 walk_type(db, self, &mut cb);
1967 }
1968}
1969
1970impl HirDisplay for Type {
1971 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
1972 self.ty.value.hir_fmt(f)
1973 }
1974}
1975
1976// FIXME: closures
1977#[derive(Debug)]
1978pub struct Callable {
1979 ty: Type,
1980 sig: CallableSig,
1981 def: Option<CallableDefId>,
1982 pub(crate) is_bound_method: bool,
1983}
1984
1985pub enum CallableKind {
1986 Function(Function),
1987 TupleStruct(Struct),
1988 TupleEnumVariant(Variant),
1989 Closure,
1990}
1991
1992impl Callable {
1993 pub fn kind(&self) -> CallableKind {
1994 match self.def {
1995 Some(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()),
1996 Some(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()),
1997 Some(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()),
1998 None => CallableKind::Closure,
1999 }
2000 }
2001 pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {
2002 let func = match self.def {
2003 Some(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
2004 _ => return None,
2005 };
2006 let src = func.lookup(db.upcast()).source(db.upcast());
2007 let param_list = src.value.param_list()?;
2008 param_list.self_param()
2009 }
2010 pub fn n_params(&self) -> usize {
2011 self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
2012 }
2013 pub fn params(
2014 &self,
2015 db: &dyn HirDatabase,
2016 ) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> {
2017 let types = self
2018 .sig
2019 .params()
2020 .iter()
2021 .skip(if self.is_bound_method { 1 } else { 0 })
2022 .map(|ty| self.ty.derived(ty.clone()));
2023 let patterns = match self.def {
2024 Some(CallableDefId::FunctionId(func)) => {
2025 let src = func.lookup(db.upcast()).source(db.upcast());
2026 src.value.param_list().map(|param_list| {
2027 param_list
2028 .self_param()
2029 .map(|it| Some(Either::Left(it)))
2030 .filter(|_| !self.is_bound_method)
2031 .into_iter()
2032 .chain(param_list.params().map(|it| it.pat().map(Either::Right)))
2033 })
2034 }
2035 _ => None,
2036 };
2037 patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect()
2038 }
2039 pub fn return_type(&self) -> Type {
2040 self.ty.derived(self.sig.ret().clone())
2041 }
2042}
2043
2044/// For IDE only
2045#[derive(Debug, PartialEq, Eq, Hash)]
2046pub enum ScopeDef {
2047 ModuleDef(ModuleDef),
2048 MacroDef(MacroDef),
2049 GenericParam(GenericParam),
2050 ImplSelfType(Impl),
2051 AdtSelfType(Adt),
2052 Local(Local),
2053 Unknown,
2054}
2055
2056impl ScopeDef {
2057 pub fn all_items(def: PerNs) -> ArrayVec<[Self; 3]> {
2058 let mut items = ArrayVec::new();
2059
2060 match (def.take_types(), def.take_values()) {
2061 (Some(m1), None) => items.push(ScopeDef::ModuleDef(m1.into())),
2062 (None, Some(m2)) => items.push(ScopeDef::ModuleDef(m2.into())),
2063 (Some(m1), Some(m2)) => {
2064 // Some items, like unit structs and enum variants, are
2065 // returned as both a type and a value. Here we want
2066 // to de-duplicate them.
2067 if m1 != m2 {
2068 items.push(ScopeDef::ModuleDef(m1.into()));
2069 items.push(ScopeDef::ModuleDef(m2.into()));
2070 } else {
2071 items.push(ScopeDef::ModuleDef(m1.into()));
2072 }
2073 }
2074 (None, None) => {}
2075 };
2076
2077 if let Some(macro_def_id) = def.take_macros() {
2078 items.push(ScopeDef::MacroDef(macro_def_id.into()));
2079 }
2080
2081 if items.is_empty() {
2082 items.push(ScopeDef::Unknown);
2083 }
2084
2085 items
2086 }
2087}
2088
2089pub trait HasVisibility {
2090 fn visibility(&self, db: &dyn HirDatabase) -> Visibility;
2091 fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool {
2092 let vis = self.visibility(db);
2093 vis.is_visible_from(db.upcast(), module.id)
2094 }
2095}
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index b5814da11..179b9d51e 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -11,9 +11,8 @@ use hir_def::{
11}; 11};
12 12
13use crate::{ 13use crate::{
14 code_model::{BuiltinType, GenericParam}, 14 Adt, AssocItem, BuiltinType, DefWithBody, Field, GenericDef, GenericParam, Label, Local,
15 Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local, MacroDef, ModuleDef, Variant, 15 MacroDef, ModuleDef, Variant, VariantDef,
16 VariantDef,
17}; 16};
18 17
19macro_rules! from_id { 18macro_rules! from_id {
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 69fcdab07..4ef38c0f0 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -20,49 +20,2118 @@
20#![recursion_limit = "512"] 20#![recursion_limit = "512"]
21 21
22mod semantics; 22mod semantics;
23pub mod db;
24mod source_analyzer; 23mod source_analyzer;
25 24
26pub mod diagnostics;
27
28mod from_id; 25mod from_id;
29mod code_model;
30mod attrs; 26mod attrs;
31mod has_source; 27mod has_source;
32 28
29pub mod diagnostics;
30pub mod db;
31
32use std::{iter, sync::Arc};
33
34use arrayvec::ArrayVec;
35use base_db::{CrateDisplayName, CrateId, Edition, FileId};
36use either::Either;
37use hir_def::{
38 adt::{ReprKind, VariantData},
39 expr::{BindingAnnotation, LabelId, Pat, PatId},
40 item_tree::ItemTreeNode,
41 lang_item::LangItemTarget,
42 per_ns::PerNs,
43 resolver::{HasResolver, Resolver},
44 src::HasSource as _,
45 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId,
46 DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
47 LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
48 TypeParamId, UnionId,
49};
50use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind};
51use hir_ty::{
52 autoderef,
53 display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter},
54 method_resolution,
55 traits::{FnTrait, Solution, SolutionVariables},
56 AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate,
57 InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Scalar, Substs, TraitEnvironment,
58 Ty, TyDefId, TyVariableKind,
59};
60use rustc_hash::FxHashSet;
61use stdx::{format_to, impl_from};
62use syntax::{
63 ast::{self, AttrsOwner, NameOwner},
64 AstNode, SmolStr,
65};
66use tt::{Ident, Leaf, Literal, TokenTree};
67
68use crate::db::{DefDatabase, HirDatabase};
69
33pub use crate::{ 70pub use crate::{
34 attrs::{HasAttrs, Namespace}, 71 attrs::{HasAttrs, Namespace},
35 code_model::{
36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, BuiltinType, Callable,
37 CallableKind, Const, ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field,
38 FieldSource, Function, GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam,
39 Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias,
40 TypeParam, Union, Variant, VariantDef,
41 },
42 has_source::HasSource, 72 has_source::HasSource,
43 semantics::{PathResolution, Semantics, SemanticsScope}, 73 semantics::{PathResolution, Semantics, SemanticsScope},
44}; 74};
45 75
46pub use hir_def::{ 76// Be careful with these re-exports.
47 adt::StructKind, 77//
48 attr::{Attrs, Documentation}, 78// `hir` is the boundary between the compiler and the IDE. It should try hard to
49 body::scope::ExprScopes, 79// isolate the compiler from the ide, to allow the two to be refactored
50 find_path::PrefixKind, 80// independently. Re-exporting something from the compiler is the sure way to
51 import_map, 81// breach the boundary.
52 item_scope::ItemInNs, 82//
53 nameres::ModuleSource, 83// Generally, a refactoring which *removes* a name from this list is a good
54 path::{ModPath, PathKind}, 84// idea!
55 type_ref::{Mutability, TypeRef}, 85pub use {
56 visibility::Visibility, 86 hir_def::{
57}; 87 adt::StructKind,
58pub use hir_expand::{ 88 attr::{Attrs, Documentation},
59 name::{known, AsName, Name}, 89 body::scope::ExprScopes,
60 ExpandResult, HirFileId, InFile, MacroCallId, MacroCallLoc, /* FIXME */ MacroDefId, 90 find_path::PrefixKind,
61 MacroFile, Origin, 91 import_map,
92 item_scope::ItemInNs,
93 nameres::ModuleSource,
94 path::{ModPath, PathKind},
95 type_ref::{Mutability, TypeRef},
96 visibility::Visibility,
97 },
98 hir_expand::{
99 name::{known, Name},
100 ExpandResult, HirFileId, InFile, MacroCallId, MacroCallLoc, /* FIXME */ MacroDefId,
101 MacroFile, Origin,
102 },
103 hir_ty::display::HirDisplay,
62}; 104};
63pub use hir_ty::display::HirDisplay;
64 105
65// These are negative re-exports: pub using these names is forbidden, they 106// These are negative re-exports: pub using these names is forbidden, they
66// should remain private to hir internals. 107// should remain private to hir internals.
67#[allow(unused)] 108#[allow(unused)]
68use {hir_def::path::Path, hir_expand::hygiene::Hygiene}; 109use {
110 hir_def::path::Path,
111 hir_expand::{hygiene::Hygiene, name::AsName},
112};
113
114/// hir::Crate describes a single crate. It's the main interface with which
115/// a crate's dependencies interact. Mostly, it should be just a proxy for the
116/// root module.
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
118pub struct Crate {
119 pub(crate) id: CrateId,
120}
121
122#[derive(Debug)]
123pub struct CrateDependency {
124 pub krate: Crate,
125 pub name: Name,
126}
127
128impl Crate {
129 pub fn dependencies(self, db: &dyn HirDatabase) -> Vec<CrateDependency> {
130 db.crate_graph()[self.id]
131 .dependencies
132 .iter()
133 .map(|dep| {
134 let krate = Crate { id: dep.crate_id };
135 let name = dep.as_name();
136 CrateDependency { krate, name }
137 })
138 .collect()
139 }
140
141 // FIXME: add `transitive_reverse_dependencies`.
142 pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> {
143 let crate_graph = db.crate_graph();
144 crate_graph
145 .iter()
146 .filter(|&krate| {
147 crate_graph[krate].dependencies.iter().any(|it| it.crate_id == self.id)
148 })
149 .map(|id| Crate { id })
150 .collect()
151 }
152
153 pub fn root_module(self, db: &dyn HirDatabase) -> Module {
154 let def_map = db.crate_def_map(self.id);
155 Module { id: def_map.module_id(def_map.root()) }
156 }
157
158 pub fn root_file(self, db: &dyn HirDatabase) -> FileId {
159 db.crate_graph()[self.id].root_file_id
160 }
161
162 pub fn edition(self, db: &dyn HirDatabase) -> Edition {
163 db.crate_graph()[self.id].edition
164 }
165
166 pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateDisplayName> {
167 db.crate_graph()[self.id].display_name.clone()
168 }
169
170 pub fn query_external_importables(
171 self,
172 db: &dyn DefDatabase,
173 query: import_map::Query,
174 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
175 import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item {
176 ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()),
177 ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()),
178 })
179 }
180
181 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
182 db.crate_graph().iter().map(|id| Crate { id }).collect()
183 }
184
185 /// Try to get the root URL of the documentation of a crate.
186 pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> {
187 // Look for #![doc(html_root_url = "...")]
188 let attrs = db.attrs(AttrDefId::ModuleId(self.root_module(db).into()));
189 let doc_attr_q = attrs.by_key("doc");
190
191 if !doc_attr_q.exists() {
192 return None;
193 }
194
195 let doc_url = doc_attr_q.tt_values().map(|tt| {
196 let name = tt.token_trees.iter()
197 .skip_while(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident, ..})) if ident == "html_root_url"))
198 .skip(2)
199 .next();
200
201 match name {
202 Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text),
203 _ => None
204 }
205 }).flat_map(|t| t).next();
206
207 doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/")
208 }
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
212pub struct Module {
213 pub(crate) id: ModuleId,
214}
215
216/// The defs which can be visible in the module.
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
218pub enum ModuleDef {
219 Module(Module),
220 Function(Function),
221 Adt(Adt),
222 // Can't be directly declared, but can be imported.
223 Variant(Variant),
224 Const(Const),
225 Static(Static),
226 Trait(Trait),
227 TypeAlias(TypeAlias),
228 BuiltinType(BuiltinType),
229}
230impl_from!(
231 Module,
232 Function,
233 Adt(Struct, Enum, Union),
234 Variant,
235 Const,
236 Static,
237 Trait,
238 TypeAlias,
239 BuiltinType
240 for ModuleDef
241);
242
243impl From<VariantDef> for ModuleDef {
244 fn from(var: VariantDef) -> Self {
245 match var {
246 VariantDef::Struct(t) => Adt::from(t).into(),
247 VariantDef::Union(t) => Adt::from(t).into(),
248 VariantDef::Variant(t) => t.into(),
249 }
250 }
251}
252
253impl ModuleDef {
254 pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
255 match self {
256 ModuleDef::Module(it) => it.parent(db),
257 ModuleDef::Function(it) => Some(it.module(db)),
258 ModuleDef::Adt(it) => Some(it.module(db)),
259 ModuleDef::Variant(it) => Some(it.module(db)),
260 ModuleDef::Const(it) => Some(it.module(db)),
261 ModuleDef::Static(it) => Some(it.module(db)),
262 ModuleDef::Trait(it) => Some(it.module(db)),
263 ModuleDef::TypeAlias(it) => Some(it.module(db)),
264 ModuleDef::BuiltinType(_) => None,
265 }
266 }
267
268 pub fn canonical_path(&self, db: &dyn HirDatabase) -> Option<String> {
269 let mut segments = Vec::new();
270 segments.push(self.name(db)?.to_string());
271 for m in self.module(db)?.path_to_root(db) {
272 segments.extend(m.name(db).map(|it| it.to_string()))
273 }
274 segments.reverse();
275 Some(segments.join("::"))
276 }
277
278 pub fn definition_visibility(&self, db: &dyn HirDatabase) -> Option<Visibility> {
279 let module = match self {
280 ModuleDef::Module(it) => it.parent(db)?,
281 ModuleDef::Function(it) => return Some(it.visibility(db)),
282 ModuleDef::Adt(it) => it.module(db),
283 ModuleDef::Variant(it) => {
284 let parent = it.parent_enum(db);
285 let module = it.module(db);
286 return module.visibility_of(db, &ModuleDef::Adt(Adt::Enum(parent)));
287 }
288 ModuleDef::Const(it) => return Some(it.visibility(db)),
289 ModuleDef::Static(it) => it.module(db),
290 ModuleDef::Trait(it) => it.module(db),
291 ModuleDef::TypeAlias(it) => return Some(it.visibility(db)),
292 ModuleDef::BuiltinType(_) => return None,
293 };
294
295 module.visibility_of(db, self)
296 }
297
298 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
299 match self {
300 ModuleDef::Adt(it) => Some(it.name(db)),
301 ModuleDef::Trait(it) => Some(it.name(db)),
302 ModuleDef::Function(it) => Some(it.name(db)),
303 ModuleDef::Variant(it) => Some(it.name(db)),
304 ModuleDef::TypeAlias(it) => Some(it.name(db)),
305 ModuleDef::Module(it) => it.name(db),
306 ModuleDef::Const(it) => it.name(db),
307 ModuleDef::Static(it) => it.name(db),
308
309 ModuleDef::BuiltinType(it) => Some(it.name()),
310 }
311 }
312
313 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
314 let id = match self {
315 ModuleDef::Adt(it) => match it {
316 Adt::Struct(it) => it.id.into(),
317 Adt::Enum(it) => it.id.into(),
318 Adt::Union(it) => it.id.into(),
319 },
320 ModuleDef::Trait(it) => it.id.into(),
321 ModuleDef::Function(it) => it.id.into(),
322 ModuleDef::TypeAlias(it) => it.id.into(),
323 ModuleDef::Module(it) => it.id.into(),
324 ModuleDef::Const(it) => it.id.into(),
325 ModuleDef::Static(it) => it.id.into(),
326 _ => return,
327 };
328
329 let module = match self.module(db) {
330 Some(it) => it,
331 None => return,
332 };
333
334 hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink)
335 }
336}
337
338impl Module {
339 /// Name of this module.
340 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
341 let def_map = self.id.def_map(db.upcast());
342 let parent = def_map[self.id.local_id].parent?;
343 def_map[parent].children.iter().find_map(|(name, module_id)| {
344 if *module_id == self.id.local_id {
345 Some(name.clone())
346 } else {
347 None
348 }
349 })
350 }
351
352 /// Returns the crate this module is part of.
353 pub fn krate(self) -> Crate {
354 Crate { id: self.id.krate() }
355 }
356
357 /// Topmost parent of this module. Every module has a `crate_root`, but some
358 /// might be missing `krate`. This can happen if a module's file is not included
359 /// in the module tree of any target in `Cargo.toml`.
360 pub fn crate_root(self, db: &dyn HirDatabase) -> Module {
361 let def_map = db.crate_def_map(self.id.krate());
362 Module { id: def_map.module_id(def_map.root()) }
363 }
364
365 /// Iterates over all child modules.
366 pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> {
367 let def_map = self.id.def_map(db.upcast());
368 let children = def_map[self.id.local_id]
369 .children
370 .iter()
371 .map(|(_, module_id)| Module { id: def_map.module_id(*module_id) })
372 .collect::<Vec<_>>();
373 children.into_iter()
374 }
375
376 /// Finds a parent module.
377 pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> {
378 // FIXME: handle block expressions as modules (their parent is in a different DefMap)
379 let def_map = self.id.def_map(db.upcast());
380 let parent_id = def_map[self.id.local_id].parent?;
381 Some(Module { id: def_map.module_id(parent_id) })
382 }
383
384 pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> {
385 let mut res = vec![self];
386 let mut curr = self;
387 while let Some(next) = curr.parent(db) {
388 res.push(next);
389 curr = next
390 }
391 res
392 }
393
394 /// Returns a `ModuleScope`: a set of items, visible in this module.
395 pub fn scope(
396 self,
397 db: &dyn HirDatabase,
398 visible_from: Option<Module>,
399 ) -> Vec<(Name, ScopeDef)> {
400 self.id.def_map(db.upcast())[self.id.local_id]
401 .scope
402 .entries()
403 .filter_map(|(name, def)| {
404 if let Some(m) = visible_from {
405 let filtered =
406 def.filter_visibility(|vis| vis.is_visible_from(db.upcast(), m.id));
407 if filtered.is_none() && !def.is_none() {
408 None
409 } else {
410 Some((name, filtered))
411 }
412 } else {
413 Some((name, def))
414 }
415 })
416 .flat_map(|(name, def)| {
417 ScopeDef::all_items(def).into_iter().map(move |item| (name.clone(), item))
418 })
419 .collect()
420 }
421
422 pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> {
423 self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into())
424 }
425
426 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
427 let _p = profile::span("Module::diagnostics").detail(|| {
428 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
429 });
430 let def_map = self.id.def_map(db.upcast());
431 def_map.add_diagnostics(db.upcast(), self.id.local_id, sink);
432 for decl in self.declarations(db) {
433 match decl {
434 crate::ModuleDef::Function(f) => f.diagnostics(db, sink),
435 crate::ModuleDef::Module(m) => {
436 // Only add diagnostics from inline modules
437 if def_map[m.id.local_id].origin.is_inline() {
438 m.diagnostics(db, sink)
439 }
440 }
441 _ => {
442 decl.diagnostics(db, sink);
443 }
444 }
445 }
446
447 for impl_def in self.impl_defs(db) {
448 for item in impl_def.items(db) {
449 if let AssocItem::Function(f) = item {
450 f.diagnostics(db, sink);
451 }
452 }
453 }
454 }
455
456 pub fn declarations(self, db: &dyn HirDatabase) -> Vec<ModuleDef> {
457 let def_map = self.id.def_map(db.upcast());
458 def_map[self.id.local_id].scope.declarations().map(ModuleDef::from).collect()
459 }
460
461 pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> {
462 let def_map = self.id.def_map(db.upcast());
463 def_map[self.id.local_id].scope.impls().map(Impl::from).collect()
464 }
465
466 /// Finds a path that can be used to refer to the given item from within
467 /// this module, if possible.
468 pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> {
469 hir_def::find_path::find_path(db, item.into(), self.into())
470 }
471
472 /// Finds a path that can be used to refer to the given item from within
473 /// this module, if possible. This is used for returning import paths for use-statements.
474 pub fn find_use_path_prefixed(
475 self,
476 db: &dyn DefDatabase,
477 item: impl Into<ItemInNs>,
478 prefix_kind: PrefixKind,
479 ) -> Option<ModPath> {
480 hir_def::find_path::find_path_prefixed(db, item.into(), self.into(), prefix_kind)
481 }
482}
483
484#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
485pub struct Field {
486 pub(crate) parent: VariantDef,
487 pub(crate) id: LocalFieldId,
488}
489
490#[derive(Debug, PartialEq, Eq)]
491pub enum FieldSource {
492 Named(ast::RecordField),
493 Pos(ast::TupleField),
494}
495
496impl Field {
497 pub fn name(&self, db: &dyn HirDatabase) -> Name {
498 self.parent.variant_data(db).fields()[self.id].name.clone()
499 }
500
501 /// Returns the type as in the signature of the struct (i.e., with
502 /// placeholder types for type parameters). This is good for showing
503 /// signature help, but not so good to actually get the type of the field
504 /// when you actually have a variable of the struct.
505 pub fn signature_ty(&self, db: &dyn HirDatabase) -> Type {
506 let var_id = self.parent.into();
507 let generic_def_id: GenericDefId = match self.parent {
508 VariantDef::Struct(it) => it.id.into(),
509 VariantDef::Union(it) => it.id.into(),
510 VariantDef::Variant(it) => it.parent.id.into(),
511 };
512 let substs = Substs::type_params(db, generic_def_id);
513 let ty = db.field_types(var_id)[self.id].clone().subst(&substs);
514 Type::new(db, self.parent.module(db).id.krate(), var_id, ty)
515 }
516
517 pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef {
518 self.parent
519 }
520}
521
522impl HasVisibility for Field {
523 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
524 let variant_data = self.parent.variant_data(db);
525 let visibility = &variant_data.fields()[self.id].visibility;
526 let parent_id: hir_def::VariantId = self.parent.into();
527 visibility.resolve(db.upcast(), &parent_id.resolver(db.upcast()))
528 }
529}
530
531#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
532pub struct Struct {
533 pub(crate) id: StructId,
534}
535
536impl Struct {
537 pub fn module(self, db: &dyn HirDatabase) -> Module {
538 Module { id: self.id.lookup(db.upcast()).container }
539 }
540
541 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
542 Some(self.module(db).krate())
543 }
544
545 pub fn name(self, db: &dyn HirDatabase) -> Name {
546 db.struct_data(self.id).name.clone()
547 }
548
549 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
550 db.struct_data(self.id)
551 .variant_data
552 .fields()
553 .iter()
554 .map(|(id, _)| Field { parent: self.into(), id })
555 .collect()
556 }
557
558 pub fn ty(self, db: &dyn HirDatabase) -> Type {
559 Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id)
560 }
561
562 pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> {
563 db.struct_data(self.id).repr.clone()
564 }
565
566 pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
567 self.variant_data(db).kind()
568 }
569
570 fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
571 db.struct_data(self.id).variant_data.clone()
572 }
573}
574
575#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
576pub struct Union {
577 pub(crate) id: UnionId,
578}
579
580impl Union {
581 pub fn name(self, db: &dyn HirDatabase) -> Name {
582 db.union_data(self.id).name.clone()
583 }
584
585 pub fn module(self, db: &dyn HirDatabase) -> Module {
586 Module { id: self.id.lookup(db.upcast()).container }
587 }
588
589 pub fn ty(self, db: &dyn HirDatabase) -> Type {
590 Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id)
591 }
592
593 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
594 db.union_data(self.id)
595 .variant_data
596 .fields()
597 .iter()
598 .map(|(id, _)| Field { parent: self.into(), id })
599 .collect()
600 }
601
602 fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
603 db.union_data(self.id).variant_data.clone()
604 }
605}
606
607#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
608pub struct Enum {
609 pub(crate) id: EnumId,
610}
611
612impl Enum {
613 pub fn module(self, db: &dyn HirDatabase) -> Module {
614 Module { id: self.id.lookup(db.upcast()).container }
615 }
616
617 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
618 Some(self.module(db).krate())
619 }
620
621 pub fn name(self, db: &dyn HirDatabase) -> Name {
622 db.enum_data(self.id).name.clone()
623 }
624
625 pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> {
626 db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect()
627 }
628
629 pub fn ty(self, db: &dyn HirDatabase) -> Type {
630 Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id)
631 }
632}
633
634#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
635pub struct Variant {
636 pub(crate) parent: Enum,
637 pub(crate) id: LocalEnumVariantId,
638}
639
640impl Variant {
641 pub fn module(self, db: &dyn HirDatabase) -> Module {
642 self.parent.module(db)
643 }
644 pub fn parent_enum(self, _db: &dyn HirDatabase) -> Enum {
645 self.parent
646 }
647
648 pub fn name(self, db: &dyn HirDatabase) -> Name {
649 db.enum_data(self.parent.id).variants[self.id].name.clone()
650 }
651
652 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
653 self.variant_data(db)
654 .fields()
655 .iter()
656 .map(|(id, _)| Field { parent: self.into(), id })
657 .collect()
658 }
659
660 pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
661 self.variant_data(db).kind()
662 }
663
664 pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
665 db.enum_data(self.parent.id).variants[self.id].variant_data.clone()
666 }
667}
668
669/// A Data Type
670#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
671pub enum Adt {
672 Struct(Struct),
673 Union(Union),
674 Enum(Enum),
675}
676impl_from!(Struct, Union, Enum for Adt);
677
678impl Adt {
679 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
680 let subst = db.generic_defaults(self.into());
681 subst.iter().any(|ty| &ty.value == &Ty::Unknown)
682 }
683
684 /// Turns this ADT into a type. Any type parameters of the ADT will be
685 /// turned into unknown types, which is good for e.g. finding the most
686 /// general set of completions, but will not look very nice when printed.
687 pub fn ty(self, db: &dyn HirDatabase) -> Type {
688 let id = AdtId::from(self);
689 Type::from_def(db, id.module(db.upcast()).krate(), id)
690 }
691
692 pub fn module(self, db: &dyn HirDatabase) -> Module {
693 match self {
694 Adt::Struct(s) => s.module(db),
695 Adt::Union(s) => s.module(db),
696 Adt::Enum(e) => e.module(db),
697 }
698 }
699
700 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
701 Some(self.module(db).krate())
702 }
703
704 pub fn name(self, db: &dyn HirDatabase) -> Name {
705 match self {
706 Adt::Struct(s) => s.name(db),
707 Adt::Union(u) => u.name(db),
708 Adt::Enum(e) => e.name(db),
709 }
710 }
711}
712
713#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
714pub enum VariantDef {
715 Struct(Struct),
716 Union(Union),
717 Variant(Variant),
718}
719impl_from!(Struct, Union, Variant for VariantDef);
720
721impl VariantDef {
722 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
723 match self {
724 VariantDef::Struct(it) => it.fields(db),
725 VariantDef::Union(it) => it.fields(db),
726 VariantDef::Variant(it) => it.fields(db),
727 }
728 }
729
730 pub fn module(self, db: &dyn HirDatabase) -> Module {
731 match self {
732 VariantDef::Struct(it) => it.module(db),
733 VariantDef::Union(it) => it.module(db),
734 VariantDef::Variant(it) => it.module(db),
735 }
736 }
737
738 pub fn name(&self, db: &dyn HirDatabase) -> Name {
739 match self {
740 VariantDef::Struct(s) => s.name(db),
741 VariantDef::Union(u) => u.name(db),
742 VariantDef::Variant(e) => e.name(db),
743 }
744 }
745
746 pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
747 match self {
748 VariantDef::Struct(it) => it.variant_data(db),
749 VariantDef::Union(it) => it.variant_data(db),
750 VariantDef::Variant(it) => it.variant_data(db),
751 }
752 }
753}
754
755/// The defs which have a body.
756#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
757pub enum DefWithBody {
758 Function(Function),
759 Static(Static),
760 Const(Const),
761}
762impl_from!(Function, Const, Static for DefWithBody);
763
764impl DefWithBody {
765 pub fn module(self, db: &dyn HirDatabase) -> Module {
766 match self {
767 DefWithBody::Const(c) => c.module(db),
768 DefWithBody::Function(f) => f.module(db),
769 DefWithBody::Static(s) => s.module(db),
770 }
771 }
772
773 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
774 match self {
775 DefWithBody::Function(f) => Some(f.name(db)),
776 DefWithBody::Static(s) => s.name(db),
777 DefWithBody::Const(c) => c.name(db),
778 }
779 }
780}
781
782#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
783pub struct Function {
784 pub(crate) id: FunctionId,
785}
786
787impl Function {
788 pub fn module(self, db: &dyn HirDatabase) -> Module {
789 self.id.lookup(db.upcast()).module(db.upcast()).into()
790 }
791
792 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
793 Some(self.module(db).krate())
794 }
795
796 pub fn name(self, db: &dyn HirDatabase) -> Name {
797 db.function_data(self.id).name.clone()
798 }
799
800 /// Get this function's return type
801 pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
802 let resolver = self.id.resolver(db.upcast());
803 let krate = self.id.lookup(db.upcast()).container.module(db.upcast()).krate();
804 let ret_type = &db.function_data(self.id).ret_type;
805 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
806 let ty = Ty::from_hir_ext(&ctx, ret_type).0;
807 Type::new_with_resolver_inner(db, krate, &resolver, ty)
808 }
809
810 pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
811 if !db.function_data(self.id).has_self_param {
812 return None;
813 }
814 Some(SelfParam { func: self.id })
815 }
816
817 pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> {
818 let resolver = self.id.resolver(db.upcast());
819 let krate = self.id.lookup(db.upcast()).container.module(db.upcast()).krate();
820 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
821 let environment = TraitEnvironment::lower(db, &resolver);
822 db.function_data(self.id)
823 .params
824 .iter()
825 .map(|type_ref| {
826 let ty = Type {
827 krate,
828 ty: InEnvironment {
829 value: Ty::from_hir_ext(&ctx, type_ref).0,
830 environment: environment.clone(),
831 },
832 };
833 Param { ty }
834 })
835 .collect()
836 }
837 pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
838 if self.self_param(db).is_none() {
839 return None;
840 }
841 let mut res = self.assoc_fn_params(db);
842 res.remove(0);
843 Some(res)
844 }
845
846 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
847 db.function_data(self.id).is_unsafe
848 }
849
850 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
851 let krate = self.module(db).id.krate();
852 hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink);
853 hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink);
854 hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
855 }
856
857 /// Whether this function declaration has a definition.
858 ///
859 /// This is false in the case of required (not provided) trait methods.
860 pub fn has_body(self, db: &dyn HirDatabase) -> bool {
861 db.function_data(self.id).has_body
862 }
863
864 /// A textual representation of the HIR of this function for debugging purposes.
865 pub fn debug_hir(self, db: &dyn HirDatabase) -> String {
866 let body = db.body(self.id.into());
867
868 let mut result = String::new();
869 format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db));
870 for (id, expr) in body.exprs.iter() {
871 format_to!(result, "{:?}: {:?}\n", id, expr);
872 }
873
874 result
875 }
876}
877
878// Note: logically, this belongs to `hir_ty`, but we are not using it there yet.
879pub enum Access {
880 Shared,
881 Exclusive,
882 Owned,
883}
884
885impl From<hir_ty::Mutability> for Access {
886 fn from(mutability: hir_ty::Mutability) -> Access {
887 match mutability {
888 hir_ty::Mutability::Not => Access::Shared,
889 hir_ty::Mutability::Mut => Access::Exclusive,
890 }
891 }
892}
893
894#[derive(Debug)]
895pub struct Param {
896 ty: Type,
897}
898
899impl Param {
900 pub fn ty(&self) -> &Type {
901 &self.ty
902 }
903}
904
905#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
906pub struct SelfParam {
907 func: FunctionId,
908}
909
910impl SelfParam {
911 pub fn access(self, db: &dyn HirDatabase) -> Access {
912 let func_data = db.function_data(self.func);
913 func_data
914 .params
915 .first()
916 .map(|param| match *param {
917 TypeRef::Reference(.., mutability) => match mutability {
918 hir_def::type_ref::Mutability::Shared => Access::Shared,
919 hir_def::type_ref::Mutability::Mut => Access::Exclusive,
920 },
921 _ => Access::Owned,
922 })
923 .unwrap_or(Access::Owned)
924 }
925}
926
927impl HasVisibility for Function {
928 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
929 let function_data = db.function_data(self.id);
930 let visibility = &function_data.visibility;
931 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
932 }
933}
934
935#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
936pub struct Const {
937 pub(crate) id: ConstId,
938}
939
940impl Const {
941 pub fn module(self, db: &dyn HirDatabase) -> Module {
942 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
943 }
944
945 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
946 Some(self.module(db).krate())
947 }
948
949 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
950 db.const_data(self.id).name.clone()
951 }
952}
953
954impl HasVisibility for Const {
955 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
956 let function_data = db.const_data(self.id);
957 let visibility = &function_data.visibility;
958 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
959 }
960}
961
962#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
963pub struct Static {
964 pub(crate) id: StaticId,
965}
966
967impl Static {
968 pub fn module(self, db: &dyn HirDatabase) -> Module {
969 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
970 }
971
972 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
973 Some(self.module(db).krate())
974 }
975
976 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
977 db.static_data(self.id).name.clone()
978 }
979
980 pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
981 db.static_data(self.id).mutable
982 }
983}
984
985#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
986pub struct Trait {
987 pub(crate) id: TraitId,
988}
989
990impl Trait {
991 pub fn module(self, db: &dyn HirDatabase) -> Module {
992 Module { id: self.id.lookup(db.upcast()).container }
993 }
994
995 pub fn name(self, db: &dyn HirDatabase) -> Name {
996 db.trait_data(self.id).name.clone()
997 }
998
999 pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
1000 db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect()
1001 }
1002
1003 pub fn is_auto(self, db: &dyn HirDatabase) -> bool {
1004 db.trait_data(self.id).auto
1005 }
1006}
1007
1008#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1009pub struct TypeAlias {
1010 pub(crate) id: TypeAliasId,
1011}
1012
1013impl TypeAlias {
1014 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
1015 let subst = db.generic_defaults(self.id.into());
1016 subst.iter().any(|ty| &ty.value == &Ty::Unknown)
1017 }
1018
1019 pub fn module(self, db: &dyn HirDatabase) -> Module {
1020 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
1021 }
1022
1023 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
1024 Some(self.module(db).krate())
1025 }
1026
1027 pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> {
1028 db.type_alias_data(self.id).type_ref.clone()
1029 }
1030
1031 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1032 Type::from_def(db, self.id.lookup(db.upcast()).module(db.upcast()).krate(), self.id)
1033 }
1034
1035 pub fn name(self, db: &dyn HirDatabase) -> Name {
1036 db.type_alias_data(self.id).name.clone()
1037 }
1038}
1039
1040impl HasVisibility for TypeAlias {
1041 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
1042 let function_data = db.type_alias_data(self.id);
1043 let visibility = &function_data.visibility;
1044 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
1045 }
1046}
1047
1048#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1049pub struct BuiltinType {
1050 pub(crate) inner: hir_def::builtin_type::BuiltinType,
1051}
1052
1053impl BuiltinType {
1054 pub fn ty(self, db: &dyn HirDatabase, module: Module) -> Type {
1055 let resolver = module.id.resolver(db.upcast());
1056 Type::new_with_resolver(db, &resolver, Ty::builtin(self.inner))
1057 .expect("crate not present in resolver")
1058 }
1059
1060 pub fn name(self) -> Name {
1061 self.inner.as_name()
1062 }
1063}
1064
1065#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1066pub struct MacroDef {
1067 pub(crate) id: MacroDefId,
1068}
1069
1070impl MacroDef {
1071 /// FIXME: right now, this just returns the root module of the crate that
1072 /// defines this macro. The reasons for this is that macros are expanded
1073 /// early, in `hir_expand`, where modules simply do not exist yet.
1074 pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
1075 let krate = self.id.krate;
1076 let def_map = db.crate_def_map(krate);
1077 let module_id = def_map.root();
1078 Some(Module { id: def_map.module_id(module_id) })
1079 }
1080
1081 /// XXX: this parses the file
1082 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1083 self.source(db)?.value.name().map(|it| it.as_name())
1084 }
1085
1086 /// Indicate it is a proc-macro
1087 pub fn is_proc_macro(&self) -> bool {
1088 matches!(self.id.kind, MacroDefKind::ProcMacro(_))
1089 }
1090
1091 /// Indicate it is a derive macro
1092 pub fn is_derive_macro(&self) -> bool {
1093 matches!(self.id.kind, MacroDefKind::ProcMacro(_) | MacroDefKind::BuiltInDerive(_))
1094 }
1095}
1096
1097/// Invariant: `inner.as_assoc_item(db).is_some()`
1098/// We do not actively enforce this invariant.
1099#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1100pub enum AssocItem {
1101 Function(Function),
1102 Const(Const),
1103 TypeAlias(TypeAlias),
1104}
1105#[derive(Debug)]
1106pub enum AssocItemContainer {
1107 Trait(Trait),
1108 Impl(Impl),
1109}
1110pub trait AsAssocItem {
1111 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem>;
1112}
1113
1114impl AsAssocItem for Function {
1115 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1116 as_assoc_item(db, AssocItem::Function, self.id)
1117 }
1118}
1119impl AsAssocItem for Const {
1120 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1121 as_assoc_item(db, AssocItem::Const, self.id)
1122 }
1123}
1124impl AsAssocItem for TypeAlias {
1125 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1126 as_assoc_item(db, AssocItem::TypeAlias, self.id)
1127 }
1128}
1129impl AsAssocItem for ModuleDef {
1130 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1131 match self {
1132 ModuleDef::Function(it) => it.as_assoc_item(db),
1133 ModuleDef::Const(it) => it.as_assoc_item(db),
1134 ModuleDef::TypeAlias(it) => it.as_assoc_item(db),
1135 _ => None,
1136 }
1137 }
1138}
1139fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem>
1140where
1141 ID: Lookup<Data = AssocItemLoc<AST>>,
1142 DEF: From<ID>,
1143 CTOR: FnOnce(DEF) -> AssocItem,
1144 AST: ItemTreeNode,
1145{
1146 match id.lookup(db.upcast()).container {
1147 AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
1148 AssocContainerId::ModuleId(_) => None,
1149 }
1150}
1151
1152impl AssocItem {
1153 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1154 match self {
1155 AssocItem::Function(it) => Some(it.name(db)),
1156 AssocItem::Const(it) => it.name(db),
1157 AssocItem::TypeAlias(it) => Some(it.name(db)),
1158 }
1159 }
1160 pub fn module(self, db: &dyn HirDatabase) -> Module {
1161 match self {
1162 AssocItem::Function(f) => f.module(db),
1163 AssocItem::Const(c) => c.module(db),
1164 AssocItem::TypeAlias(t) => t.module(db),
1165 }
1166 }
1167 pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer {
1168 let container = match self {
1169 AssocItem::Function(it) => it.id.lookup(db.upcast()).container,
1170 AssocItem::Const(it) => it.id.lookup(db.upcast()).container,
1171 AssocItem::TypeAlias(it) => it.id.lookup(db.upcast()).container,
1172 };
1173 match container {
1174 AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()),
1175 AssocContainerId::ImplId(id) => AssocItemContainer::Impl(id.into()),
1176 AssocContainerId::ModuleId(_) => panic!("invalid AssocItem"),
1177 }
1178 }
1179
1180 pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> {
1181 match self.container(db) {
1182 AssocItemContainer::Trait(t) => Some(t),
1183 _ => None,
1184 }
1185 }
1186}
1187
1188impl HasVisibility for AssocItem {
1189 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
1190 match self {
1191 AssocItem::Function(f) => f.visibility(db),
1192 AssocItem::Const(c) => c.visibility(db),
1193 AssocItem::TypeAlias(t) => t.visibility(db),
1194 }
1195 }
1196}
1197
1198#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
1199pub enum GenericDef {
1200 Function(Function),
1201 Adt(Adt),
1202 Trait(Trait),
1203 TypeAlias(TypeAlias),
1204 Impl(Impl),
1205 // enum variants cannot have generics themselves, but their parent enums
1206 // can, and this makes some code easier to write
1207 Variant(Variant),
1208 // consts can have type parameters from their parents (i.e. associated consts of traits)
1209 Const(Const),
1210}
1211impl_from!(
1212 Function,
1213 Adt(Struct, Enum, Union),
1214 Trait,
1215 TypeAlias,
1216 Impl,
1217 Variant,
1218 Const
1219 for GenericDef
1220);
1221
1222impl GenericDef {
1223 pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> {
1224 let generics = db.generic_params(self.into());
1225 let ty_params = generics
1226 .types
1227 .iter()
1228 .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } })
1229 .map(GenericParam::TypeParam);
1230 let lt_params = generics
1231 .lifetimes
1232 .iter()
1233 .map(|(local_id, _)| LifetimeParam {
1234 id: LifetimeParamId { parent: self.into(), local_id },
1235 })
1236 .map(GenericParam::LifetimeParam);
1237 let const_params = generics
1238 .consts
1239 .iter()
1240 .map(|(local_id, _)| ConstParam { id: ConstParamId { parent: self.into(), local_id } })
1241 .map(GenericParam::ConstParam);
1242 ty_params.chain(lt_params).chain(const_params).collect()
1243 }
1244
1245 pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> {
1246 let generics = db.generic_params(self.into());
1247 generics
1248 .types
1249 .iter()
1250 .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } })
1251 .collect()
1252 }
1253}
1254
1255#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1256pub struct Local {
1257 pub(crate) parent: DefWithBodyId,
1258 pub(crate) pat_id: PatId,
1259}
1260
1261impl Local {
1262 pub fn is_param(self, db: &dyn HirDatabase) -> bool {
1263 let src = self.source(db);
1264 match src.value {
1265 Either::Left(bind_pat) => {
1266 bind_pat.syntax().ancestors().any(|it| ast::Param::can_cast(it.kind()))
1267 }
1268 Either::Right(_self_param) => true,
1269 }
1270 }
1271
1272 // FIXME: why is this an option? It shouldn't be?
1273 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1274 let body = db.body(self.parent.into());
1275 match &body[self.pat_id] {
1276 Pat::Bind { name, .. } => Some(name.clone()),
1277 _ => None,
1278 }
1279 }
1280
1281 pub fn is_self(self, db: &dyn HirDatabase) -> bool {
1282 self.name(db) == Some(name![self])
1283 }
1284
1285 pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
1286 let body = db.body(self.parent.into());
1287 matches!(&body[self.pat_id], Pat::Bind { mode: BindingAnnotation::Mutable, .. })
1288 }
1289
1290 pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
1291 self.parent.into()
1292 }
1293
1294 pub fn module(self, db: &dyn HirDatabase) -> Module {
1295 self.parent(db).module(db)
1296 }
1297
1298 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1299 let def = DefWithBodyId::from(self.parent);
1300 let infer = db.infer(def);
1301 let ty = infer[self.pat_id].clone();
1302 let krate = def.module(db.upcast()).krate();
1303 Type::new(db, krate, def, ty)
1304 }
1305
1306 pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> {
1307 let (_body, source_map) = db.body_with_source_map(self.parent.into());
1308 let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
1309 let root = src.file_syntax(db.upcast());
1310 src.map(|ast| {
1311 ast.map_left(|it| it.cast().unwrap().to_node(&root)).map_right(|it| it.to_node(&root))
1312 })
1313 }
1314}
1315
1316#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1317pub struct Label {
1318 pub(crate) parent: DefWithBodyId,
1319 pub(crate) label_id: LabelId,
1320}
1321
1322impl Label {
1323 pub fn module(self, db: &dyn HirDatabase) -> Module {
1324 self.parent(db).module(db)
1325 }
1326
1327 pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
1328 self.parent.into()
1329 }
1330
1331 pub fn name(self, db: &dyn HirDatabase) -> Name {
1332 let body = db.body(self.parent.into());
1333 body[self.label_id].name.clone()
1334 }
1335
1336 pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> {
1337 let (_body, source_map) = db.body_with_source_map(self.parent.into());
1338 let src = source_map.label_syntax(self.label_id);
1339 let root = src.file_syntax(db.upcast());
1340 src.map(|ast| ast.to_node(&root))
1341 }
1342}
1343
1344#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1345pub enum GenericParam {
1346 TypeParam(TypeParam),
1347 LifetimeParam(LifetimeParam),
1348 ConstParam(ConstParam),
1349}
1350impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam);
1351
1352impl GenericParam {
1353 pub fn module(self, db: &dyn HirDatabase) -> Module {
1354 match self {
1355 GenericParam::TypeParam(it) => it.module(db),
1356 GenericParam::LifetimeParam(it) => it.module(db),
1357 GenericParam::ConstParam(it) => it.module(db),
1358 }
1359 }
1360
1361 pub fn name(self, db: &dyn HirDatabase) -> Name {
1362 match self {
1363 GenericParam::TypeParam(it) => it.name(db),
1364 GenericParam::LifetimeParam(it) => it.name(db),
1365 GenericParam::ConstParam(it) => it.name(db),
1366 }
1367 }
1368}
1369
1370#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1371pub struct TypeParam {
1372 pub(crate) id: TypeParamId,
1373}
1374
1375impl TypeParam {
1376 pub fn name(self, db: &dyn HirDatabase) -> Name {
1377 let params = db.generic_params(self.id.parent);
1378 params.types[self.id.local_id].name.clone().unwrap_or_else(Name::missing)
1379 }
1380
1381 pub fn module(self, db: &dyn HirDatabase) -> Module {
1382 self.id.parent.module(db.upcast()).into()
1383 }
1384
1385 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1386 let resolver = self.id.parent.resolver(db.upcast());
1387 let krate = self.id.parent.module(db.upcast()).krate();
1388 let ty = Ty::Placeholder(self.id);
1389 Type::new_with_resolver_inner(db, krate, &resolver, ty)
1390 }
1391
1392 pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
1393 db.generic_predicates_for_param(self.id)
1394 .into_iter()
1395 .filter_map(|pred| match &pred.value {
1396 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1397 Some(Trait::from(trait_ref.trait_))
1398 }
1399 _ => None,
1400 })
1401 .collect()
1402 }
1403
1404 pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
1405 let params = db.generic_defaults(self.id.parent);
1406 let local_idx = hir_ty::param_idx(db, self.id)?;
1407 let resolver = self.id.parent.resolver(db.upcast());
1408 let krate = self.id.parent.module(db.upcast()).krate();
1409 let ty = params.get(local_idx)?.clone();
1410 let subst = Substs::type_params(db, self.id.parent);
1411 let ty = ty.subst(&subst.prefix(local_idx));
1412 Some(Type::new_with_resolver_inner(db, krate, &resolver, ty))
1413 }
1414}
1415
1416impl HirDisplay for TypeParam {
1417 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
1418 write!(f, "{}", self.name(f.db))?;
1419 let bounds = f.db.generic_predicates_for_param(self.id);
1420 let substs = Substs::type_params(f.db, self.id.parent);
1421 let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>();
1422 if !(predicates.is_empty() || f.omit_verbose_types()) {
1423 write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?;
1424 }
1425 Ok(())
1426 }
1427}
1428
1429#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1430pub struct LifetimeParam {
1431 pub(crate) id: LifetimeParamId,
1432}
1433
1434impl LifetimeParam {
1435 pub fn name(self, db: &dyn HirDatabase) -> Name {
1436 let params = db.generic_params(self.id.parent);
1437 params.lifetimes[self.id.local_id].name.clone()
1438 }
1439
1440 pub fn module(self, db: &dyn HirDatabase) -> Module {
1441 self.id.parent.module(db.upcast()).into()
1442 }
1443
1444 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1445 self.id.parent.into()
1446 }
1447}
1448
1449#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1450pub struct ConstParam {
1451 pub(crate) id: ConstParamId,
1452}
1453
1454impl ConstParam {
1455 pub fn name(self, db: &dyn HirDatabase) -> Name {
1456 let params = db.generic_params(self.id.parent);
1457 params.consts[self.id.local_id].name.clone()
1458 }
1459
1460 pub fn module(self, db: &dyn HirDatabase) -> Module {
1461 self.id.parent.module(db.upcast()).into()
1462 }
1463
1464 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1465 self.id.parent.into()
1466 }
1467
1468 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1469 let def = self.id.parent;
1470 let krate = def.module(db.upcast()).krate();
1471 Type::new(db, krate, def, db.const_param_ty(self.id))
1472 }
1473}
1474
1475#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1476pub struct Impl {
1477 pub(crate) id: ImplId,
1478}
1479
1480impl Impl {
1481 pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<Impl> {
1482 let inherent = db.inherent_impls_in_crate(krate.id);
1483 let trait_ = db.trait_impls_in_crate(krate.id);
1484
1485 inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect()
1486 }
1487 pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<Impl> {
1488 let impls = db.trait_impls_in_crate(krate.id);
1489 impls.for_trait(trait_.id).map(Self::from).collect()
1490 }
1491
1492 // FIXME: the return type is wrong. This should be a hir version of
1493 // `TraitRef` (ie, resolved `TypeRef`).
1494 pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> {
1495 db.impl_data(self.id).target_trait.clone()
1496 }
1497
1498 pub fn target_ty(self, db: &dyn HirDatabase) -> Type {
1499 let impl_data = db.impl_data(self.id);
1500 let resolver = self.id.resolver(db.upcast());
1501 let krate = self.id.lookup(db.upcast()).container.krate();
1502 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
1503 let ty = Ty::from_hir(&ctx, &impl_data.target_type);
1504 Type::new_with_resolver_inner(db, krate, &resolver, ty)
1505 }
1506
1507 pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
1508 db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect()
1509 }
1510
1511 pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
1512 db.impl_data(self.id).is_negative
1513 }
1514
1515 pub fn module(self, db: &dyn HirDatabase) -> Module {
1516 self.id.lookup(db.upcast()).container.into()
1517 }
1518
1519 pub fn krate(self, db: &dyn HirDatabase) -> Crate {
1520 Crate { id: self.module(db).id.krate() }
1521 }
1522
1523 pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> {
1524 let src = self.source(db)?;
1525 let item = src.file_id.is_builtin_derive(db.upcast())?;
1526 let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id);
1527
1528 // FIXME: handle `cfg_attr`
1529 let attr = item
1530 .value
1531 .attrs()
1532 .filter_map(|it| {
1533 let path = ModPath::from_src(it.path()?, &hygenic)?;
1534 if path.as_ident()?.to_string() == "derive" {
1535 Some(it)
1536 } else {
1537 None
1538 }
1539 })
1540 .last()?;
1541
1542 Some(item.with_value(attr))
1543 }
1544}
1545
1546#[derive(Clone, PartialEq, Eq, Debug)]
1547pub struct Type {
1548 krate: CrateId,
1549 ty: InEnvironment<Ty>,
1550}
1551
1552impl Type {
1553 pub(crate) fn new_with_resolver(
1554 db: &dyn HirDatabase,
1555 resolver: &Resolver,
1556 ty: Ty,
1557 ) -> Option<Type> {
1558 let krate = resolver.krate()?;
1559 Some(Type::new_with_resolver_inner(db, krate, resolver, ty))
1560 }
1561 pub(crate) fn new_with_resolver_inner(
1562 db: &dyn HirDatabase,
1563 krate: CrateId,
1564 resolver: &Resolver,
1565 ty: Ty,
1566 ) -> Type {
1567 let environment = TraitEnvironment::lower(db, &resolver);
1568 Type { krate, ty: InEnvironment { value: ty, environment } }
1569 }
1570
1571 fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type {
1572 let resolver = lexical_env.resolver(db.upcast());
1573 let environment = TraitEnvironment::lower(db, &resolver);
1574 Type { krate, ty: InEnvironment { value: ty, environment } }
1575 }
1576
1577 fn from_def(
1578 db: &dyn HirDatabase,
1579 krate: CrateId,
1580 def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>,
1581 ) -> Type {
1582 let substs = Substs::build_for_def(db, def).fill_with_unknown().build();
1583 let ty = db.ty(def.into()).subst(&substs);
1584 Type::new(db, krate, def, ty)
1585 }
1586
1587 pub fn is_unit(&self) -> bool {
1588 matches!(self.ty.value, Ty::Tuple(0, ..))
1589 }
1590 pub fn is_bool(&self) -> bool {
1591 matches!(self.ty.value, Ty::Scalar(Scalar::Bool))
1592 }
1593
1594 pub fn is_mutable_reference(&self) -> bool {
1595 matches!(self.ty.value, Ty::Ref(hir_ty::Mutability::Mut, ..))
1596 }
1597
1598 pub fn remove_ref(&self) -> Option<Type> {
1599 match &self.ty.value {
1600 Ty::Ref(.., substs) => Some(self.derived(substs[0].clone())),
1601 _ => None,
1602 }
1603 }
1604
1605 pub fn is_unknown(&self) -> bool {
1606 matches!(self.ty.value, Ty::Unknown)
1607 }
1608
1609 /// Checks that particular type `ty` implements `std::future::Future`.
1610 /// This function is used in `.await` syntax completion.
1611 pub fn impls_future(&self, db: &dyn HirDatabase) -> bool {
1612 // No special case for the type of async block, since Chalk can figure it out.
1613
1614 let krate = self.krate;
1615
1616 let std_future_trait =
1617 db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait());
1618 let std_future_trait = match std_future_trait {
1619 Some(it) => it,
1620 None => return false,
1621 };
1622
1623 let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1624 method_resolution::implements_trait(
1625 &canonical_ty,
1626 db,
1627 self.ty.environment.clone(),
1628 krate,
1629 std_future_trait,
1630 )
1631 }
1632
1633 /// Checks that particular type `ty` implements `std::ops::FnOnce`.
1634 ///
1635 /// This function can be used to check if a particular type is callable, since FnOnce is a
1636 /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce.
1637 pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool {
1638 let krate = self.krate;
1639
1640 let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) {
1641 Some(it) => it,
1642 None => return false,
1643 };
1644
1645 let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1646 method_resolution::implements_trait_unique(
1647 &canonical_ty,
1648 db,
1649 self.ty.environment.clone(),
1650 krate,
1651 fnonce_trait,
1652 )
1653 }
1654
1655 pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool {
1656 let trait_ref = hir_ty::TraitRef {
1657 trait_: trait_.id,
1658 substs: Substs::build_for_def(db, trait_.id)
1659 .push(self.ty.value.clone())
1660 .fill(args.iter().map(|t| t.ty.value.clone()))
1661 .build(),
1662 };
1663
1664 let goal = Canonical {
1665 value: hir_ty::InEnvironment::new(
1666 self.ty.environment.clone(),
1667 hir_ty::Obligation::Trait(trait_ref),
1668 ),
1669 kinds: Arc::new([]),
1670 };
1671
1672 db.trait_solve(self.krate, goal).is_some()
1673 }
1674
1675 pub fn normalize_trait_assoc_type(
1676 &self,
1677 db: &dyn HirDatabase,
1678 trait_: Trait,
1679 args: &[Type],
1680 alias: TypeAlias,
1681 ) -> Option<Type> {
1682 let subst = Substs::build_for_def(db, trait_.id)
1683 .push(self.ty.value.clone())
1684 .fill(args.iter().map(|t| t.ty.value.clone()))
1685 .build();
1686 let predicate = ProjectionPredicate {
1687 projection_ty: ProjectionTy { associated_ty: alias.id, parameters: subst },
1688 ty: Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)),
1689 };
1690 let goal = Canonical {
1691 value: InEnvironment::new(
1692 self.ty.environment.clone(),
1693 Obligation::Projection(predicate),
1694 ),
1695 kinds: Arc::new([TyVariableKind::General]),
1696 };
1697
1698 match db.trait_solve(self.krate, goal)? {
1699 Solution::Unique(SolutionVariables(subst)) => {
1700 subst.value.first().map(|ty| self.derived(ty.clone()))
1701 }
1702 Solution::Ambig(_) => None,
1703 }
1704 }
1705
1706 pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
1707 let lang_item = db.lang_item(self.krate, SmolStr::new("copy"));
1708 let copy_trait = match lang_item {
1709 Some(LangItemTarget::TraitId(it)) => it,
1710 _ => return false,
1711 };
1712 self.impls_trait(db, copy_trait.into(), &[])
1713 }
1714
1715 pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
1716 let def = match self.ty.value {
1717 Ty::FnDef(def, _) => Some(def),
1718 _ => None,
1719 };
1720
1721 let sig = self.ty.value.callable_sig(db)?;
1722 Some(Callable { ty: self.clone(), sig, def, is_bound_method: false })
1723 }
1724
1725 pub fn is_closure(&self) -> bool {
1726 matches!(&self.ty.value, Ty::Closure { .. })
1727 }
1728
1729 pub fn is_fn(&self) -> bool {
1730 matches!(&self.ty.value, Ty::FnDef(..) | Ty::Function { .. })
1731 }
1732
1733 pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
1734 let adt_id = match self.ty.value {
1735 Ty::Adt(hir_ty::AdtId(adt_id), ..) => adt_id,
1736 _ => return false,
1737 };
1738
1739 let adt = adt_id.into();
1740 match adt {
1741 Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)),
1742 _ => false,
1743 }
1744 }
1745
1746 pub fn is_raw_ptr(&self) -> bool {
1747 matches!(&self.ty.value, Ty::Raw(..))
1748 }
1749
1750 pub fn contains_unknown(&self) -> bool {
1751 return go(&self.ty.value);
1752
1753 fn go(ty: &Ty) -> bool {
1754 match ty {
1755 Ty::Unknown => true,
1756 _ => ty.substs().map_or(false, |substs| substs.iter().any(go)),
1757 }
1758 }
1759 }
1760
1761 pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> {
1762 let (variant_id, substs) = match self.ty.value {
1763 Ty::Adt(hir_ty::AdtId(AdtId::StructId(s)), ref substs) => (s.into(), substs),
1764 Ty::Adt(hir_ty::AdtId(AdtId::UnionId(u)), ref substs) => (u.into(), substs),
1765 _ => return Vec::new(),
1766 };
1767
1768 db.field_types(variant_id)
1769 .iter()
1770 .map(|(local_id, ty)| {
1771 let def = Field { parent: variant_id.into(), id: local_id };
1772 let ty = ty.clone().subst(substs);
1773 (def, self.derived(ty))
1774 })
1775 .collect()
1776 }
1777
1778 pub fn tuple_fields(&self, _db: &dyn HirDatabase) -> Vec<Type> {
1779 if let Ty::Tuple(_, substs) = &self.ty.value {
1780 substs.iter().map(|ty| self.derived(ty.clone())).collect()
1781 } else {
1782 Vec::new()
1783 }
1784 }
1785
1786 pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
1787 // There should be no inference vars in types passed here
1788 // FIXME check that?
1789 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1790 let environment = self.ty.environment.clone();
1791 let ty = InEnvironment { value: canonical, environment };
1792 autoderef(db, Some(self.krate), ty)
1793 .map(|canonical| canonical.value)
1794 .map(move |ty| self.derived(ty))
1795 }
1796
1797 // This would be nicer if it just returned an iterator, but that runs into
1798 // lifetime problems, because we need to borrow temp `CrateImplDefs`.
1799 pub fn iterate_assoc_items<T>(
1800 self,
1801 db: &dyn HirDatabase,
1802 krate: Crate,
1803 mut callback: impl FnMut(AssocItem) -> Option<T>,
1804 ) -> Option<T> {
1805 for krate in self.ty.value.def_crates(db, krate.id)? {
1806 let impls = db.inherent_impls_in_crate(krate);
1807
1808 for impl_def in impls.for_self_ty(&self.ty.value) {
1809 for &item in db.impl_data(*impl_def).items.iter() {
1810 if let Some(result) = callback(item.into()) {
1811 return Some(result);
1812 }
1813 }
1814 }
1815 }
1816 None
1817 }
1818
1819 pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ {
1820 self.ty
1821 .value
1822 .strip_references()
1823 .substs()
1824 .into_iter()
1825 .flat_map(|substs| substs.iter())
1826 .map(move |ty| self.derived(ty.clone()))
1827 }
1828
1829 pub fn iterate_method_candidates<T>(
1830 &self,
1831 db: &dyn HirDatabase,
1832 krate: Crate,
1833 traits_in_scope: &FxHashSet<TraitId>,
1834 name: Option<&Name>,
1835 mut callback: impl FnMut(&Ty, Function) -> Option<T>,
1836 ) -> Option<T> {
1837 // There should be no inference vars in types passed here
1838 // FIXME check that?
1839 // FIXME replace Unknown by bound vars here
1840 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1841
1842 let env = self.ty.environment.clone();
1843 let krate = krate.id;
1844
1845 method_resolution::iterate_method_candidates(
1846 &canonical,
1847 db,
1848 env,
1849 krate,
1850 traits_in_scope,
1851 name,
1852 method_resolution::LookupMode::MethodCall,
1853 |ty, it| match it {
1854 AssocItemId::FunctionId(f) => callback(ty, f.into()),
1855 _ => None,
1856 },
1857 )
1858 }
1859
1860 pub fn iterate_path_candidates<T>(
1861 &self,
1862 db: &dyn HirDatabase,
1863 krate: Crate,
1864 traits_in_scope: &FxHashSet<TraitId>,
1865 name: Option<&Name>,
1866 mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
1867 ) -> Option<T> {
1868 // There should be no inference vars in types passed here
1869 // FIXME check that?
1870 // FIXME replace Unknown by bound vars here
1871 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1872
1873 let env = self.ty.environment.clone();
1874 let krate = krate.id;
1875
1876 method_resolution::iterate_method_candidates(
1877 &canonical,
1878 db,
1879 env,
1880 krate,
1881 traits_in_scope,
1882 name,
1883 method_resolution::LookupMode::Path,
1884 |ty, it| callback(ty, it.into()),
1885 )
1886 }
1887
1888 pub fn as_adt(&self) -> Option<Adt> {
1889 let (adt, _subst) = self.ty.value.as_adt()?;
1890 Some(adt.into())
1891 }
1892
1893 pub fn as_dyn_trait(&self) -> Option<Trait> {
1894 self.ty.value.dyn_trait().map(Into::into)
1895 }
1896
1897 pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> {
1898 self.ty.value.impl_trait_bounds(db).map(|it| {
1899 it.into_iter()
1900 .filter_map(|pred| match pred {
1901 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1902 Some(Trait::from(trait_ref.trait_))
1903 }
1904 _ => None,
1905 })
1906 .collect()
1907 })
1908 }
1909
1910 pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> {
1911 self.ty.value.associated_type_parent_trait(db).map(Into::into)
1912 }
1913
1914 // FIXME: provide required accessors such that it becomes implementable from outside.
1915 pub fn is_equal_for_find_impls(&self, other: &Type) -> bool {
1916 let rref = other.remove_ref();
1917 self.ty.value.equals_ctor(rref.as_ref().map_or(&other.ty.value, |it| &it.ty.value))
1918 }
1919
1920 fn derived(&self, ty: Ty) -> Type {
1921 Type {
1922 krate: self.krate,
1923 ty: InEnvironment { value: ty, environment: self.ty.environment.clone() },
1924 }
1925 }
1926
1927 pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) {
1928 // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself.
1929 // We need a different order here.
1930
1931 fn walk_substs(
1932 db: &dyn HirDatabase,
1933 type_: &Type,
1934 substs: &Substs,
1935 cb: &mut impl FnMut(Type),
1936 ) {
1937 for ty in substs.iter() {
1938 walk_type(db, &type_.derived(ty.clone()), cb);
1939 }
1940 }
1941
1942 fn walk_bounds(
1943 db: &dyn HirDatabase,
1944 type_: &Type,
1945 bounds: &[GenericPredicate],
1946 cb: &mut impl FnMut(Type),
1947 ) {
1948 for pred in bounds {
1949 match pred {
1950 GenericPredicate::Implemented(trait_ref) => {
1951 cb(type_.clone());
1952 walk_substs(db, type_, &trait_ref.substs, cb);
1953 }
1954 _ => (),
1955 }
1956 }
1957 }
1958
1959 fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
1960 let ty = type_.ty.value.strip_references();
1961 match ty {
1962 Ty::Adt(..) => {
1963 cb(type_.derived(ty.clone()));
1964 }
1965 Ty::AssociatedType(..) => {
1966 if let Some(_) = ty.associated_type_parent_trait(db) {
1967 cb(type_.derived(ty.clone()));
1968 }
1969 }
1970 Ty::OpaqueType(..) => {
1971 if let Some(bounds) = ty.impl_trait_bounds(db) {
1972 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1973 }
1974 }
1975 Ty::Alias(AliasTy::Opaque(opaque_ty)) => {
1976 if let Some(bounds) = ty.impl_trait_bounds(db) {
1977 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1978 }
1979
1980 walk_substs(db, type_, &opaque_ty.parameters, cb);
1981 }
1982 Ty::Placeholder(_) => {
1983 if let Some(bounds) = ty.impl_trait_bounds(db) {
1984 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1985 }
1986 }
1987 Ty::Dyn(bounds) => {
1988 walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb);
1989 }
1990
1991 _ => {}
1992 }
1993 if let Some(substs) = ty.substs() {
1994 walk_substs(db, type_, &substs, cb);
1995 }
1996 }
1997
1998 walk_type(db, self, &mut cb);
1999 }
2000}
2001
2002impl HirDisplay for Type {
2003 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
2004 self.ty.value.hir_fmt(f)
2005 }
2006}
2007
2008// FIXME: closures
2009#[derive(Debug)]
2010pub struct Callable {
2011 ty: Type,
2012 sig: CallableSig,
2013 def: Option<CallableDefId>,
2014 pub(crate) is_bound_method: bool,
2015}
2016
2017pub enum CallableKind {
2018 Function(Function),
2019 TupleStruct(Struct),
2020 TupleEnumVariant(Variant),
2021 Closure,
2022}
2023
2024impl Callable {
2025 pub fn kind(&self) -> CallableKind {
2026 match self.def {
2027 Some(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()),
2028 Some(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()),
2029 Some(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()),
2030 None => CallableKind::Closure,
2031 }
2032 }
2033 pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {
2034 let func = match self.def {
2035 Some(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
2036 _ => return None,
2037 };
2038 let src = func.lookup(db.upcast()).source(db.upcast());
2039 let param_list = src.value.param_list()?;
2040 param_list.self_param()
2041 }
2042 pub fn n_params(&self) -> usize {
2043 self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
2044 }
2045 pub fn params(
2046 &self,
2047 db: &dyn HirDatabase,
2048 ) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> {
2049 let types = self
2050 .sig
2051 .params()
2052 .iter()
2053 .skip(if self.is_bound_method { 1 } else { 0 })
2054 .map(|ty| self.ty.derived(ty.clone()));
2055 let patterns = match self.def {
2056 Some(CallableDefId::FunctionId(func)) => {
2057 let src = func.lookup(db.upcast()).source(db.upcast());
2058 src.value.param_list().map(|param_list| {
2059 param_list
2060 .self_param()
2061 .map(|it| Some(Either::Left(it)))
2062 .filter(|_| !self.is_bound_method)
2063 .into_iter()
2064 .chain(param_list.params().map(|it| it.pat().map(Either::Right)))
2065 })
2066 }
2067 _ => None,
2068 };
2069 patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect()
2070 }
2071 pub fn return_type(&self) -> Type {
2072 self.ty.derived(self.sig.ret().clone())
2073 }
2074}
2075
2076/// For IDE only
2077#[derive(Debug, PartialEq, Eq, Hash)]
2078pub enum ScopeDef {
2079 ModuleDef(ModuleDef),
2080 MacroDef(MacroDef),
2081 GenericParam(GenericParam),
2082 ImplSelfType(Impl),
2083 AdtSelfType(Adt),
2084 Local(Local),
2085 Unknown,
2086}
2087
2088impl ScopeDef {
2089 pub fn all_items(def: PerNs) -> ArrayVec<[Self; 3]> {
2090 let mut items = ArrayVec::new();
2091
2092 match (def.take_types(), def.take_values()) {
2093 (Some(m1), None) => items.push(ScopeDef::ModuleDef(m1.into())),
2094 (None, Some(m2)) => items.push(ScopeDef::ModuleDef(m2.into())),
2095 (Some(m1), Some(m2)) => {
2096 // Some items, like unit structs and enum variants, are
2097 // returned as both a type and a value. Here we want
2098 // to de-duplicate them.
2099 if m1 != m2 {
2100 items.push(ScopeDef::ModuleDef(m1.into()));
2101 items.push(ScopeDef::ModuleDef(m2.into()));
2102 } else {
2103 items.push(ScopeDef::ModuleDef(m1.into()));
2104 }
2105 }
2106 (None, None) => {}
2107 };
2108
2109 if let Some(macro_def_id) = def.take_macros() {
2110 items.push(ScopeDef::MacroDef(macro_def_id.into()));
2111 }
2112
2113 if items.is_empty() {
2114 items.push(ScopeDef::Unknown);
2115 }
2116
2117 items
2118 }
2119}
2120
2121impl From<ItemInNs> for ScopeDef {
2122 fn from(item: ItemInNs) -> Self {
2123 match item {
2124 ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
2125 ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
2126 ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
2127 }
2128 }
2129}
2130
2131pub trait HasVisibility {
2132 fn visibility(&self, db: &dyn HirDatabase) -> Visibility;
2133 fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool {
2134 let vis = self.visibility(db);
2135 vis.is_visible_from(db.upcast(), module.id)
2136 }
2137}
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 144851f83..945638cc5 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -20,12 +20,11 @@ use syntax::{
20}; 20};
21 21
22use crate::{ 22use crate::{
23 code_model::Access,
24 db::HirDatabase, 23 db::HirDatabase,
25 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, 24 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
26 source_analyzer::{resolve_hir_path, SourceAnalyzer}, 25 source_analyzer::{resolve_hir_path, SourceAnalyzer},
27 AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile, Label, 26 Access, AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile,
28 LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, 27 Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type,
29 TypeAlias, TypeParam, VariantDef, 28 TypeAlias, TypeParam, VariantDef,
30}; 29};
31 30
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 64ce4add1..d546512cb 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -28,9 +28,8 @@ use syntax::{
28}; 28};
29 29
30use crate::{ 30use crate::{
31 code_model::BuiltinType, db::HirDatabase, semantics::PathResolution, Adt, Const, Field, 31 db::HirDatabase, semantics::PathResolution, Adt, BuiltinType, Const, Field, Function, Local,
32 Function, Local, MacroDef, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, 32 MacroDef, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Variant,
33 Variant,
34}; 33};
35use base_db::CrateId; 34use base_db::CrateId;
36 35
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml
index 535221294..c2d99280f 100644
--- a/crates/hir_def/Cargo.toml
+++ b/crates/hir_def/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13log = "0.4.8" 14log = "0.4.8"
14once_cell = "1.3.1" 15once_cell = "1.3.1"
15rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
@@ -27,10 +28,10 @@ base_db = { path = "../base_db", version = "0.0.0" }
27syntax = { path = "../syntax", version = "0.0.0" } 28syntax = { path = "../syntax", version = "0.0.0" }
28profile = { path = "../profile", version = "0.0.0" } 29profile = { path = "../profile", version = "0.0.0" }
29hir_expand = { path = "../hir_expand", version = "0.0.0" } 30hir_expand = { path = "../hir_expand", version = "0.0.0" }
30test_utils = { path = "../test_utils", version = "0.0.0" }
31mbe = { path = "../mbe", version = "0.0.0" } 31mbe = { path = "../mbe", version = "0.0.0" }
32cfg = { path = "../cfg", version = "0.0.0" } 32cfg = { path = "../cfg", version = "0.0.0" }
33tt = { path = "../tt", version = "0.0.0" } 33tt = { path = "../tt", version = "0.0.0" }
34 34
35[dev-dependencies] 35[dev-dependencies]
36test_utils = { path = "../test_utils" }
36expect-test = "1.1" 37expect-test = "1.1"
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs
index ed36c3109..efbde17d8 100644
--- a/crates/hir_def/src/adt.rs
+++ b/crates/hir_def/src/adt.rs
@@ -21,8 +21,7 @@ use crate::{
21 trace::Trace, 21 trace::Trace,
22 type_ref::TypeRef, 22 type_ref::TypeRef,
23 visibility::RawVisibility, 23 visibility::RawVisibility,
24 EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, 24 EnumId, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, VariantId,
25 VariantId,
26}; 25};
27use cfg::CfgOptions; 26use cfg::CfgOptions;
28 27
@@ -92,10 +91,10 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
92impl StructData { 91impl StructData {
93 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { 92 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
94 let loc = id.lookup(db); 93 let loc = id.lookup(db);
95 let krate = loc.container.module(db).krate; 94 let krate = loc.container.krate;
96 let item_tree = db.item_tree(loc.id.file_id); 95 let item_tree = db.item_tree(loc.id.file_id);
97 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); 96 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
98 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); 97 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
99 98
100 let strukt = &item_tree[loc.id.value]; 99 let strukt = &item_tree[loc.id.value];
101 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None); 100 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None);
@@ -107,10 +106,10 @@ impl StructData {
107 } 106 }
108 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { 107 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
109 let loc = id.lookup(db); 108 let loc = id.lookup(db);
110 let krate = loc.container.module(db).krate; 109 let krate = loc.container.krate;
111 let item_tree = db.item_tree(loc.id.file_id); 110 let item_tree = db.item_tree(loc.id.file_id);
112 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); 111 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
113 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); 112 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
114 113
115 let union = &item_tree[loc.id.value]; 114 let union = &item_tree[loc.id.value];
116 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None); 115 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None);
@@ -126,7 +125,7 @@ impl StructData {
126impl EnumData { 125impl EnumData {
127 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { 126 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
128 let loc = e.lookup(db); 127 let loc = e.lookup(db);
129 let krate = loc.container.module(db).krate; 128 let krate = loc.container.krate;
130 let item_tree = db.item_tree(loc.id.file_id); 129 let item_tree = db.item_tree(loc.id.file_id);
131 let cfg_options = db.crate_graph()[krate].cfg_options.clone(); 130 let cfg_options = db.crate_graph()[krate].cfg_options.clone();
132 131
@@ -168,7 +167,7 @@ impl HasChildSource<LocalEnumVariantId> for EnumId {
168 ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> { 167 ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> {
169 let src = self.lookup(db).source(db); 168 let src = self.lookup(db).source(db);
170 let mut trace = Trace::new_for_map(); 169 let mut trace = Trace::new_for_map();
171 lower_enum(db, &mut trace, &src, self.lookup(db).container.module(db)); 170 lower_enum(db, &mut trace, &src, self.lookup(db).container);
172 src.with_value(trace.into_map()) 171 src.with_value(trace.into_map())
173 } 172 }
174} 173}
@@ -238,10 +237,10 @@ impl HasChildSource<LocalFieldId> for VariantId {
238 // I don't really like the fact that we call into parent source 237 // I don't really like the fact that we call into parent source
239 // here, this might add to more queries then necessary. 238 // here, this might add to more queries then necessary.
240 let src = it.parent.child_source(db); 239 let src = it.parent.child_source(db);
241 (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container.module(db)) 240 (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container)
242 } 241 }
243 VariantId::StructId(it) => { 242 VariantId::StructId(it) => {
244 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container.module(db)) 243 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container)
245 } 244 }
246 VariantId::UnionId(it) => ( 245 VariantId::UnionId(it) => (
247 it.lookup(db).source(db).map(|it| { 246 it.lookup(db).source(db).map(|it| {
@@ -249,7 +248,7 @@ impl HasChildSource<LocalFieldId> for VariantId {
249 .map(ast::StructKind::Record) 248 .map(ast::StructKind::Record)
250 .unwrap_or(ast::StructKind::Unit) 249 .unwrap_or(ast::StructKind::Unit)
251 }), 250 }),
252 it.lookup(db).container.module(db), 251 it.lookup(db).container,
253 ), 252 ),
254 }; 253 };
255 let mut expander = CfgExpander::new(db, src.file_id, module_id.krate); 254 let mut expander = CfgExpander::new(db, src.file_id, module_id.krate);
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index fe4c3fa28..97cdbbb9e 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -13,7 +13,6 @@ use syntax::{
13 ast::{self, AstNode, AttrsOwner}, 13 ast::{self, AstNode, AttrsOwner},
14 match_ast, AstToken, SmolStr, SyntaxNode, 14 match_ast, AstToken, SmolStr, SyntaxNode,
15}; 15};
16use test_utils::mark;
17use tt::Subtree; 16use tt::Subtree;
18 17
19use crate::{ 18use crate::{
@@ -177,7 +176,7 @@ impl RawAttrs {
177 if cfg_options.check(&cfg) == Some(false) { 176 if cfg_options.check(&cfg) == Some(false) {
178 None 177 None
179 } else { 178 } else {
180 mark::hit!(cfg_attr_active); 179 cov_mark::hit!(cfg_attr_active);
181 180
182 let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?; 181 let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?;
183 let hygiene = Hygiene::new_unhygienic(); // FIXME 182 let hygiene = Hygiene::new_unhygienic(); // FIXME
@@ -268,7 +267,7 @@ impl Attrs {
268 db: &dyn DefDatabase, 267 db: &dyn DefDatabase,
269 e: EnumId, 268 e: EnumId,
270 ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> { 269 ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> {
271 let krate = e.lookup(db).container.module(db).krate; 270 let krate = e.lookup(db).container.krate;
272 let src = e.child_source(db); 271 let src = e.child_source(db);
273 let mut res = ArenaMap::default(); 272 let mut res = ArenaMap::default();
274 273
@@ -367,7 +366,7 @@ fn inner_attributes(
367 // Excerpt from the reference: 366 // Excerpt from the reference:
368 // Block expressions accept outer and inner attributes, but only when they are the outer 367 // Block expressions accept outer and inner attributes, but only when they are the outer
369 // expression of an expression statement or the final expression of another block expression. 368 // expression of an expression statement or the final expression of another block expression.
370 ast::BlockExpr(it) => return None, 369 ast::BlockExpr(_it) => return None,
371 _ => return None, 370 _ => return None,
372 } 371 }
373 }; 372 };
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index ff4b4a0cf..19c4eb521 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -20,7 +20,6 @@ use la_arena::{Arena, ArenaMap};
20use profile::Count; 20use profile::Count;
21use rustc_hash::FxHashMap; 21use rustc_hash::FxHashMap;
22use syntax::{ast, AstNode, AstPtr}; 22use syntax::{ast, AstNode, AstPtr};
23use test_utils::mark;
24 23
25pub(crate) use lower::LowerCtx; 24pub(crate) use lower::LowerCtx;
26 25
@@ -29,11 +28,10 @@ use crate::{
29 db::DefDatabase, 28 db::DefDatabase,
30 expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, 29 expr::{Expr, ExprId, Label, LabelId, Pat, PatId},
31 item_scope::BuiltinShadowMode, 30 item_scope::BuiltinShadowMode,
32 item_scope::ItemScope,
33 nameres::DefMap, 31 nameres::DefMap,
34 path::{ModPath, Path}, 32 path::{ModPath, Path},
35 src::HasSource, 33 src::HasSource,
36 AsMacroCall, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId, 34 AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId,
37}; 35};
38 36
39/// A subset of Expander that only deals with cfg attributes. We only need it to 37/// A subset of Expander that only deals with cfg attributes. We only need it to
@@ -87,11 +85,11 @@ impl Expander {
87 module: ModuleId, 85 module: ModuleId,
88 ) -> Expander { 86 ) -> Expander {
89 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); 87 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
90 let crate_def_map = module.def_map(db); 88 let def_map = module.def_map(db);
91 let ast_id_map = db.ast_id_map(current_file_id); 89 let ast_id_map = db.ast_id_map(current_file_id);
92 Expander { 90 Expander {
93 cfg_expander, 91 cfg_expander,
94 def_map: crate_def_map, 92 def_map,
95 current_file_id, 93 current_file_id,
96 ast_id_map, 94 ast_id_map,
97 module: module.local_id, 95 module: module.local_id,
@@ -105,7 +103,7 @@ impl Expander {
105 macro_call: ast::MacroCall, 103 macro_call: ast::MacroCall,
106 ) -> ExpandResult<Option<(Mark, T)>> { 104 ) -> ExpandResult<Option<(Mark, T)>> {
107 if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT { 105 if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT {
108 mark::hit!(your_stack_belongs_to_me); 106 cov_mark::hit!(your_stack_belongs_to_me);
109 return ExpandResult::str_err("reached recursion limit during macro expansion".into()); 107 return ExpandResult::str_err("reached recursion limit during macro expansion".into());
110 } 108 }
111 109
@@ -227,7 +225,8 @@ pub struct Body {
227 pub params: Vec<PatId>, 225 pub params: Vec<PatId>,
228 /// The `ExprId` of the actual body expression. 226 /// The `ExprId` of the actual body expression.
229 pub body_expr: ExprId, 227 pub body_expr: ExprId,
230 pub item_scope: ItemScope, 228 /// Block expressions in this body that may contain inner items.
229 pub block_scopes: Vec<BlockId>,
231 _c: Count<Self>, 230 _c: Count<Self>,
232} 231}
233 232
@@ -296,7 +295,7 @@ impl Body {
296 } 295 }
297 }; 296 };
298 let expander = Expander::new(db, file_id, module); 297 let expander = Expander::new(db, file_id, module);
299 let (body, source_map) = Body::new(db, def, expander, params, body); 298 let (body, source_map) = Body::new(db, expander, params, body);
300 (Arc::new(body), Arc::new(source_map)) 299 (Arc::new(body), Arc::new(source_map))
301 } 300 }
302 301
@@ -306,12 +305,11 @@ impl Body {
306 305
307 fn new( 306 fn new(
308 db: &dyn DefDatabase, 307 db: &dyn DefDatabase,
309 def: DefWithBodyId,
310 expander: Expander, 308 expander: Expander,
311 params: Option<ast::ParamList>, 309 params: Option<ast::ParamList>,
312 body: Option<ast::Expr>, 310 body: Option<ast::Expr>,
313 ) -> (Body, BodySourceMap) { 311 ) -> (Body, BodySourceMap) {
314 lower::lower(db, def, expander, params, body) 312 lower::lower(db, expander, params, body)
315 } 313 }
316} 314}
317 315
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 40beb2f7a..4d79ab72c 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -1,13 +1,13 @@
1//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` 1//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
2//! representation. 2//! representation.
3 3
4use std::{any::type_name, mem, sync::Arc}; 4use std::{mem, sync::Arc};
5 5
6use either::Either; 6use either::Either;
7use hir_expand::{ 7use hir_expand::{
8 hygiene::Hygiene, 8 hygiene::Hygiene,
9 name::{name, AsName, Name}, 9 name::{name, AsName, Name},
10 ExpandError, HirFileId, MacroDefId, MacroDefKind, 10 ExpandError, HirFileId,
11}; 11};
12use la_arena::Arena; 12use la_arena::Arena;
13use profile::Count; 13use profile::Count;
@@ -19,7 +19,6 @@ use syntax::{
19 }, 19 },
20 AstNode, AstPtr, SyntaxNodePtr, 20 AstNode, AstPtr, SyntaxNodePtr,
21}; 21};
22use test_utils::mark;
23 22
24use crate::{ 23use crate::{
25 adt::StructKind, 24 adt::StructKind,
@@ -33,11 +32,10 @@ use crate::{
33 Statement, 32 Statement,
34 }, 33 },
35 item_scope::BuiltinShadowMode, 34 item_scope::BuiltinShadowMode,
36 item_tree::{ItemTree, ItemTreeId, ItemTreeNode}, 35 item_tree::ItemTree,
37 path::{GenericArgs, Path}, 36 path::{GenericArgs, Path},
38 type_ref::{Mutability, Rawness, TypeRef}, 37 type_ref::{Mutability, Rawness, TypeRef},
39 AdtId, BlockLoc, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, 38 AdtId, BlockLoc, ModuleDefId,
40 ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
41}; 39};
42 40
43use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; 41use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
@@ -61,7 +59,6 @@ impl LowerCtx {
61 59
62pub(super) fn lower( 60pub(super) fn lower(
63 db: &dyn DefDatabase, 61 db: &dyn DefDatabase,
64 def: DefWithBodyId,
65 expander: Expander, 62 expander: Expander,
66 params: Option<ast::ParamList>, 63 params: Option<ast::ParamList>,
67 body: Option<ast::Expr>, 64 body: Option<ast::Expr>,
@@ -69,7 +66,6 @@ pub(super) fn lower(
69 let item_tree = db.item_tree(expander.current_file_id); 66 let item_tree = db.item_tree(expander.current_file_id);
70 ExprCollector { 67 ExprCollector {
71 db, 68 db,
72 def,
73 source_map: BodySourceMap::default(), 69 source_map: BodySourceMap::default(),
74 body: Body { 70 body: Body {
75 exprs: Arena::default(), 71 exprs: Arena::default(),
@@ -77,7 +73,7 @@ pub(super) fn lower(
77 labels: Arena::default(), 73 labels: Arena::default(),
78 params: Vec::new(), 74 params: Vec::new(),
79 body_expr: dummy_expr_id(), 75 body_expr: dummy_expr_id(),
80 item_scope: Default::default(), 76 block_scopes: Vec::new(),
81 _c: Count::new(), 77 _c: Count::new(),
82 }, 78 },
83 item_trees: { 79 item_trees: {
@@ -92,7 +88,6 @@ pub(super) fn lower(
92 88
93struct ExprCollector<'a> { 89struct ExprCollector<'a> {
94 db: &'a dyn DefDatabase, 90 db: &'a dyn DefDatabase,
95 def: DefWithBodyId,
96 expander: Expander, 91 expander: Expander,
97 body: Body, 92 body: Body,
98 source_map: BodySourceMap, 93 source_map: BodySourceMap,
@@ -286,7 +281,7 @@ impl ExprCollector<'_> {
286 None => self.collect_expr_opt(condition.expr()), 281 None => self.collect_expr_opt(condition.expr()),
287 // if let -- desugar to match 282 // if let -- desugar to match
288 Some(pat) => { 283 Some(pat) => {
289 mark::hit!(infer_resolve_while_let); 284 cov_mark::hit!(infer_resolve_while_let);
290 let pat = self.collect_pat(pat); 285 let pat = self.collect_pat(pat);
291 let match_expr = self.collect_expr_opt(condition.expr()); 286 let match_expr = self.collect_expr_opt(condition.expr());
292 let placeholder_pat = self.missing_pat(); 287 let placeholder_pat = self.missing_pat();
@@ -606,32 +601,6 @@ impl ExprCollector<'_> {
606 } 601 }
607 } 602 }
608 603
609 fn find_inner_item<N: ItemTreeNode>(&self, ast: &N::Source) -> Option<ItemTreeId<N>> {
610 let id = self.expander.ast_id(ast);
611 let tree = &self.item_trees[&id.file_id];
612
613 // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes
614
615 // Root file (non-macro).
616 let item_tree_id = tree
617 .all_inner_items()
618 .chain(tree.top_level_items().iter().copied())
619 .filter_map(|mod_item| mod_item.downcast::<N>())
620 .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value.upcast())
621 .or_else(|| {
622 log::debug!(
623 "couldn't find inner {} item for {:?} (AST: `{}` - {:?})",
624 type_name::<N>(),
625 id,
626 ast.syntax(),
627 ast.syntax(),
628 );
629 None
630 })?;
631
632 Some(ItemTreeId::new(id.file_id, item_tree_id))
633 }
634
635 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { 604 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
636 if let Some(expr) = expr { 605 if let Some(expr) = expr {
637 self.collect_expr(expr) 606 self.collect_expr(expr)
@@ -663,7 +632,6 @@ impl ExprCollector<'_> {
663 match expansion { 632 match expansion {
664 Some(expansion) => { 633 Some(expansion) => {
665 let statements: ast::MacroStmts = expansion; 634 let statements: ast::MacroStmts = expansion;
666 this.collect_stmts_items(statements.statements());
667 635
668 statements.statements().for_each(|stmt| { 636 statements.statements().for_each(|stmt| {
669 if let Some(mut r) = this.collect_stmt(stmt) { 637 if let Some(mut r) = this.collect_stmt(stmt) {
@@ -701,6 +669,8 @@ impl ExprCollector<'_> {
701 let block_loc = 669 let block_loc =
702 BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) }; 670 BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) };
703 let block_id = self.db.intern_block(block_loc); 671 let block_id = self.db.intern_block(block_loc);
672 self.body.block_scopes.push(block_id);
673
704 let opt_def_map = self.db.block_def_map(block_id); 674 let opt_def_map = self.db.block_def_map(block_id);
705 let has_def_map = opt_def_map.is_some(); 675 let has_def_map = opt_def_map.is_some();
706 let def_map = opt_def_map.unwrap_or_else(|| self.expander.def_map.clone()); 676 let def_map = opt_def_map.unwrap_or_else(|| self.expander.def_map.clone());
@@ -708,7 +678,6 @@ impl ExprCollector<'_> {
708 let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); 678 let prev_def_map = mem::replace(&mut self.expander.def_map, def_map);
709 let prev_local_module = mem::replace(&mut self.expander.module, module); 679 let prev_local_module = mem::replace(&mut self.expander.module, module);
710 680
711 self.collect_stmts_items(block.statements());
712 let statements = 681 let statements =
713 block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); 682 block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect();
714 let tail = block.tail_expr().map(|e| self.collect_expr(e)); 683 let tail = block.tail_expr().map(|e| self.collect_expr(e));
@@ -723,108 +692,6 @@ impl ExprCollector<'_> {
723 expr_id 692 expr_id
724 } 693 }
725 694
726 fn collect_stmts_items(&mut self, stmts: ast::AstChildren<ast::Stmt>) {
727 let container = ContainerId::DefWithBodyId(self.def);
728
729 let items = stmts
730 .filter_map(|stmt| match stmt {
731 ast::Stmt::Item(it) => Some(it),
732 ast::Stmt::LetStmt(_) | ast::Stmt::ExprStmt(_) => None,
733 })
734 .filter_map(|item| {
735 let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
736 ast::Item::Fn(def) => {
737 let id = self.find_inner_item(&def)?;
738 (
739 FunctionLoc { container: container.into(), id }.intern(self.db).into(),
740 def.name(),
741 )
742 }
743 ast::Item::TypeAlias(def) => {
744 let id = self.find_inner_item(&def)?;
745 (
746 TypeAliasLoc { container: container.into(), id }.intern(self.db).into(),
747 def.name(),
748 )
749 }
750 ast::Item::Const(def) => {
751 let id = self.find_inner_item(&def)?;
752 (
753 ConstLoc { container: container.into(), id }.intern(self.db).into(),
754 def.name(),
755 )
756 }
757 ast::Item::Static(def) => {
758 let id = self.find_inner_item(&def)?;
759 (StaticLoc { container, id }.intern(self.db).into(), def.name())
760 }
761 ast::Item::Struct(def) => {
762 let id = self.find_inner_item(&def)?;
763 (StructLoc { container, id }.intern(self.db).into(), def.name())
764 }
765 ast::Item::Enum(def) => {
766 let id = self.find_inner_item(&def)?;
767 (EnumLoc { container, id }.intern(self.db).into(), def.name())
768 }
769 ast::Item::Union(def) => {
770 let id = self.find_inner_item(&def)?;
771 (UnionLoc { container, id }.intern(self.db).into(), def.name())
772 }
773 ast::Item::Trait(def) => {
774 let id = self.find_inner_item(&def)?;
775 (TraitLoc { container, id }.intern(self.db).into(), def.name())
776 }
777 ast::Item::ExternBlock(_) => return None, // FIXME: collect from extern blocks
778 ast::Item::Impl(_)
779 | ast::Item::Use(_)
780 | ast::Item::ExternCrate(_)
781 | ast::Item::Module(_)
782 | ast::Item::MacroCall(_) => return None,
783 ast::Item::MacroRules(def) => {
784 return Some(Either::Right(ast::Macro::from(def)));
785 }
786 ast::Item::MacroDef(def) => {
787 return Some(Either::Right(ast::Macro::from(def)));
788 }
789 };
790
791 Some(Either::Left((def, name)))
792 })
793 .collect::<Vec<_>>();
794
795 for either in items {
796 match either {
797 Either::Left((def, name)) => {
798 self.body.item_scope.define_def(def);
799 if let Some(name) = name {
800 let vis = crate::visibility::Visibility::Public; // FIXME determine correctly
801 let has_constructor = match def {
802 ModuleDefId::AdtId(AdtId::StructId(s)) => {
803 self.db.struct_data(s).variant_data.kind() != StructKind::Record
804 }
805 _ => true,
806 };
807 self.body.item_scope.push_res(
808 name.as_name(),
809 crate::per_ns::PerNs::from_def(def, vis, has_constructor),
810 );
811 }
812 }
813 Either::Right(e) => {
814 let mac = MacroDefId {
815 krate: self.expander.def_map.krate(),
816 ast_id: Some(self.expander.ast_id(&e)),
817 kind: MacroDefKind::Declarative,
818 local_inner: false,
819 };
820 if let Some(name) = e.name() {
821 self.body.item_scope.define_legacy_macro(name.as_name(), mac);
822 }
823 }
824 }
825 }
826 }
827
828 fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId { 695 fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId {
829 if let Some(block) = expr { 696 if let Some(block) = expr {
830 self.collect_block(block) 697 self.collect_block(block)
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs
index 210b4a617..1bbb54fc6 100644
--- a/crates/hir_def/src/body/scope.rs
+++ b/crates/hir_def/src/body/scope.rs
@@ -186,7 +186,7 @@ mod tests {
186 use base_db::{fixture::WithFixture, FileId, SourceDatabase}; 186 use base_db::{fixture::WithFixture, FileId, SourceDatabase};
187 use hir_expand::{name::AsName, InFile}; 187 use hir_expand::{name::AsName, InFile};
188 use syntax::{algo::find_node_at_offset, ast, AstNode}; 188 use syntax::{algo::find_node_at_offset, ast, AstNode};
189 use test_utils::{assert_eq_text, extract_offset, mark}; 189 use test_utils::{assert_eq_text, extract_offset};
190 190
191 use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId}; 191 use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId};
192 192
@@ -454,7 +454,7 @@ fn foo() {
454 454
455 #[test] 455 #[test]
456 fn while_let_desugaring() { 456 fn while_let_desugaring() {
457 mark::check!(infer_resolve_while_let); 457 cov_mark::check!(infer_resolve_while_let);
458 do_check_local_name( 458 do_check_local_name(
459 r#" 459 r#"
460fn test() { 460fn test() {
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index bb43569d7..991a32b15 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -2,7 +2,6 @@ mod block;
2 2
3use base_db::{fixture::WithFixture, SourceDatabase}; 3use base_db::{fixture::WithFixture, SourceDatabase};
4use expect_test::Expect; 4use expect_test::Expect;
5use test_utils::mark;
6 5
7use crate::{test_db::TestDB, ModuleDefId}; 6use crate::{test_db::TestDB, ModuleDefId};
8 7
@@ -48,7 +47,7 @@ fn check_at(ra_fixture: &str, expect: Expect) {
48 47
49#[test] 48#[test]
50fn your_stack_belongs_to_me() { 49fn your_stack_belongs_to_me() {
51 mark::check!(your_stack_belongs_to_me); 50 cov_mark::check!(your_stack_belongs_to_me);
52 lower( 51 lower(
53 " 52 "
54macro_rules! n_nuple { 53macro_rules! n_nuple {
diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs
index 8bca72a17..3b6ba4cde 100644
--- a/crates/hir_def/src/body/tests/block.rs
+++ b/crates/hir_def/src/body/tests/block.rs
@@ -165,16 +165,16 @@ fn macro_resolve() {
165 check_at( 165 check_at(
166 r#" 166 r#"
167//- /lib.rs crate:lib deps:core 167//- /lib.rs crate:lib deps:core
168use core::mark; 168use core::cov_mark;
169 169
170fn f() { 170fn f() {
171 fn nested() { 171 fn nested() {
172 mark::hit!(Hit); 172 cov_mark::hit!(Hit);
173 $0 173 $0
174 } 174 }
175} 175}
176//- /core.rs crate:core 176//- /core.rs crate:core
177pub mod mark { 177pub mod cov_mark {
178 #[macro_export] 178 #[macro_export]
179 macro_rules! _hit { 179 macro_rules! _hit {
180 ($name:ident) => { 180 ($name:ident) => {
@@ -193,8 +193,8 @@ pub mod mark {
193 nested: v 193 nested: v
194 194
195 crate 195 crate
196 cov_mark: t
196 f: v 197 f: v
197 mark: t
198 "#]], 198 "#]],
199 ); 199 );
200} 200}
@@ -264,7 +264,7 @@ fn main() {
264fn underscore_import() { 264fn underscore_import() {
265 // This used to panic, because the default (private) visibility inside block expressions would 265 // This used to panic, because the default (private) visibility inside block expressions would
266 // point into the containing `DefMap`, which visibilities should never be able to do. 266 // point into the containing `DefMap`, which visibilities should never be able to do.
267 mark::check!(adjust_vis_in_block_def_map); 267 cov_mark::check!(adjust_vis_in_block_def_map);
268 check_at( 268 check_at(
269 r#" 269 r#"
270mod m { 270mod m {
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs
index 65d85c86a..2a331dcaf 100644
--- a/crates/hir_def/src/child_by_source.rs
+++ b/crates/hir_def/src/child_by_source.rs
@@ -17,13 +17,16 @@ use crate::{
17}; 17};
18 18
19pub trait ChildBySource { 19pub trait ChildBySource {
20 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap;
21}
22
23impl ChildBySource for TraitId {
24 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 20 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
25 let mut res = DynMap::default(); 21 let mut res = DynMap::default();
22 self.child_by_source_to(db, &mut res);
23 res
24 }
25 fn child_by_source_to(&self, db: &dyn DefDatabase, map: &mut DynMap);
26}
26 27
28impl ChildBySource for TraitId {
29 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
27 let data = db.trait_data(*self); 30 let data = db.trait_data(*self);
28 for (_name, item) in data.items.iter() { 31 for (_name, item) in data.items.iter() {
29 match *item { 32 match *item {
@@ -41,15 +44,11 @@ impl ChildBySource for TraitId {
41 } 44 }
42 } 45 }
43 } 46 }
44
45 res
46 } 47 }
47} 48}
48 49
49impl ChildBySource for ImplId { 50impl ChildBySource for ImplId {
50 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 51 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
51 let mut res = DynMap::default();
52
53 let data = db.impl_data(*self); 52 let data = db.impl_data(*self);
54 for &item in data.items.iter() { 53 for &item in data.items.iter() {
55 match item { 54 match item {
@@ -67,25 +66,21 @@ impl ChildBySource for ImplId {
67 } 66 }
68 } 67 }
69 } 68 }
70
71 res
72 } 69 }
73} 70}
74 71
75impl ChildBySource for ModuleId { 72impl ChildBySource for ModuleId {
76 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 73 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
77 let crate_def_map = self.def_map(db); 74 let def_map = self.def_map(db);
78 let module_data = &crate_def_map[self.local_id]; 75 let module_data = &def_map[self.local_id];
79 module_data.scope.child_by_source(db) 76 module_data.scope.child_by_source_to(db, res);
80 } 77 }
81} 78}
82 79
83impl ChildBySource for ItemScope { 80impl ChildBySource for ItemScope {
84 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 81 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
85 let mut res = DynMap::default(); 82 self.declarations().for_each(|item| add_module_def(db, res, item));
86 self.declarations().for_each(|item| add_module_def(db, &mut res, item)); 83 self.impls().for_each(|imp| add_impl(db, res, imp));
87 self.impls().for_each(|imp| add_impl(db, &mut res, imp));
88 return res;
89 84
90 fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { 85 fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) {
91 match item { 86 match item {
@@ -134,9 +129,7 @@ impl ChildBySource for ItemScope {
134} 129}
135 130
136impl ChildBySource for VariantId { 131impl ChildBySource for VariantId {
137 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 132 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
138 let mut res = DynMap::default();
139
140 let arena_map = self.child_source(db); 133 let arena_map = self.child_source(db);
141 let arena_map = arena_map.as_ref(); 134 let arena_map = arena_map.as_ref();
142 for (local_id, source) in arena_map.value.iter() { 135 for (local_id, source) in arena_map.value.iter() {
@@ -150,28 +143,27 @@ impl ChildBySource for VariantId {
150 } 143 }
151 } 144 }
152 } 145 }
153 res
154 } 146 }
155} 147}
156 148
157impl ChildBySource for EnumId { 149impl ChildBySource for EnumId {
158 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 150 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
159 let mut res = DynMap::default();
160
161 let arena_map = self.child_source(db); 151 let arena_map = self.child_source(db);
162 let arena_map = arena_map.as_ref(); 152 let arena_map = arena_map.as_ref();
163 for (local_id, source) in arena_map.value.iter() { 153 for (local_id, source) in arena_map.value.iter() {
164 let id = EnumVariantId { parent: *self, local_id }; 154 let id = EnumVariantId { parent: *self, local_id };
165 res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id) 155 res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id)
166 } 156 }
167
168 res
169 } 157 }
170} 158}
171 159
172impl ChildBySource for DefWithBodyId { 160impl ChildBySource for DefWithBodyId {
173 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 161 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
174 let body = db.body(*self); 162 let body = db.body(*self);
175 body.item_scope.child_by_source(db) 163 for def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) {
164 // All block expressions are merged into the same map, because they logically all add
165 // inner items to the containing `DefWithBodyId`.
166 def_map[def_map.root()].scope.child_by_source_to(db, res);
167 }
176 } 168 }
177} 169}
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index d3380e0f4..aea53d527 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -97,7 +97,7 @@ impl TraitData {
97 let tr_def = &item_tree[tr_loc.id.value]; 97 let tr_def = &item_tree[tr_loc.id.value];
98 let name = tr_def.name.clone(); 98 let name = tr_def.name.clone();
99 let auto = tr_def.auto; 99 let auto = tr_def.auto;
100 let module_id = tr_loc.container.module(db); 100 let module_id = tr_loc.container;
101 let container = AssocContainerId::TraitId(tr); 101 let container = AssocContainerId::TraitId(tr);
102 let mut expander = Expander::new(db, tr_loc.id.file_id, module_id); 102 let mut expander = Expander::new(db, tr_loc.id.file_id, module_id);
103 103
@@ -147,7 +147,7 @@ impl ImplData {
147 let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); 147 let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone());
148 let target_type = item_tree[impl_def.target_type].clone(); 148 let target_type = item_tree[impl_def.target_type].clone();
149 let is_negative = impl_def.is_negative; 149 let is_negative = impl_def.is_negative;
150 let module_id = impl_loc.container.module(db); 150 let module_id = impl_loc.container;
151 let container = AssocContainerId::ImplId(id); 151 let container = AssocContainerId::ImplId(id);
152 let mut expander = Expander::new(db, impl_loc.id.file_id, module_id); 152 let mut expander = Expander::new(db, impl_loc.id.file_id, module_id);
153 153
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index 6c01f1ed0..cca5a086b 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -133,7 +133,7 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
133 fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; 133 fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
134} 134}
135 135
136fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<DefMap> { 136fn crate_def_map_wait(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
137 let _p = profile::span("crate_def_map:wait"); 137 let _p = profile::span("crate_def_map:wait");
138 db.crate_def_map_query(krate) 138 db.crate_def_map_query(krate)
139} 139}
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 3a98ffbaa..de08e2737 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -4,7 +4,6 @@ use std::iter;
4 4
5use hir_expand::name::{known, AsName, Name}; 5use hir_expand::name::{known, AsName, Name};
6use rustc_hash::FxHashSet; 6use rustc_hash::FxHashSet;
7use test_utils::mark;
8 7
9use crate::nameres::DefMap; 8use crate::nameres::DefMap;
10use crate::{ 9use crate::{
@@ -215,7 +214,7 @@ fn find_path_inner(
215 best_path_len - 1, 214 best_path_len - 1,
216 prefixed, 215 prefixed,
217 )?; 216 )?;
218 mark::hit!(partially_imported); 217 cov_mark::hit!(partially_imported);
219 path.push_segment(info.path.segments.last().unwrap().clone()); 218 path.push_segment(info.path.segments.last().unwrap().clone());
220 Some(path) 219 Some(path)
221 }) 220 })
@@ -235,7 +234,7 @@ fn find_path_inner(
235 // that correctly (FIXME). 234 // that correctly (FIXME).
236 if let Some(item_module) = item.as_module_def_id().and_then(|did| did.module(db)) { 235 if let Some(item_module) = item.as_module_def_id().and_then(|did| did.module(db)) {
237 if item_module.def_map(db).block_id().is_some() && prefixed.is_some() { 236 if item_module.def_map(db).block_id().is_some() && prefixed.is_some() {
238 mark::hit!(prefixed_in_block_expression); 237 cov_mark::hit!(prefixed_in_block_expression);
239 prefixed = Some(PrefixKind::Plain); 238 prefixed = Some(PrefixKind::Plain);
240 } 239 }
241 } 240 }
@@ -252,18 +251,18 @@ fn find_path_inner(
252fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { 251fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
253 if old_path.starts_with_std() && new_path.can_start_with_std() { 252 if old_path.starts_with_std() && new_path.can_start_with_std() {
254 if prefer_no_std { 253 if prefer_no_std {
255 mark::hit!(prefer_no_std_paths); 254 cov_mark::hit!(prefer_no_std_paths);
256 new_path 255 new_path
257 } else { 256 } else {
258 mark::hit!(prefer_std_paths); 257 cov_mark::hit!(prefer_std_paths);
259 old_path 258 old_path
260 } 259 }
261 } else if new_path.starts_with_std() && old_path.can_start_with_std() { 260 } else if new_path.starts_with_std() && old_path.can_start_with_std() {
262 if prefer_no_std { 261 if prefer_no_std {
263 mark::hit!(prefer_no_std_paths); 262 cov_mark::hit!(prefer_no_std_paths);
264 old_path 263 old_path
265 } else { 264 } else {
266 mark::hit!(prefer_std_paths); 265 cov_mark::hit!(prefer_std_paths);
267 new_path 266 new_path
268 } 267 }
269 } else if new_path.len() < old_path.len() { 268 } else if new_path.len() < old_path.len() {
@@ -364,7 +363,6 @@ mod tests {
364 use base_db::fixture::WithFixture; 363 use base_db::fixture::WithFixture;
365 use hir_expand::hygiene::Hygiene; 364 use hir_expand::hygiene::Hygiene;
366 use syntax::ast::AstNode; 365 use syntax::ast::AstNode;
367 use test_utils::mark;
368 366
369 use crate::test_db::TestDB; 367 use crate::test_db::TestDB;
370 368
@@ -522,7 +520,7 @@ mod tests {
522 520
523 #[test] 521 #[test]
524 fn partially_imported() { 522 fn partially_imported() {
525 mark::check!(partially_imported); 523 cov_mark::check!(partially_imported);
526 // Tests that short paths are used even for external items, when parts of the path are 524 // Tests that short paths are used even for external items, when parts of the path are
527 // already in scope. 525 // already in scope.
528 let code = r#" 526 let code = r#"
@@ -686,7 +684,7 @@ mod tests {
686 684
687 #[test] 685 #[test]
688 fn prefer_std_paths_over_alloc() { 686 fn prefer_std_paths_over_alloc() {
689 mark::check!(prefer_std_paths); 687 cov_mark::check!(prefer_std_paths);
690 let code = r#" 688 let code = r#"
691 //- /main.rs crate:main deps:alloc,std 689 //- /main.rs crate:main deps:alloc,std
692 $0 690 $0
@@ -712,7 +710,7 @@ mod tests {
712 710
713 #[test] 711 #[test]
714 fn prefer_core_paths_over_std() { 712 fn prefer_core_paths_over_std() {
715 mark::check!(prefer_no_std_paths); 713 cov_mark::check!(prefer_no_std_paths);
716 let code = r#" 714 let code = r#"
717 //- /main.rs crate:main deps:core,std 715 //- /main.rs crate:main deps:core,std
718 #![no_std] 716 #![no_std]
@@ -842,7 +840,7 @@ mod tests {
842 840
843 #[test] 841 #[test]
844 fn inner_items_from_inner_module() { 842 fn inner_items_from_inner_module() {
845 mark::check!(prefixed_in_block_expression); 843 cov_mark::check!(prefixed_in_block_expression);
846 check_found_path( 844 check_found_path(
847 r#" 845 r#"
848 fn main() { 846 fn main() {
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 3ace3be1f..a056ab797 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -421,8 +421,7 @@ impl HasChildSource<LocalConstParamId> for GenericDefId {
421} 421}
422 422
423impl ChildBySource for GenericDefId { 423impl ChildBySource for GenericDefId {
424 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 424 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
425 let mut res = DynMap::default();
426 let (_, sm) = GenericParams::new(db, *self); 425 let (_, sm) = GenericParams::new(db, *self);
427 426
428 let sm = sm.as_ref(); 427 let sm = sm.as_ref();
@@ -440,6 +439,5 @@ impl ChildBySource for GenericDefId {
440 let id = ConstParamId { parent: *self, local_id }; 439 let id = ConstParamId { parent: *self, local_id };
441 res[keys::CONST_PARAM].insert(sm.with_value(src.clone()), id); 440 res[keys::CONST_PARAM].insert(sm.with_value(src.clone()), id);
442 } 441 }
443 res
444 } 442 }
445} 443}
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index 0a3dc7956..369bc3350 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -8,7 +8,6 @@ use hir_expand::name::Name;
8use indexmap::{map::Entry, IndexMap}; 8use indexmap::{map::Entry, IndexMap};
9use itertools::Itertools; 9use itertools::Itertools;
10use rustc_hash::{FxHashSet, FxHasher}; 10use rustc_hash::{FxHashSet, FxHasher};
11use test_utils::mark;
12 11
13use crate::{ 12use crate::{
14 db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, 13 db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId,
@@ -193,7 +192,7 @@ impl ImportMap {
193 // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias` 192 // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias`
194 // qualifier, ergo no need to store it for imports in import_map 193 // qualifier, ergo no need to store it for imports in import_map
195 AssocItemId::TypeAliasId(_) => { 194 AssocItemId::TypeAliasId(_) => {
196 mark::hit!(type_aliases_ignored); 195 cov_mark::hit!(type_aliases_ignored);
197 continue; 196 continue;
198 } 197 }
199 }; 198 };
@@ -388,7 +387,7 @@ pub fn search_dependencies<'a>(
388 db: &'a dyn DefDatabase, 387 db: &'a dyn DefDatabase,
389 krate: CrateId, 388 krate: CrateId,
390 query: Query, 389 query: Query,
391) -> Vec<ItemInNs> { 390) -> FxHashSet<ItemInNs> {
392 let _p = profile::span("search_dependencies").detail(|| format!("{:?}", query)); 391 let _p = profile::span("search_dependencies").detail(|| format!("{:?}", query));
393 392
394 let graph = db.crate_graph(); 393 let graph = db.crate_graph();
@@ -403,41 +402,42 @@ pub fn search_dependencies<'a>(
403 } 402 }
404 403
405 let mut stream = op.union(); 404 let mut stream = op.union();
406 let mut res = Vec::new(); 405
406 let mut all_indexed_values = FxHashSet::default();
407 while let Some((_, indexed_values)) = stream.next() { 407 while let Some((_, indexed_values)) = stream.next() {
408 for indexed_value in indexed_values { 408 all_indexed_values.extend(indexed_values.iter().copied());
409 let import_map = &import_maps[indexed_value.index]; 409 }
410 let importables = &import_map.importables[indexed_value.value as usize..];
411 410
412 let common_importable_data = &import_map.map[&importables[0]]; 411 let mut res = FxHashSet::default();
413 if !query.import_matches(common_importable_data, true) { 412 for indexed_value in all_indexed_values {
414 continue; 413 let import_map = &import_maps[indexed_value.index];
415 } 414 let importables = &import_map.importables[indexed_value.value as usize..];
416 415
417 // Path shared by the importable items in this group. 416 let common_importable_data = &import_map.map[&importables[0]];
418 let common_importables_path_fst = fst_path(&common_importable_data.path); 417 if !query.import_matches(common_importable_data, true) {
419 // Add the items from this `ModPath` group. Those are all subsequent items in 418 continue;
420 // `importables` whose paths match `path`. 419 }
421 let iter = importables 420
422 .iter() 421 // Path shared by the importable items in this group.
423 .copied() 422 let common_importables_path_fst = fst_path(&common_importable_data.path);
424 .take_while(|item| { 423 // Add the items from this `ModPath` group. Those are all subsequent items in
425 common_importables_path_fst == fst_path(&import_map.map[item].path) 424 // `importables` whose paths match `path`.
426 }) 425 let iter = importables
427 .filter(|&item| match item_import_kind(item) { 426 .iter()
428 Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), 427 .copied()
429 None => true, 428 .take_while(|item| common_importables_path_fst == fst_path(&import_map.map[item].path))
430 }) 429 .filter(|&item| match item_import_kind(item) {
431 .filter(|item| { 430 Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
432 !query.case_sensitive // we've already checked the common importables path case-insensitively 431 None => true,
432 })
433 .filter(|item| {
434 !query.case_sensitive // we've already checked the common importables path case-insensitively
433 || query.import_matches(&import_map.map[item], false) 435 || query.import_matches(&import_map.map[item], false)
434 }); 436 });
435 res.extend(iter); 437 res.extend(iter);
436 438
437 if res.len() >= query.limit { 439 if res.len() >= query.limit {
438 res.truncate(query.limit); 440 return res;
439 return res;
440 }
441 } 441 }
442 } 442 }
443 443
@@ -462,7 +462,6 @@ fn item_import_kind(item: ItemInNs) -> Option<ImportKind> {
462mod tests { 462mod tests {
463 use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; 463 use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
464 use expect_test::{expect, Expect}; 464 use expect_test::{expect, Expect};
465 use test_utils::mark;
466 465
467 use crate::{test_db::TestDB, AssocContainerId, Lookup}; 466 use crate::{test_db::TestDB, AssocContainerId, Lookup};
468 467
@@ -800,7 +799,7 @@ mod tests {
800 799
801 #[test] 800 #[test]
802 fn fuzzy_import_trait_and_assoc_items() { 801 fn fuzzy_import_trait_and_assoc_items() {
803 mark::check!(type_aliases_ignored); 802 cov_mark::check!(type_aliases_ignored);
804 let ra_fixture = r#" 803 let ra_fixture = r#"
805 //- /main.rs crate:main deps:dep 804 //- /main.rs crate:main deps:dep
806 //- /dep.rs crate:dep 805 //- /dep.rs crate:dep
@@ -821,10 +820,10 @@ mod tests {
821 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), 820 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
822 expect![[r#" 821 expect![[r#"
823 dep::fmt (t) 822 dep::fmt (t)
823 dep::fmt::Display::format_method (a)
824 dep::fmt::Display (t) 824 dep::fmt::Display (t)
825 dep::fmt::Display::FMT_CONST (a) 825 dep::fmt::Display::FMT_CONST (a)
826 dep::fmt::Display::format_function (a) 826 dep::fmt::Display::format_function (a)
827 dep::fmt::Display::format_method (a)
828 "#]], 827 "#]],
829 ); 828 );
830 } 829 }
@@ -850,9 +849,9 @@ mod tests {
850 "main", 849 "main",
851 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(), 850 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(),
852 expect![[r#" 851 expect![[r#"
852 dep::fmt::Display::format_method (a)
853 dep::fmt::Display::FMT_CONST (a) 853 dep::fmt::Display::FMT_CONST (a)
854 dep::fmt::Display::format_function (a) 854 dep::fmt::Display::format_function (a)
855 dep::fmt::Display::format_method (a)
856 "#]], 855 "#]],
857 ); 856 );
858 857
@@ -911,12 +910,12 @@ mod tests {
911 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), 910 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
912 expect![[r#" 911 expect![[r#"
913 dep::fmt (t) 912 dep::fmt (t)
914 dep::Fmt (t) 913 dep::format (f)
915 dep::Fmt (v) 914 dep::Fmt (v)
916 dep::Fmt (m)
917 dep::fmt::Display (t) 915 dep::fmt::Display (t)
916 dep::Fmt (t)
918 dep::fmt::Display::fmt (a) 917 dep::fmt::Display::fmt (a)
919 dep::format (f) 918 dep::Fmt (m)
920 "#]], 919 "#]],
921 ); 920 );
922 921
@@ -926,10 +925,10 @@ mod tests {
926 Query::new("fmt".to_string()).search_mode(SearchMode::Equals), 925 Query::new("fmt".to_string()).search_mode(SearchMode::Equals),
927 expect![[r#" 926 expect![[r#"
928 dep::fmt (t) 927 dep::fmt (t)
929 dep::Fmt (t)
930 dep::Fmt (v) 928 dep::Fmt (v)
931 dep::Fmt (m) 929 dep::Fmt (t)
932 dep::fmt::Display::fmt (a) 930 dep::fmt::Display::fmt (a)
931 dep::Fmt (m)
933 "#]], 932 "#]],
934 ); 933 );
935 934
@@ -939,11 +938,11 @@ mod tests {
939 Query::new("fmt".to_string()).search_mode(SearchMode::Contains), 938 Query::new("fmt".to_string()).search_mode(SearchMode::Contains),
940 expect![[r#" 939 expect![[r#"
941 dep::fmt (t) 940 dep::fmt (t)
942 dep::Fmt (t)
943 dep::Fmt (v) 941 dep::Fmt (v)
944 dep::Fmt (m)
945 dep::fmt::Display (t) 942 dep::fmt::Display (t)
943 dep::Fmt (t)
946 dep::fmt::Display::fmt (a) 944 dep::fmt::Display::fmt (a)
945 dep::Fmt (m)
947 "#]], 946 "#]],
948 ); 947 );
949 } 948 }
@@ -980,11 +979,11 @@ mod tests {
980 Query::new("fmt".to_string()), 979 Query::new("fmt".to_string()),
981 expect![[r#" 980 expect![[r#"
982 dep::fmt (t) 981 dep::fmt (t)
983 dep::Fmt (t)
984 dep::Fmt (v) 982 dep::Fmt (v)
985 dep::Fmt (m)
986 dep::fmt::Display (t) 983 dep::fmt::Display (t)
984 dep::Fmt (t)
987 dep::fmt::Display::fmt (a) 985 dep::fmt::Display::fmt (a)
986 dep::Fmt (m)
988 "#]], 987 "#]],
989 ); 988 );
990 989
@@ -994,10 +993,10 @@ mod tests {
994 Query::new("fmt".to_string()).name_only(), 993 Query::new("fmt".to_string()).name_only(),
995 expect![[r#" 994 expect![[r#"
996 dep::fmt (t) 995 dep::fmt (t)
997 dep::Fmt (t)
998 dep::Fmt (v) 996 dep::Fmt (v)
999 dep::Fmt (m) 997 dep::Fmt (t)
1000 dep::fmt::Display::fmt (a) 998 dep::fmt::Display::fmt (a)
999 dep::Fmt (m)
1001 "#]], 1000 "#]],
1002 ); 1001 );
1003 } 1002 }
@@ -1018,9 +1017,9 @@ mod tests {
1018 Query::new("FMT".to_string()), 1017 Query::new("FMT".to_string()),
1019 expect![[r#" 1018 expect![[r#"
1020 dep::fmt (t) 1019 dep::fmt (t)
1020 dep::FMT (v)
1021 dep::fmt (v) 1021 dep::fmt (v)
1022 dep::FMT (t) 1022 dep::FMT (t)
1023 dep::FMT (v)
1024 "#]], 1023 "#]],
1025 ); 1024 );
1026 1025
@@ -1060,6 +1059,8 @@ mod tests {
1060 expect![[r#" 1059 expect![[r#"
1061 dep::fmt (t) 1060 dep::fmt (t)
1062 dep::Fmt (t) 1061 dep::Fmt (t)
1062 dep::Fmt (m)
1063 dep::Fmt (v)
1063 "#]], 1064 "#]],
1064 ); 1065 );
1065 } 1066 }
@@ -1080,9 +1081,9 @@ mod tests {
1080 Query::new("FMT".to_string()), 1081 Query::new("FMT".to_string()),
1081 expect![[r#" 1082 expect![[r#"
1082 dep::fmt (t) 1083 dep::fmt (t)
1084 dep::FMT (v)
1083 dep::fmt (v) 1085 dep::fmt (v)
1084 dep::FMT (t) 1086 dep::FMT (t)
1085 dep::FMT (v)
1086 "#]], 1087 "#]],
1087 ); 1088 );
1088 1089
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index 4e5daa2ff..aafd73b60 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -9,7 +9,6 @@ use hir_expand::MacroDefKind;
9use once_cell::sync::Lazy; 9use once_cell::sync::Lazy;
10use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
11use stdx::format_to; 11use stdx::format_to;
12use test_utils::mark;
13 12
14use crate::{ 13use crate::{
15 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, 14 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId,
@@ -169,37 +168,6 @@ impl ItemScope {
169 self.unnamed_trait_imports.insert(tr, vis); 168 self.unnamed_trait_imports.insert(tr, vis);
170 } 169 }
171 170
172 pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool {
173 let mut changed = false;
174
175 if let Some(types) = def.types {
176 self.types.entry(name.clone()).or_insert_with(|| {
177 changed = true;
178 types
179 });
180 }
181 if let Some(values) = def.values {
182 self.values.entry(name.clone()).or_insert_with(|| {
183 changed = true;
184 values
185 });
186 }
187 if let Some(macros) = def.macros {
188 self.macros.entry(name.clone()).or_insert_with(|| {
189 changed = true;
190 macros
191 });
192 }
193
194 if def.is_none() {
195 if self.unresolved.insert(name) {
196 changed = true;
197 }
198 }
199
200 changed
201 }
202
203 pub(crate) fn push_res_with_import( 171 pub(crate) fn push_res_with_import(
204 &mut self, 172 &mut self,
205 glob_imports: &mut PerNsGlobImports, 173 glob_imports: &mut PerNsGlobImports,
@@ -237,7 +205,7 @@ impl ItemScope {
237 if $glob_imports.$field.contains(&$lookup) 205 if $glob_imports.$field.contains(&$lookup)
238 && matches!($def_import_type, ImportType::Named) => 206 && matches!($def_import_type, ImportType::Named) =>
239 { 207 {
240 mark::hit!(import_shadowed); 208 cov_mark::hit!(import_shadowed);
241 $glob_imports.$field.remove(&$lookup); 209 $glob_imports.$field.remove(&$lookup);
242 if let Some(fld) = $def.$field { 210 if let Some(fld) = $def.$field {
243 entry.insert(fld); 211 entry.insert(fld);
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 3233b1957..6bb334573 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -25,7 +25,6 @@ use profile::Count;
25use rustc_hash::FxHashMap; 25use rustc_hash::FxHashMap;
26use smallvec::SmallVec; 26use smallvec::SmallVec;
27use syntax::{ast, match_ast, SyntaxKind}; 27use syntax::{ast, match_ast, SyntaxKind};
28use test_utils::mark;
29 28
30use crate::{ 29use crate::{
31 attr::{Attrs, RawAttrs}, 30 attr::{Attrs, RawAttrs},
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 8f2f0b340..240fdacf9 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -466,7 +466,7 @@ impl Ctx {
466 .collect() 466 .collect()
467 }) 467 })
468 .unwrap_or_else(|| { 468 .unwrap_or_else(|| {
469 mark::hit!(name_res_works_for_broken_modules); 469 cov_mark::hit!(name_res_works_for_broken_modules);
470 Box::new([]) as Box<[_]> 470 Box::new([]) as Box<[_]>
471 }), 471 }),
472 } 472 }
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 4498d94bb..6d11c5be4 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -108,7 +108,7 @@ pub type LocalModuleId = Idx<nameres::ModuleData>;
108 108
109#[derive(Debug)] 109#[derive(Debug)]
110pub struct ItemLoc<N: ItemTreeNode> { 110pub struct ItemLoc<N: ItemTreeNode> {
111 pub container: ContainerId, 111 pub container: ModuleId,
112 pub id: ItemTreeId<N>, 112 pub id: ItemTreeId<N>,
113} 113}
114 114
@@ -279,18 +279,12 @@ pub struct ConstParamId {
279pub type LocalConstParamId = Idx<generics::ConstParamData>; 279pub type LocalConstParamId = Idx<generics::ConstParamData>;
280 280
281#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 281#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
282pub enum ContainerId {
283 ModuleId(ModuleId),
284 DefWithBodyId(DefWithBodyId),
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
288pub enum AssocContainerId { 282pub enum AssocContainerId {
289 ContainerId(ContainerId), 283 ModuleId(ModuleId),
290 ImplId(ImplId), 284 ImplId(ImplId),
291 TraitId(TraitId), 285 TraitId(TraitId),
292} 286}
293impl_from!(ContainerId for AssocContainerId); 287impl_from!(ModuleId for AssocContainerId);
294 288
295/// A Data Type 289/// A Data Type
296#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 290#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -447,21 +441,12 @@ pub trait HasModule {
447 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId; 441 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId;
448} 442}
449 443
450impl HasModule for ContainerId {
451 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
452 match *self {
453 ContainerId::ModuleId(it) => it,
454 ContainerId::DefWithBodyId(it) => it.module(db),
455 }
456 }
457}
458
459impl HasModule for AssocContainerId { 444impl HasModule for AssocContainerId {
460 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { 445 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
461 match *self { 446 match *self {
462 AssocContainerId::ContainerId(it) => it.module(db), 447 AssocContainerId::ModuleId(it) => it,
463 AssocContainerId::ImplId(it) => it.lookup(db).container.module(db), 448 AssocContainerId::ImplId(it) => it.lookup(db).container,
464 AssocContainerId::TraitId(it) => it.lookup(db).container.module(db), 449 AssocContainerId::TraitId(it) => it.lookup(db).container,
465 } 450 }
466 } 451 }
467} 452}
@@ -479,16 +464,15 @@ impl HasModule for AdtId {
479 AdtId::UnionId(it) => it.lookup(db).container, 464 AdtId::UnionId(it) => it.lookup(db).container,
480 AdtId::EnumId(it) => it.lookup(db).container, 465 AdtId::EnumId(it) => it.lookup(db).container,
481 } 466 }
482 .module(db)
483 } 467 }
484} 468}
485 469
486impl HasModule for VariantId { 470impl HasModule for VariantId {
487 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { 471 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
488 match self { 472 match self {
489 VariantId::EnumVariantId(it) => it.parent.lookup(db).container.module(db), 473 VariantId::EnumVariantId(it) => it.parent.lookup(db).container,
490 VariantId::StructId(it) => it.lookup(db).container.module(db), 474 VariantId::StructId(it) => it.lookup(db).container,
491 VariantId::UnionId(it) => it.lookup(db).container.module(db), 475 VariantId::UnionId(it) => it.lookup(db).container,
492 } 476 }
493 } 477 }
494} 478}
@@ -518,18 +502,18 @@ impl HasModule for GenericDefId {
518 match self { 502 match self {
519 GenericDefId::FunctionId(it) => it.lookup(db).module(db), 503 GenericDefId::FunctionId(it) => it.lookup(db).module(db),
520 GenericDefId::AdtId(it) => it.module(db), 504 GenericDefId::AdtId(it) => it.module(db),
521 GenericDefId::TraitId(it) => it.lookup(db).container.module(db), 505 GenericDefId::TraitId(it) => it.lookup(db).container,
522 GenericDefId::TypeAliasId(it) => it.lookup(db).module(db), 506 GenericDefId::TypeAliasId(it) => it.lookup(db).module(db),
523 GenericDefId::ImplId(it) => it.lookup(db).container.module(db), 507 GenericDefId::ImplId(it) => it.lookup(db).container,
524 GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container.module(db), 508 GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container,
525 GenericDefId::ConstId(it) => it.lookup(db).module(db), 509 GenericDefId::ConstId(it) => it.lookup(db).module(db),
526 } 510 }
527 } 511 }
528} 512}
529 513
530impl HasModule for StaticLoc { 514impl HasModule for StaticLoc {
531 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { 515 fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId {
532 self.container.module(db) 516 self.container
533 } 517 }
534} 518}
535 519
@@ -542,10 +526,10 @@ impl ModuleDefId {
542 ModuleDefId::ModuleId(id) => *id, 526 ModuleDefId::ModuleId(id) => *id,
543 ModuleDefId::FunctionId(id) => id.lookup(db).module(db), 527 ModuleDefId::FunctionId(id) => id.lookup(db).module(db),
544 ModuleDefId::AdtId(id) => id.module(db), 528 ModuleDefId::AdtId(id) => id.module(db),
545 ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db), 529 ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container,
546 ModuleDefId::ConstId(id) => id.lookup(db).container.module(db), 530 ModuleDefId::ConstId(id) => id.lookup(db).container.module(db),
547 ModuleDefId::StaticId(id) => id.lookup(db).container.module(db), 531 ModuleDefId::StaticId(id) => id.lookup(db).container,
548 ModuleDefId::TraitId(id) => id.lookup(db).container.module(db), 532 ModuleDefId::TraitId(id) => id.lookup(db).container,
549 ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db), 533 ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db),
550 ModuleDefId::BuiltinType(_) => return None, 534 ModuleDefId::BuiltinType(_) => return None,
551 }) 535 })
@@ -559,12 +543,12 @@ impl AttrDefId {
559 AttrDefId::FieldId(it) => it.parent.module(db).krate, 543 AttrDefId::FieldId(it) => it.parent.module(db).krate,
560 AttrDefId::AdtId(it) => it.module(db).krate, 544 AttrDefId::AdtId(it) => it.module(db).krate,
561 AttrDefId::FunctionId(it) => it.lookup(db).module(db).krate, 545 AttrDefId::FunctionId(it) => it.lookup(db).module(db).krate,
562 AttrDefId::EnumVariantId(it) => it.parent.lookup(db).container.module(db).krate, 546 AttrDefId::EnumVariantId(it) => it.parent.lookup(db).container.krate,
563 AttrDefId::StaticId(it) => it.lookup(db).module(db).krate, 547 AttrDefId::StaticId(it) => it.lookup(db).module(db).krate,
564 AttrDefId::ConstId(it) => it.lookup(db).module(db).krate, 548 AttrDefId::ConstId(it) => it.lookup(db).module(db).krate,
565 AttrDefId::TraitId(it) => it.lookup(db).container.module(db).krate, 549 AttrDefId::TraitId(it) => it.lookup(db).container.krate,
566 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate, 550 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate,
567 AttrDefId::ImplId(it) => it.lookup(db).container.module(db).krate, 551 AttrDefId::ImplId(it) => it.lookup(db).container.krate,
568 AttrDefId::GenericParamId(it) => { 552 AttrDefId::GenericParamId(it) => {
569 match it { 553 match it {
570 GenericParamId::TypeParamId(it) => it.parent, 554 GenericParamId::TypeParamId(it) => it.parent,
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index e51d89b43..9ed48c506 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -18,7 +18,6 @@ use hir_expand::{
18use hir_expand::{InFile, MacroCallLoc}; 18use hir_expand::{InFile, MacroCallLoc};
19use rustc_hash::{FxHashMap, FxHashSet}; 19use rustc_hash::{FxHashMap, FxHashSet};
20use syntax::ast; 20use syntax::ast;
21use test_utils::mark;
22use tt::{Leaf, TokenTree}; 21use tt::{Leaf, TokenTree};
23 22
24use crate::{ 23use crate::{
@@ -38,9 +37,9 @@ use crate::{
38 path::{ImportAlias, ModPath, PathKind}, 37 path::{ImportAlias, ModPath, PathKind},
39 per_ns::PerNs, 38 per_ns::PerNs,
40 visibility::{RawVisibility, Visibility}, 39 visibility::{RawVisibility, Visibility},
41 AdtId, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, 40 AdtId, AstId, AstIdWithPath, ConstLoc, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern,
42 ImplLoc, Intern, LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, 41 LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
43 UnionLoc, UnresolvedMacro, 42 UnresolvedMacro,
44}; 43};
45 44
46const GLOB_RECURSION_LIMIT: usize = 100; 45const GLOB_RECURSION_LIMIT: usize = 100;
@@ -462,7 +461,7 @@ impl DefCollector<'_> {
462 let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name); 461 let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name);
463 462
464 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { 463 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
465 mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); 464 cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
466 self.import_all_macros_exported(current_module_id, m.krate); 465 self.import_all_macros_exported(current_module_id, m.krate);
467 } 466 }
468 } 467 }
@@ -571,10 +570,10 @@ impl DefCollector<'_> {
571 match def.take_types() { 570 match def.take_types() {
572 Some(ModuleDefId::ModuleId(m)) => { 571 Some(ModuleDefId::ModuleId(m)) => {
573 if import.is_prelude { 572 if import.is_prelude {
574 mark::hit!(std_prelude); 573 cov_mark::hit!(std_prelude);
575 self.def_map.prelude = Some(m); 574 self.def_map.prelude = Some(m);
576 } else if m.krate != self.def_map.krate { 575 } else if m.krate != self.def_map.krate {
577 mark::hit!(glob_across_crates); 576 cov_mark::hit!(glob_across_crates);
578 // glob import from other crate => we can just import everything once 577 // glob import from other crate => we can just import everything once
579 let item_map = m.def_map(self.db); 578 let item_map = m.def_map(self.db);
580 let scope = &item_map[m.local_id].scope; 579 let scope = &item_map[m.local_id].scope;
@@ -626,7 +625,7 @@ impl DefCollector<'_> {
626 } 625 }
627 } 626 }
628 Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { 627 Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
629 mark::hit!(glob_enum); 628 cov_mark::hit!(glob_enum);
630 // glob import from enum => just import all the variants 629 // glob import from enum => just import all the variants
631 630
632 // XXX: urgh, so this works by accident! Here, we look at 631 // XXX: urgh, so this works by accident! Here, we look at
@@ -675,7 +674,7 @@ impl DefCollector<'_> {
675 674
676 self.update(module_id, &[(name, def)], vis, ImportType::Named); 675 self.update(module_id, &[(name, def)], vis, ImportType::Named);
677 } 676 }
678 None => mark::hit!(bogus_paths), 677 None => cov_mark::hit!(bogus_paths),
679 } 678 }
680 } 679 }
681 } 680 }
@@ -738,7 +737,7 @@ impl DefCollector<'_> {
738 if max_vis == old_vis { 737 if max_vis == old_vis {
739 false 738 false
740 } else { 739 } else {
741 mark::hit!(upgrade_underscore_visibility); 740 cov_mark::hit!(upgrade_underscore_visibility);
742 true 741 true
743 } 742 }
744 } 743 }
@@ -866,7 +865,7 @@ impl DefCollector<'_> {
866 depth: usize, 865 depth: usize,
867 ) { 866 ) {
868 if depth > EXPANSION_DEPTH_LIMIT { 867 if depth > EXPANSION_DEPTH_LIMIT {
869 mark::hit!(macro_expansion_overflow); 868 cov_mark::hit!(macro_expansion_overflow);
870 log::warn!("macro expansion is too deep"); 869 log::warn!("macro expansion is too deep");
871 return; 870 return;
872 } 871 }
@@ -1009,7 +1008,7 @@ impl ModCollector<'_, '_> {
1009 // Prelude module is always considered to be `#[macro_use]`. 1008 // Prelude module is always considered to be `#[macro_use]`.
1010 if let Some(prelude_module) = self.def_collector.def_map.prelude { 1009 if let Some(prelude_module) = self.def_collector.def_map.prelude {
1011 if prelude_module.krate != self.def_collector.def_map.krate { 1010 if prelude_module.krate != self.def_collector.def_map.krate {
1012 mark::hit!(prelude_is_macro_use); 1011 cov_mark::hit!(prelude_is_macro_use);
1013 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); 1012 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
1014 } 1013 }
1015 } 1014 }
@@ -1043,7 +1042,6 @@ impl ModCollector<'_, '_> {
1043 } 1042 }
1044 } 1043 }
1045 let module = self.def_collector.def_map.module_id(self.module_id); 1044 let module = self.def_collector.def_map.module_id(self.module_id);
1046 let container = ContainerId::ModuleId(module);
1047 1045
1048 let mut def = None; 1046 let mut def = None;
1049 match item { 1047 match item {
@@ -1110,9 +1108,9 @@ impl ModCollector<'_, '_> {
1110 } 1108 }
1111 ModItem::Impl(imp) => { 1109 ModItem::Impl(imp) => {
1112 let module = self.def_collector.def_map.module_id(self.module_id); 1110 let module = self.def_collector.def_map.module_id(self.module_id);
1113 let container = ContainerId::ModuleId(module); 1111 let impl_id =
1114 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } 1112 ImplLoc { container: module, id: ItemTreeId::new(self.file_id, imp) }
1115 .intern(self.def_collector.db); 1113 .intern(self.def_collector.db);
1116 self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id) 1114 self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id)
1117 } 1115 }
1118 ModItem::Function(id) => { 1116 ModItem::Function(id) => {
@@ -1122,7 +1120,7 @@ impl ModCollector<'_, '_> {
1122 1120
1123 def = Some(DefData { 1121 def = Some(DefData {
1124 id: FunctionLoc { 1122 id: FunctionLoc {
1125 container: container.into(), 1123 container: module.into(),
1126 id: ItemTreeId::new(self.file_id, id), 1124 id: ItemTreeId::new(self.file_id, id),
1127 } 1125 }
1128 .intern(self.def_collector.db) 1126 .intern(self.def_collector.db)
@@ -1141,7 +1139,7 @@ impl ModCollector<'_, '_> {
1141 self.collect_derives(&attrs, it.ast_id.upcast()); 1139 self.collect_derives(&attrs, it.ast_id.upcast());
1142 1140
1143 def = Some(DefData { 1141 def = Some(DefData {
1144 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) } 1142 id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1145 .intern(self.def_collector.db) 1143 .intern(self.def_collector.db)
1146 .into(), 1144 .into(),
1147 name: &it.name, 1145 name: &it.name,
@@ -1158,7 +1156,7 @@ impl ModCollector<'_, '_> {
1158 self.collect_derives(&attrs, it.ast_id.upcast()); 1156 self.collect_derives(&attrs, it.ast_id.upcast());
1159 1157
1160 def = Some(DefData { 1158 def = Some(DefData {
1161 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } 1159 id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1162 .intern(self.def_collector.db) 1160 .intern(self.def_collector.db)
1163 .into(), 1161 .into(),
1164 name: &it.name, 1162 name: &it.name,
@@ -1175,7 +1173,7 @@ impl ModCollector<'_, '_> {
1175 self.collect_derives(&attrs, it.ast_id.upcast()); 1173 self.collect_derives(&attrs, it.ast_id.upcast());
1176 1174
1177 def = Some(DefData { 1175 def = Some(DefData {
1178 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } 1176 id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1179 .intern(self.def_collector.db) 1177 .intern(self.def_collector.db)
1180 .into(), 1178 .into(),
1181 name: &it.name, 1179 name: &it.name,
@@ -1189,7 +1187,7 @@ impl ModCollector<'_, '_> {
1189 if let Some(name) = &it.name { 1187 if let Some(name) = &it.name {
1190 def = Some(DefData { 1188 def = Some(DefData {
1191 id: ConstLoc { 1189 id: ConstLoc {
1192 container: container.into(), 1190 container: module.into(),
1193 id: ItemTreeId::new(self.file_id, id), 1191 id: ItemTreeId::new(self.file_id, id),
1194 } 1192 }
1195 .intern(self.def_collector.db) 1193 .intern(self.def_collector.db)
@@ -1204,7 +1202,7 @@ impl ModCollector<'_, '_> {
1204 let it = &self.item_tree[id]; 1202 let it = &self.item_tree[id];
1205 1203
1206 def = Some(DefData { 1204 def = Some(DefData {
1207 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } 1205 id: StaticLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1208 .intern(self.def_collector.db) 1206 .intern(self.def_collector.db)
1209 .into(), 1207 .into(),
1210 name: &it.name, 1208 name: &it.name,
@@ -1216,7 +1214,7 @@ impl ModCollector<'_, '_> {
1216 let it = &self.item_tree[id]; 1214 let it = &self.item_tree[id];
1217 1215
1218 def = Some(DefData { 1216 def = Some(DefData {
1219 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } 1217 id: TraitLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1220 .intern(self.def_collector.db) 1218 .intern(self.def_collector.db)
1221 .into(), 1219 .into(),
1222 name: &it.name, 1220 name: &it.name,
@@ -1229,7 +1227,7 @@ impl ModCollector<'_, '_> {
1229 1227
1230 def = Some(DefData { 1228 def = Some(DefData {
1231 id: TypeAliasLoc { 1229 id: TypeAliasLoc {
1232 container: container.into(), 1230 container: module.into(),
1233 id: ItemTreeId::new(self.file_id, id), 1231 id: ItemTreeId::new(self.file_id, id),
1234 } 1232 }
1235 .intern(self.def_collector.db) 1233 .intern(self.def_collector.db)
diff --git a/crates/hir_def/src/nameres/mod_resolution.rs b/crates/hir_def/src/nameres/mod_resolution.rs
index af3262439..d5de9899c 100644
--- a/crates/hir_def/src/nameres/mod_resolution.rs
+++ b/crates/hir_def/src/nameres/mod_resolution.rs
@@ -2,7 +2,6 @@
2use base_db::{AnchoredPath, FileId}; 2use base_db::{AnchoredPath, FileId};
3use hir_expand::name::Name; 3use hir_expand::name::Name;
4use syntax::SmolStr; 4use syntax::SmolStr;
5use test_utils::mark;
6 5
7use crate::{db::DefDatabase, HirFileId}; 6use crate::{db::DefDatabase, HirFileId};
8 7
@@ -28,7 +27,7 @@ impl ModDir {
28 let depth = self.depth + 1; 27 let depth = self.depth + 1;
29 if depth > MOD_DEPTH_LIMIT { 28 if depth > MOD_DEPTH_LIMIT {
30 log::error!("MOD_DEPTH_LIMIT exceeded"); 29 log::error!("MOD_DEPTH_LIMIT exceeded");
31 mark::hit!(circular_mods); 30 cov_mark::hit!(circular_mods);
32 return None; 31 return None;
33 } 32 }
34 Some(ModDir { dir_path, root_non_dir_owner, depth }) 33 Some(ModDir { dir_path, root_non_dir_owner, depth })
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs
index dd1db0094..8258dcffb 100644
--- a/crates/hir_def/src/nameres/path_resolution.rs
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -13,7 +13,6 @@
13use base_db::Edition; 13use base_db::Edition;
14use hir_expand::name; 14use hir_expand::name;
15use hir_expand::name::Name; 15use hir_expand::name::Name;
16use test_utils::mark;
17 16
18use crate::{ 17use crate::{
19 db::DefDatabase, 18 db::DefDatabase,
@@ -63,7 +62,7 @@ impl ResolvePathResult {
63impl DefMap { 62impl DefMap {
64 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { 63 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
65 if name == &name!(self) { 64 if name == &name!(self) {
66 mark::hit!(extern_crate_self_as); 65 cov_mark::hit!(extern_crate_self_as);
67 return PerNs::types(self.module_id(self.root).into(), Visibility::Public); 66 return PerNs::types(self.module_id(self.root).into(), Visibility::Public);
68 } 67 }
69 self.extern_prelude 68 self.extern_prelude
@@ -101,7 +100,7 @@ impl DefMap {
101 // DefMap they're written in, so we restrict them when that happens. 100 // DefMap they're written in, so we restrict them when that happens.
102 if let Visibility::Module(m) = vis { 101 if let Visibility::Module(m) = vis {
103 if self.block_id() != m.block { 102 if self.block_id() != m.block {
104 mark::hit!(adjust_vis_in_block_def_map); 103 cov_mark::hit!(adjust_vis_in_block_def_map);
105 vis = Visibility::Module(self.module_id(self.root())); 104 vis = Visibility::Module(self.module_id(self.root()));
106 log::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis); 105 log::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
107 } 106 }
@@ -169,12 +168,12 @@ impl DefMap {
169 let mut curr_per_ns: PerNs = match path.kind { 168 let mut curr_per_ns: PerNs = match path.kind {
170 PathKind::DollarCrate(krate) => { 169 PathKind::DollarCrate(krate) => {
171 if krate == self.krate { 170 if krate == self.krate {
172 mark::hit!(macro_dollar_crate_self); 171 cov_mark::hit!(macro_dollar_crate_self);
173 PerNs::types(self.crate_root(db).into(), Visibility::Public) 172 PerNs::types(self.crate_root(db).into(), Visibility::Public)
174 } else { 173 } else {
175 let def_map = db.crate_def_map(krate); 174 let def_map = db.crate_def_map(krate);
176 let module = def_map.module_id(def_map.root); 175 let module = def_map.module_id(def_map.root);
177 mark::hit!(macro_dollar_crate_other); 176 cov_mark::hit!(macro_dollar_crate_other);
178 PerNs::types(module.into(), Visibility::Public) 177 PerNs::types(module.into(), Visibility::Public)
179 } 178 }
180 } 179 }
@@ -310,7 +309,7 @@ impl DefMap {
310 } 309 }
311 ModuleDefId::AdtId(AdtId::EnumId(e)) => { 310 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
312 // enum variant 311 // enum variant
313 mark::hit!(can_import_enum_variant); 312 cov_mark::hit!(can_import_enum_variant);
314 let enum_data = db.enum_data(e); 313 let enum_data = db.enum_data(e);
315 match enum_data.variant(&segment) { 314 match enum_data.variant(&segment) {
316 Some(local_id) => { 315 Some(local_id) => {
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index bd3e2701b..de3aa4f9a 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -9,7 +9,6 @@ use std::sync::Arc;
9 9
10use base_db::{fixture::WithFixture, SourceDatabase}; 10use base_db::{fixture::WithFixture, SourceDatabase};
11use expect_test::{expect, Expect}; 11use expect_test::{expect, Expect};
12use test_utils::mark;
13 12
14use crate::{db::DefDatabase, test_db::TestDB}; 13use crate::{db::DefDatabase, test_db::TestDB};
15 14
@@ -136,7 +135,7 @@ mod m {
136 135
137#[test] 136#[test]
138fn bogus_paths() { 137fn bogus_paths() {
139 mark::check!(bogus_paths); 138 cov_mark::check!(bogus_paths);
140 check( 139 check(
141 r#" 140 r#"
142//- /lib.rs 141//- /lib.rs
@@ -243,7 +242,7 @@ pub struct Baz;
243 242
244#[test] 243#[test]
245fn std_prelude() { 244fn std_prelude() {
246 mark::check!(std_prelude); 245 cov_mark::check!(std_prelude);
247 check( 246 check(
248 r#" 247 r#"
249//- /main.rs crate:main deps:test_crate 248//- /main.rs crate:main deps:test_crate
@@ -267,7 +266,7 @@ pub enum Foo { Bar, Baz };
267 266
268#[test] 267#[test]
269fn can_import_enum_variant() { 268fn can_import_enum_variant() {
270 mark::check!(can_import_enum_variant); 269 cov_mark::check!(can_import_enum_variant);
271 check( 270 check(
272 r#" 271 r#"
273enum E { V } 272enum E { V }
@@ -628,7 +627,7 @@ use crate::reex::*;
628 627
629#[test] 628#[test]
630fn underscore_pub_crate_reexport() { 629fn underscore_pub_crate_reexport() {
631 mark::check!(upgrade_underscore_visibility); 630 cov_mark::check!(upgrade_underscore_visibility);
632 check( 631 check(
633 r#" 632 r#"
634//- /main.rs crate:main deps:lib 633//- /main.rs crate:main deps:lib
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
index e8e72e5ef..d5ef8ceb5 100644
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -1,5 +1,4 @@
1use base_db::fixture::WithFixture; 1use base_db::fixture::WithFixture;
2use test_utils::mark;
3 2
4use crate::test_db::TestDB; 3use crate::test_db::TestDB;
5 4
@@ -63,7 +62,7 @@ fn unresolved_extern_crate() {
63 62
64#[test] 63#[test]
65fn extern_crate_self_as() { 64fn extern_crate_self_as() {
66 mark::check!(extern_crate_self_as); 65 cov_mark::check!(extern_crate_self_as);
67 check_diagnostics( 66 check_diagnostics(
68 r" 67 r"
69 //- /lib.rs 68 //- /lib.rs
@@ -140,7 +139,7 @@ fn inactive_item() {
140/// Tests that `cfg` attributes behind `cfg_attr` is handled properly. 139/// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
141#[test] 140#[test]
142fn inactive_via_cfg_attr() { 141fn inactive_via_cfg_attr() {
143 mark::check!(cfg_attr_active); 142 cov_mark::check!(cfg_attr_active);
144 check_diagnostics( 143 check_diagnostics(
145 r#" 144 r#"
146 //- /lib.rs 145 //- /lib.rs
diff --git a/crates/hir_def/src/nameres/tests/globs.rs b/crates/hir_def/src/nameres/tests/globs.rs
index 2ae836e3c..17426d54d 100644
--- a/crates/hir_def/src/nameres/tests/globs.rs
+++ b/crates/hir_def/src/nameres/tests/globs.rs
@@ -148,7 +148,7 @@ pub(crate) struct PubCrateStruct;
148 148
149#[test] 149#[test]
150fn glob_across_crates() { 150fn glob_across_crates() {
151 mark::check!(glob_across_crates); 151 cov_mark::check!(glob_across_crates);
152 check( 152 check(
153 r#" 153 r#"
154//- /main.rs crate:main deps:test_crate 154//- /main.rs crate:main deps:test_crate
@@ -184,7 +184,7 @@ struct Foo;
184 184
185#[test] 185#[test]
186fn glob_enum() { 186fn glob_enum() {
187 mark::check!(glob_enum); 187 cov_mark::check!(glob_enum);
188 check( 188 check(
189 r#" 189 r#"
190enum Foo { Bar, Baz } 190enum Foo { Bar, Baz }
@@ -201,7 +201,7 @@ use self::Foo::*;
201 201
202#[test] 202#[test]
203fn glob_enum_group() { 203fn glob_enum_group() {
204 mark::check!(glob_enum_group); 204 cov_mark::check!(glob_enum_group);
205 check( 205 check(
206 r#" 206 r#"
207enum Foo { Bar, Baz } 207enum Foo { Bar, Baz }
@@ -218,7 +218,7 @@ use self::Foo::{*};
218 218
219#[test] 219#[test]
220fn glob_shadowed_def() { 220fn glob_shadowed_def() {
221 mark::check!(import_shadowed); 221 cov_mark::check!(import_shadowed);
222 check( 222 check(
223 r#" 223 r#"
224//- /lib.rs 224//- /lib.rs
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 36ed5e8ce..f65a655bf 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -210,7 +210,7 @@ macro_rules! bar {
210 210
211#[test] 211#[test]
212fn macro_rules_from_other_crates_are_visible_with_macro_use() { 212fn macro_rules_from_other_crates_are_visible_with_macro_use() {
213 mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use); 213 cov_mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use);
214 check( 214 check(
215 r#" 215 r#"
216//- /main.rs crate:main deps:foo 216//- /main.rs crate:main deps:foo
@@ -260,7 +260,7 @@ mod priv_mod {
260 260
261#[test] 261#[test]
262fn prelude_is_macro_use() { 262fn prelude_is_macro_use() {
263 mark::check!(prelude_is_macro_use); 263 cov_mark::check!(prelude_is_macro_use);
264 check( 264 check(
265 r#" 265 r#"
266//- /main.rs crate:main deps:foo 266//- /main.rs crate:main deps:foo
@@ -550,7 +550,7 @@ mod m {
550 550
551#[test] 551#[test]
552fn macro_dollar_crate_is_correct_in_item() { 552fn macro_dollar_crate_is_correct_in_item() {
553 mark::check!(macro_dollar_crate_self); 553 cov_mark::check!(macro_dollar_crate_self);
554 check( 554 check(
555 r#" 555 r#"
556//- /main.rs crate:main deps:foo 556//- /main.rs crate:main deps:foo
@@ -608,7 +608,7 @@ struct Baz;
608 608
609#[test] 609#[test]
610fn macro_dollar_crate_is_correct_in_indirect_deps() { 610fn macro_dollar_crate_is_correct_in_indirect_deps() {
611 mark::check!(macro_dollar_crate_other); 611 cov_mark::check!(macro_dollar_crate_other);
612 // From std 612 // From std
613 check( 613 check(
614 r#" 614 r#"
@@ -686,7 +686,7 @@ pub trait Clone {}
686 686
687#[test] 687#[test]
688fn macro_expansion_overflow() { 688fn macro_expansion_overflow() {
689 mark::check!(macro_expansion_overflow); 689 cov_mark::check!(macro_expansion_overflow);
690 check( 690 check(
691 r#" 691 r#"
692macro_rules! a { 692macro_rules! a {
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs
index e80b593aa..dfbbad1f9 100644
--- a/crates/hir_def/src/nameres/tests/mod_resolution.rs
+++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs
@@ -2,7 +2,7 @@ use super::*;
2 2
3#[test] 3#[test]
4fn name_res_works_for_broken_modules() { 4fn name_res_works_for_broken_modules() {
5 mark::check!(name_res_works_for_broken_modules); 5 cov_mark::check!(name_res_works_for_broken_modules);
6 check( 6 check(
7 r" 7 r"
8//- /lib.rs 8//- /lib.rs
@@ -774,7 +774,7 @@ struct X;
774 774
775#[test] 775#[test]
776fn circular_mods() { 776fn circular_mods() {
777 mark::check!(circular_mods); 777 cov_mark::check!(circular_mods);
778 compute_crate_def_map( 778 compute_crate_def_map(
779 r#" 779 r#"
780//- /lib.rs 780//- /lib.rs
diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs
index d584b0b70..e2965b033 100644
--- a/crates/hir_def/src/path/lower/lower_use.rs
+++ b/crates/hir_def/src/path/lower/lower_use.rs
@@ -6,7 +6,6 @@ use std::iter;
6use either::Either; 6use either::Either;
7use hir_expand::{hygiene::Hygiene, name::AsName}; 7use hir_expand::{hygiene::Hygiene, name::AsName};
8use syntax::ast::{self, NameOwner}; 8use syntax::ast::{self, NameOwner};
9use test_utils::mark;
10 9
11use crate::path::{ImportAlias, ModPath, PathKind}; 10use crate::path::{ImportAlias, ModPath, PathKind};
12 11
@@ -54,7 +53,7 @@ pub(crate) fn lower_use_tree(
54 // FIXME: report errors somewhere 53 // FIXME: report errors somewhere
55 // We get here if we do 54 // We get here if we do
56 } else if is_glob { 55 } else if is_glob {
57 mark::hit!(glob_enum_group); 56 cov_mark::hit!(glob_enum_group);
58 if let Some(prefix) = prefix { 57 if let Some(prefix) = prefix {
59 cb(prefix, &tree, is_glob, None) 58 cb(prefix, &tree, is_glob, None)
60 } 59 }
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index e85f85e49..42736171e 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -19,10 +19,10 @@ use crate::{
19 path::{ModPath, PathKind}, 19 path::{ModPath, PathKind},
20 per_ns::PerNs, 20 per_ns::PerNs,
21 visibility::{RawVisibility, Visibility}, 21 visibility::{RawVisibility, Visibility},
22 AdtId, AssocContainerId, ConstId, ConstParamId, ContainerId, DefWithBodyId, EnumId, 22 AdtId, AssocContainerId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId,
23 EnumVariantId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LifetimeParamId, 23 FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LifetimeParamId, LocalModuleId,
24 LocalModuleId, Lookup, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, 24 Lookup, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
25 TypeParamId, VariantId, 25 VariantId,
26}; 26};
27 27
28#[derive(Debug, Clone, Default)] 28#[derive(Debug, Clone, Default)]
@@ -34,7 +34,7 @@ pub struct Resolver {
34// FIXME how to store these best 34// FIXME how to store these best
35#[derive(Debug, Clone)] 35#[derive(Debug, Clone)]
36struct ModuleItemMap { 36struct ModuleItemMap {
37 crate_def_map: Arc<DefMap>, 37 def_map: Arc<DefMap>,
38 module_id: LocalModuleId, 38 module_id: LocalModuleId,
39} 39}
40 40
@@ -337,11 +337,21 @@ impl Resolver {
337 let mut traits = FxHashSet::default(); 337 let mut traits = FxHashSet::default();
338 for scope in &self.scopes { 338 for scope in &self.scopes {
339 if let Scope::ModuleScope(m) = scope { 339 if let Scope::ModuleScope(m) = scope {
340 if let Some(prelude) = m.crate_def_map.prelude() { 340 if let Some(prelude) = m.def_map.prelude() {
341 let prelude_def_map = prelude.def_map(db); 341 let prelude_def_map = prelude.def_map(db);
342 traits.extend(prelude_def_map[prelude.local_id].scope.traits()); 342 traits.extend(prelude_def_map[prelude.local_id].scope.traits());
343 } 343 }
344 traits.extend(m.crate_def_map[m.module_id].scope.traits()); 344 traits.extend(m.def_map[m.module_id].scope.traits());
345
346 // Add all traits that are in scope because of the containing DefMaps
347 m.def_map.with_ancestor_maps(db, m.module_id, &mut |def_map, module| {
348 if let Some(prelude) = def_map.prelude() {
349 let prelude_def_map = prelude.def_map(db);
350 traits.extend(prelude_def_map[prelude.local_id].scope.traits());
351 }
352 traits.extend(def_map[module].scope.traits());
353 None::<()>
354 });
345 } 355 }
346 } 356 }
347 traits 357 traits
@@ -349,7 +359,7 @@ impl Resolver {
349 359
350 fn module_scope(&self) -> Option<(&DefMap, LocalModuleId)> { 360 fn module_scope(&self) -> Option<(&DefMap, LocalModuleId)> {
351 self.scopes.iter().rev().find_map(|scope| match scope { 361 self.scopes.iter().rev().find_map(|scope| match scope {
352 Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)), 362 Scope::ModuleScope(m) => Some((&*m.def_map, m.module_id)),
353 363
354 _ => None, 364 _ => None,
355 }) 365 })
@@ -413,21 +423,21 @@ impl Scope {
413 // def: m.module.into(), 423 // def: m.module.into(),
414 // }), 424 // }),
415 // ); 425 // );
416 m.crate_def_map[m.module_id].scope.entries().for_each(|(name, def)| { 426 m.def_map[m.module_id].scope.entries().for_each(|(name, def)| {
417 f(name.clone(), ScopeDef::PerNs(def)); 427 f(name.clone(), ScopeDef::PerNs(def));
418 }); 428 });
419 m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { 429 m.def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| {
420 let scope = PerNs::macros(macro_, Visibility::Public); 430 let scope = PerNs::macros(macro_, Visibility::Public);
421 seen.insert((name.clone(), scope)); 431 seen.insert((name.clone(), scope));
422 f(name.clone(), ScopeDef::PerNs(scope)); 432 f(name.clone(), ScopeDef::PerNs(scope));
423 }); 433 });
424 m.crate_def_map.extern_prelude().for_each(|(name, &def)| { 434 m.def_map.extern_prelude().for_each(|(name, &def)| {
425 f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); 435 f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public)));
426 }); 436 });
427 BUILTIN_SCOPE.iter().for_each(|(name, &def)| { 437 BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
428 f(name.clone(), ScopeDef::PerNs(def)); 438 f(name.clone(), ScopeDef::PerNs(def));
429 }); 439 });
430 if let Some(prelude) = m.crate_def_map.prelude() { 440 if let Some(prelude) = m.def_map.prelude() {
431 let prelude_def_map = prelude.def_map(db); 441 let prelude_def_map = prelude.def_map(db);
432 prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { 442 prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| {
433 let seen_tuple = (name.clone(), def); 443 let seen_tuple = (name.clone(), def);
@@ -513,8 +523,8 @@ impl Resolver {
513 self.push_scope(Scope::ImplDefScope(impl_def)) 523 self.push_scope(Scope::ImplDefScope(impl_def))
514 } 524 }
515 525
516 fn push_module_scope(self, crate_def_map: Arc<DefMap>, module_id: LocalModuleId) -> Resolver { 526 fn push_module_scope(self, def_map: Arc<DefMap>, module_id: LocalModuleId) -> Resolver {
517 self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id })) 527 self.push_scope(Scope::ModuleScope(ModuleItemMap { def_map, module_id }))
518 } 528 }
519 529
520 fn push_expr_scope( 530 fn push_expr_scope(
@@ -534,7 +544,7 @@ impl ModuleItemMap {
534 path: &ModPath, 544 path: &ModPath,
535 ) -> Option<ResolveValueResult> { 545 ) -> Option<ResolveValueResult> {
536 let (module_def, idx) = 546 let (module_def, idx) =
537 self.crate_def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other); 547 self.def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other);
538 match idx { 548 match idx {
539 None => { 549 None => {
540 let value = to_value_ns(module_def)?; 550 let value = to_value_ns(module_def)?;
@@ -564,7 +574,7 @@ impl ModuleItemMap {
564 path: &ModPath, 574 path: &ModPath,
565 ) -> Option<(TypeNs, Option<usize>)> { 575 ) -> Option<(TypeNs, Option<usize>)> {
566 let (module_def, idx) = 576 let (module_def, idx) =
567 self.crate_def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other); 577 self.def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other);
568 let res = to_type_ns(module_def)?; 578 let res = to_type_ns(module_def)?;
569 Some((res, idx)) 579 Some((res, idx))
570 } 580 }
@@ -678,19 +688,10 @@ impl HasResolver for DefWithBodyId {
678 } 688 }
679} 689}
680 690
681impl HasResolver for ContainerId {
682 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
683 match self {
684 ContainerId::ModuleId(it) => it.resolver(db),
685 ContainerId::DefWithBodyId(it) => it.module(db).resolver(db),
686 }
687 }
688}
689
690impl HasResolver for AssocContainerId { 691impl HasResolver for AssocContainerId {
691 fn resolver(self, db: &dyn DefDatabase) -> Resolver { 692 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
692 match self { 693 match self {
693 AssocContainerId::ContainerId(it) => it.resolver(db), 694 AssocContainerId::ModuleId(it) => it.resolver(db),
694 AssocContainerId::TraitId(it) => it.resolver(db), 695 AssocContainerId::TraitId(it) => it.resolver(db),
695 AssocContainerId::ImplId(it) => it.resolver(db), 696 AssocContainerId::ImplId(it) => it.resolver(db),
696 } 697 }
diff --git a/crates/hir_expand/Cargo.toml b/crates/hir_expand/Cargo.toml
index 5271110d2..76cb03126 100644
--- a/crates/hir_expand/Cargo.toml
+++ b/crates/hir_expand/Cargo.toml
@@ -21,4 +21,6 @@ parser = { path = "../parser", version = "0.0.0" }
21profile = { path = "../profile", version = "0.0.0" } 21profile = { path = "../profile", version = "0.0.0" }
22tt = { path = "../tt", version = "0.0.0" } 22tt = { path = "../tt", version = "0.0.0" }
23mbe = { path = "../mbe", version = "0.0.0" } 23mbe = { path = "../mbe", version = "0.0.0" }
24test_utils = { path = "../test_utils", version = "0.0.0" } 24
25[dev-dependencies]
26test_utils = { path = "../test_utils" }
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index d1302d749..b9c93f56f 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13itertools = "0.10.0" 14itertools = "0.10.0"
14arrayvec = "0.5.1" 15arrayvec = "0.5.1"
15smallvec = "1.2.0" 16smallvec = "1.2.0"
@@ -17,9 +18,9 @@ ena = "0.14.0"
17log = "0.4.8" 18log = "0.4.8"
18rustc-hash = "1.1.0" 19rustc-hash = "1.1.0"
19scoped-tls = "1" 20scoped-tls = "1"
20chalk-solve = { version = "0.59", default-features = false } 21chalk-solve = { version = "0.60", default-features = false }
21chalk-ir = "0.59" 22chalk-ir = "0.60"
22chalk-recursive = "0.59" 23chalk-recursive = "0.60"
23la-arena = { version = "0.2.0", path = "../../lib/arena" } 24la-arena = { version = "0.2.0", path = "../../lib/arena" }
24 25
25stdx = { path = "../stdx", version = "0.0.0" } 26stdx = { path = "../stdx", version = "0.0.0" }
@@ -28,9 +29,9 @@ hir_expand = { path = "../hir_expand", version = "0.0.0" }
28base_db = { path = "../base_db", version = "0.0.0" } 29base_db = { path = "../base_db", version = "0.0.0" }
29profile = { path = "../profile", version = "0.0.0" } 30profile = { path = "../profile", version = "0.0.0" }
30syntax = { path = "../syntax", version = "0.0.0" } 31syntax = { path = "../syntax", version = "0.0.0" }
31test_utils = { path = "../test_utils", version = "0.0.0" }
32 32
33[dev-dependencies] 33[dev-dependencies]
34test_utils = { path = "../test_utils" }
34expect-test = "1.1" 35expect-test = "1.1"
35tracing = "0.1" 36tracing = "0.1"
36tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } 37tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] }
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index b3af82444..06714409f 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -130,7 +130,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
130 ) -> chalk_ir::ProgramClauses<chalk::Interner>; 130 ) -> chalk_ir::ProgramClauses<chalk::Interner>;
131} 131}
132 132
133fn infer_wait(db: &impl HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> { 133fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
134 let _p = profile::span("infer:wait").detail(|| match def { 134 let _p = profile::span("infer:wait").detail(|| match def {
135 DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(), 135 DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(),
136 DefWithBodyId::StaticId(it) => { 136 DefWithBodyId::StaticId(it) => {
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index 6773ddea3..3605ca581 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -28,7 +28,6 @@ use syntax::{
28 ast::{self, NameOwner}, 28 ast::{self, NameOwner},
29 AstNode, AstPtr, 29 AstNode, AstPtr,
30}; 30};
31use test_utils::mark;
32 31
33use crate::{ 32use crate::{
34 db::HirDatabase, 33 db::HirDatabase,
@@ -93,16 +92,21 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
93 fn validate_func(&mut self, func: FunctionId) { 92 fn validate_func(&mut self, func: FunctionId) {
94 let data = self.db.function_data(func); 93 let data = self.db.function_data(func);
95 if data.is_extern { 94 if data.is_extern {
96 mark::hit!(extern_func_incorrect_case_ignored); 95 cov_mark::hit!(extern_func_incorrect_case_ignored);
97 return; 96 return;
98 } 97 }
99 98
100 let body = self.db.body(func.into()); 99 let body = self.db.body(func.into());
101 100
102 // Recursively validate inner scope items, such as static variables and constants. 101 // Recursively validate inner scope items, such as static variables and constants.
103 for (item_id, _) in body.item_scope.values() { 102 let db = self.db;
104 let mut validator = DeclValidator::new(self.db, self.krate, self.sink); 103 for block_def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) {
105 validator.validate_item(item_id); 104 for (_, module) in block_def_map.modules() {
105 for (def_id, _) in module.scope.values() {
106 let mut validator = DeclValidator::new(self.db, self.krate, self.sink);
107 validator.validate_item(def_id);
108 }
109 }
106 } 110 }
107 111
108 // Check whether non-snake case identifiers are allowed for this function. 112 // Check whether non-snake case identifiers are allowed for this function.
@@ -625,7 +629,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
625 fn validate_static(&mut self, static_id: StaticId) { 629 fn validate_static(&mut self, static_id: StaticId) {
626 let data = self.db.static_data(static_id); 630 let data = self.db.static_data(static_id);
627 if data.is_extern { 631 if data.is_extern {
628 mark::hit!(extern_static_incorrect_case_ignored); 632 cov_mark::hit!(extern_static_incorrect_case_ignored);
629 return; 633 return;
630 } 634 }
631 635
@@ -673,8 +677,6 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
673 677
674#[cfg(test)] 678#[cfg(test)]
675mod tests { 679mod tests {
676 use test_utils::mark;
677
678 use crate::diagnostics::tests::check_diagnostics; 680 use crate::diagnostics::tests::check_diagnostics;
679 681
680 #[test] 682 #[test]
@@ -889,8 +891,8 @@ fn main() {
889 891
890 #[test] 892 #[test]
891 fn ignores_extern_items() { 893 fn ignores_extern_items() {
892 mark::check!(extern_func_incorrect_case_ignored); 894 cov_mark::check!(extern_func_incorrect_case_ignored);
893 mark::check!(extern_static_incorrect_case_ignored); 895 cov_mark::check!(extern_static_incorrect_case_ignored);
894 check_diagnostics( 896 check_diagnostics(
895 r#" 897 r#"
896extern { 898extern {
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 66a88e2b6..2751cd304 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -2,9 +2,7 @@
2 2
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::{ 5use hir_def::{expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId};
6 expr::Statement, path::path, resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId,
7};
8use hir_expand::{diagnostics::DiagnosticSink, name}; 6use hir_expand::{diagnostics::DiagnosticSink, name};
9use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
10use syntax::{ast, AstPtr}; 8use syntax::{ast, AstPtr};
@@ -17,7 +15,7 @@ use crate::{
17 MissingPatFields, RemoveThisSemicolon, 15 MissingPatFields, RemoveThisSemicolon,
18 }, 16 },
19 utils::variant_data, 17 utils::variant_data,
20 InferenceResult, Ty, 18 AdtId, InferenceResult, Ty,
21}; 19};
22 20
23pub(crate) use hir_def::{ 21pub(crate) use hir_def::{
@@ -382,10 +380,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
382 }; 380 };
383 381
384 let (params, required) = match mismatch.expected { 382 let (params, required) = match mismatch.expected {
385 Ty::Adt(AdtId::EnumId(enum_id), ref parameters) if enum_id == core_result_enum => { 383 Ty::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ref parameters)
384 if enum_id == core_result_enum =>
385 {
386 (parameters, "Ok".to_string()) 386 (parameters, "Ok".to_string())
387 } 387 }
388 Ty::Adt(AdtId::EnumId(enum_id), ref parameters) if enum_id == core_option_enum => { 388 Ty::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ref parameters)
389 if enum_id == core_option_enum =>
390 {
389 (parameters, "Some".to_string()) 391 (parameters, "Some".to_string())
390 } 392 }
391 _ => return, 393 _ => return,
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index 86fee0050..04d39c571 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -222,12 +222,12 @@ use hir_def::{
222 adt::VariantData, 222 adt::VariantData,
223 body::Body, 223 body::Body,
224 expr::{Expr, Literal, Pat, PatId}, 224 expr::{Expr, Literal, Pat, PatId},
225 AdtId, EnumVariantId, StructId, VariantId, 225 EnumVariantId, StructId, VariantId,
226}; 226};
227use la_arena::Idx; 227use la_arena::Idx;
228use smallvec::{smallvec, SmallVec}; 228use smallvec::{smallvec, SmallVec};
229 229
230use crate::{db::HirDatabase, InferenceResult, Ty}; 230use crate::{db::HirDatabase, AdtId, InferenceResult, Ty};
231 231
232#[derive(Debug, Clone, Copy)] 232#[derive(Debug, Clone, Copy)]
233/// Either a pattern from the source code being analyzed, represented as 233/// Either a pattern from the source code being analyzed, represented as
@@ -627,7 +627,7 @@ pub(super) fn is_useful(
627 // - `!` type 627 // - `!` type
628 // In those cases, no match arm is useful. 628 // In those cases, no match arm is useful.
629 match cx.infer[cx.match_expr].strip_references() { 629 match cx.infer[cx.match_expr].strip_references() {
630 Ty::Adt(AdtId::EnumId(enum_id), ..) => { 630 Ty::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ..) => {
631 if cx.db.enum_data(*enum_id).variants.is_empty() { 631 if cx.db.enum_data(*enum_id).variants.is_empty() {
632 return Ok(Usefulness::NotUseful); 632 return Ok(Usefulness::NotUseful);
633 } 633 }
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index d4a8b48e6..ab51cb0a6 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -2,19 +2,20 @@
2 2
3use std::{borrow::Cow, fmt}; 3use std::{borrow::Cow, fmt};
4 4
5use crate::{
6 db::HirDatabase, primitive, utils::generics, AliasTy, CallableDefId, CallableSig,
7 GenericPredicate, Lifetime, Obligation, OpaqueTy, OpaqueTyId, ProjectionTy, Scalar, Substs,
8 TraitRef, Ty,
9};
10use arrayvec::ArrayVec; 5use arrayvec::ArrayVec;
11use chalk_ir::Mutability; 6use chalk_ir::Mutability;
12use hir_def::{ 7use hir_def::{
13 db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, 8 db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs,
14 AssocContainerId, HasModule, Lookup, ModuleId, TraitId, 9 AssocContainerId, Lookup, ModuleId, TraitId,
15}; 10};
16use hir_expand::name::Name; 11use hir_expand::name::Name;
17 12
13use crate::{
14 db::HirDatabase, primitive, utils::generics, AdtId, AliasTy, CallableDefId, CallableSig,
15 GenericPredicate, Lifetime, Obligation, OpaqueTy, OpaqueTyId, ProjectionTy, Scalar, Substs,
16 TraitRef, Ty,
17};
18
18pub struct HirFormatter<'a> { 19pub struct HirFormatter<'a> {
19 pub db: &'a dyn HirDatabase, 20 pub db: &'a dyn HirDatabase,
20 fmt: &'a mut dyn fmt::Write, 21 fmt: &'a mut dyn fmt::Write,
@@ -400,13 +401,13 @@ impl HirDisplay for Ty {
400 write!(f, " -> {}", ret_display)?; 401 write!(f, " -> {}", ret_display)?;
401 } 402 }
402 } 403 }
403 Ty::Adt(def_id, parameters) => { 404 Ty::Adt(AdtId(def_id), parameters) => {
404 match f.display_target { 405 match f.display_target {
405 DisplayTarget::Diagnostics | DisplayTarget::Test => { 406 DisplayTarget::Diagnostics | DisplayTarget::Test => {
406 let name = match *def_id { 407 let name = match *def_id {
407 AdtId::StructId(it) => f.db.struct_data(it).name.clone(), 408 hir_def::AdtId::StructId(it) => f.db.struct_data(it).name.clone(),
408 AdtId::UnionId(it) => f.db.union_data(it).name.clone(), 409 hir_def::AdtId::UnionId(it) => f.db.union_data(it).name.clone(),
409 AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), 410 hir_def::AdtId::EnumId(it) => f.db.enum_data(it).name.clone(),
410 }; 411 };
411 write!(f, "{}", name)?; 412 write!(f, "{}", name)?;
412 } 413 }
@@ -610,7 +611,7 @@ impl HirDisplay for CallableSig {
610} 611}
611 612
612fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> { 613fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> {
613 let krate = trait_.lookup(db).container.module(db).krate(); 614 let krate = trait_.lookup(db).container.krate();
614 let fn_traits = [ 615 let fn_traits = [
615 db.lang_item(krate, "fn".into()), 616 db.lang_item(krate, "fn".into()),
616 db.lang_item(krate, "fn_mut".into()), 617 db.lang_item(krate, "fn_mut".into()),
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index cf0a3add4..7e8846f27 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -6,7 +6,6 @@
6 6
7use chalk_ir::{Mutability, TyVariableKind}; 7use chalk_ir::{Mutability, TyVariableKind};
8use hir_def::lang_item::LangItemTarget; 8use hir_def::lang_item::LangItemTarget;
9use test_utils::mark;
10 9
11use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty}; 10use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty};
12 11
@@ -35,7 +34,7 @@ impl<'a> InferenceContext<'a> {
35 ty1.clone() 34 ty1.clone()
36 } else { 35 } else {
37 if let (Ty::FnDef(..), Ty::FnDef(..)) = (ty1, ty2) { 36 if let (Ty::FnDef(..), Ty::FnDef(..)) = (ty1, ty2) {
38 mark::hit!(coerce_fn_reification); 37 cov_mark::hit!(coerce_fn_reification);
39 // Special case: two function types. Try to coerce both to 38 // Special case: two function types. Try to coerce both to
40 // pointers to have a chance at getting a match. See 39 // pointers to have a chance at getting a match. See
41 // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 40 // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
@@ -45,7 +44,7 @@ impl<'a> InferenceContext<'a> {
45 let ptr_ty2 = Ty::fn_ptr(sig2); 44 let ptr_ty2 = Ty::fn_ptr(sig2);
46 self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) 45 self.coerce_merge_branch(&ptr_ty1, &ptr_ty2)
47 } else { 46 } else {
48 mark::hit!(coerce_merge_fail_fallback); 47 cov_mark::hit!(coerce_merge_fail_fallback);
49 ty1.clone() 48 ty1.clone()
50 } 49 }
51 } 50 }
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index cf1f1038a..262177ffb 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -8,11 +8,10 @@ use hir_def::{
8 expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, 8 expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
9 path::{GenericArg, GenericArgs}, 9 path::{GenericArg, GenericArgs},
10 resolver::resolver_for_expr, 10 resolver::resolver_for_expr,
11 AdtId, AssocContainerId, FieldId, Lookup, 11 AssocContainerId, FieldId, Lookup,
12}; 12};
13use hir_expand::name::{name, Name}; 13use hir_expand::name::{name, Name};
14use syntax::ast::RangeOp; 14use syntax::ast::RangeOp;
15use test_utils::mark;
16 15
17use crate::{ 16use crate::{
18 autoderef, 17 autoderef,
@@ -21,8 +20,8 @@ use crate::{
21 primitive::{self, UintTy}, 20 primitive::{self, UintTy},
22 traits::{FnTrait, InEnvironment}, 21 traits::{FnTrait, InEnvironment},
23 utils::{generics, variant_data, Generics}, 22 utils::{generics, variant_data, Generics},
24 Binders, CallableDefId, FnPointer, FnSig, Obligation, OpaqueTyId, Rawness, Scalar, Substs, 23 AdtId, Binders, CallableDefId, FnPointer, FnSig, Obligation, OpaqueTyId, Rawness, Scalar,
25 TraitRef, Ty, 24 Substs, TraitRef, Ty,
26}; 25};
27 26
28use super::{ 27use super::{
@@ -429,14 +428,14 @@ impl<'a> InferenceContext<'a> {
429 Ty::Tuple(_, substs) => { 428 Ty::Tuple(_, substs) => {
430 name.as_tuple_index().and_then(|idx| substs.0.get(idx).cloned()) 429 name.as_tuple_index().and_then(|idx| substs.0.get(idx).cloned())
431 } 430 }
432 Ty::Adt(AdtId::StructId(s), parameters) => { 431 Ty::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => {
433 self.db.struct_data(s).variant_data.field(name).map(|local_id| { 432 self.db.struct_data(s).variant_data.field(name).map(|local_id| {
434 let field = FieldId { parent: s.into(), local_id }; 433 let field = FieldId { parent: s.into(), local_id };
435 self.write_field_resolution(tgt_expr, field); 434 self.write_field_resolution(tgt_expr, field);
436 self.db.field_types(s.into())[field.local_id].clone().subst(&parameters) 435 self.db.field_types(s.into())[field.local_id].clone().subst(&parameters)
437 }) 436 })
438 } 437 }
439 Ty::Adt(AdtId::UnionId(u), parameters) => { 438 Ty::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => {
440 self.db.union_data(u).variant_data.field(name).map(|local_id| { 439 self.db.union_data(u).variant_data.field(name).map(|local_id| {
441 let field = FieldId { parent: u.into(), local_id }; 440 let field = FieldId { parent: u.into(), local_id };
442 self.write_field_resolution(tgt_expr, field); 441 self.write_field_resolution(tgt_expr, field);
@@ -498,7 +497,7 @@ impl<'a> InferenceContext<'a> {
498 _ => (), 497 _ => (),
499 } 498 }
500 sb = sb.fill(repeat_with(|| self.table.new_type_var())); 499 sb = sb.fill(repeat_with(|| self.table.new_type_var()));
501 Ty::Adt(box_, sb.build()) 500 Ty::adt_ty(box_, sb.build())
502 } else { 501 } else {
503 Ty::Unknown 502 Ty::Unknown
504 } 503 }
@@ -565,7 +564,7 @@ impl<'a> InferenceContext<'a> {
565 let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone()); 564 let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone());
566 565
567 if ret == Ty::Unknown { 566 if ret == Ty::Unknown {
568 mark::hit!(infer_expr_inner_binary_operator_overload); 567 cov_mark::hit!(infer_expr_inner_binary_operator_overload);
569 568
570 self.resolve_associated_type_with_params( 569 self.resolve_associated_type_with_params(
571 lhs_ty, 570 lhs_ty,
@@ -586,31 +585,31 @@ impl<'a> InferenceContext<'a> {
586 let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect)); 585 let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect));
587 match (range_type, lhs_ty, rhs_ty) { 586 match (range_type, lhs_ty, rhs_ty) {
588 (RangeOp::Exclusive, None, None) => match self.resolve_range_full() { 587 (RangeOp::Exclusive, None, None) => match self.resolve_range_full() {
589 Some(adt) => Ty::Adt(adt, Substs::empty()), 588 Some(adt) => Ty::adt_ty(adt, Substs::empty()),
590 None => Ty::Unknown, 589 None => Ty::Unknown,
591 }, 590 },
592 (RangeOp::Exclusive, None, Some(ty)) => match self.resolve_range_to() { 591 (RangeOp::Exclusive, None, Some(ty)) => match self.resolve_range_to() {
593 Some(adt) => Ty::Adt(adt, Substs::single(ty)), 592 Some(adt) => Ty::adt_ty(adt, Substs::single(ty)),
594 None => Ty::Unknown, 593 None => Ty::Unknown,
595 }, 594 },
596 (RangeOp::Inclusive, None, Some(ty)) => { 595 (RangeOp::Inclusive, None, Some(ty)) => {
597 match self.resolve_range_to_inclusive() { 596 match self.resolve_range_to_inclusive() {
598 Some(adt) => Ty::Adt(adt, Substs::single(ty)), 597 Some(adt) => Ty::adt_ty(adt, Substs::single(ty)),
599 None => Ty::Unknown, 598 None => Ty::Unknown,
600 } 599 }
601 } 600 }
602 (RangeOp::Exclusive, Some(_), Some(ty)) => match self.resolve_range() { 601 (RangeOp::Exclusive, Some(_), Some(ty)) => match self.resolve_range() {
603 Some(adt) => Ty::Adt(adt, Substs::single(ty)), 602 Some(adt) => Ty::adt_ty(adt, Substs::single(ty)),
604 None => Ty::Unknown, 603 None => Ty::Unknown,
605 }, 604 },
606 (RangeOp::Inclusive, Some(_), Some(ty)) => { 605 (RangeOp::Inclusive, Some(_), Some(ty)) => {
607 match self.resolve_range_inclusive() { 606 match self.resolve_range_inclusive() {
608 Some(adt) => Ty::Adt(adt, Substs::single(ty)), 607 Some(adt) => Ty::adt_ty(adt, Substs::single(ty)),
609 None => Ty::Unknown, 608 None => Ty::Unknown,
610 } 609 }
611 } 610 }
612 (RangeOp::Exclusive, Some(ty), None) => match self.resolve_range_from() { 611 (RangeOp::Exclusive, Some(ty), None) => match self.resolve_range_from() {
613 Some(adt) => Ty::Adt(adt, Substs::single(ty)), 612 Some(adt) => Ty::adt_ty(adt, Substs::single(ty)),
614 None => Ty::Unknown, 613 None => Ty::Unknown,
615 }, 614 },
616 (RangeOp::Inclusive, _, None) => Ty::Unknown, 615 (RangeOp::Inclusive, _, None) => Ty::Unknown,
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index eb099311c..a0ac8d80f 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -10,7 +10,6 @@ use hir_def::{
10 FieldId, 10 FieldId,
11}; 11};
12use hir_expand::name::Name; 12use hir_expand::name::Name;
13use test_utils::mark;
14 13
15use super::{BindingMode, Expectation, InferenceContext}; 14use super::{BindingMode, Expectation, InferenceContext};
16use crate::{lower::lower_to_chalk_mutability, utils::variant_data, Substs, Ty}; 15use crate::{lower::lower_to_chalk_mutability, utils::variant_data, Substs, Ty};
@@ -108,7 +107,7 @@ impl<'a> InferenceContext<'a> {
108 } 107 }
109 } 108 }
110 } else if let Pat::Ref { .. } = &body[pat] { 109 } else if let Pat::Ref { .. } = &body[pat] {
111 mark::hit!(match_ergonomics_ref); 110 cov_mark::hit!(match_ergonomics_ref);
112 // When you encounter a `&pat` pattern, reset to Move. 111 // When you encounter a `&pat` pattern, reset to Move.
113 // This is so that `w` is by value: `let (_, &w) = &(1, &2);` 112 // This is so that `w` is by value: `let (_, &w) = &(1, &2);`
114 default_bm = BindingMode::Move; 113 default_bm = BindingMode::Move;
@@ -237,7 +236,7 @@ impl<'a> InferenceContext<'a> {
237 }; 236 };
238 237
239 let inner_ty = self.infer_pat(*inner, inner_expected, default_bm); 238 let inner_ty = self.infer_pat(*inner, inner_expected, default_bm);
240 Ty::Adt(box_adt, Substs::single(inner_ty)) 239 Ty::adt_ty(box_adt, Substs::single(inner_ty))
241 } 240 }
242 None => Ty::Unknown, 241 None => Ty::Unknown,
243 }, 242 },
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 5d541104e..ae3554bac 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -260,7 +260,7 @@ impl<'a> InferenceContext<'a> {
260 })); 260 }));
261 Some(trait_substs) 261 Some(trait_substs)
262 } 262 }
263 AssocContainerId::ContainerId(_) => None, 263 AssocContainerId::ModuleId(_) => None,
264 }; 264 };
265 265
266 self.write_assoc_resolution(id, item); 266 self.write_assoc_resolution(id, item);
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index 99a89a7f3..54fcfed10 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -5,8 +5,6 @@ use std::borrow::Cow;
5use chalk_ir::{FloatTy, IntTy, TyVariableKind}; 5use chalk_ir::{FloatTy, IntTy, TyVariableKind};
6use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; 6use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue};
7 7
8use test_utils::mark;
9
10use super::{InferenceContext, Obligation}; 8use super::{InferenceContext, Obligation};
11use crate::{ 9use crate::{
12 BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferenceVar, Scalar, 10 BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferenceVar, Scalar,
@@ -387,7 +385,7 @@ impl InferenceTable {
387 // more than once 385 // more than once
388 for i in 0..3 { 386 for i in 0..3 {
389 if i > 0 { 387 if i > 0 {
390 mark::hit!(type_var_resolves_to_int_var); 388 cov_mark::hit!(type_var_resolves_to_int_var);
391 } 389 }
392 match &*ty { 390 match &*ty {
393 Ty::InferenceVar(tv, _) => { 391 Ty::InferenceVar(tv, _) => {
@@ -416,7 +414,7 @@ impl InferenceTable {
416 Ty::InferenceVar(tv, kind) => { 414 Ty::InferenceVar(tv, kind) => {
417 let inner = tv.to_inner(); 415 let inner = tv.to_inner();
418 if tv_stack.contains(&inner) { 416 if tv_stack.contains(&inner) {
419 mark::hit!(type_var_cycles_resolve_as_possible); 417 cov_mark::hit!(type_var_cycles_resolve_as_possible);
420 // recursive type 418 // recursive type
421 return self.type_variable_table.fallback_value(tv, kind); 419 return self.type_variable_table.fallback_value(tv, kind);
422 } 420 }
@@ -443,7 +441,7 @@ impl InferenceTable {
443 Ty::InferenceVar(tv, kind) => { 441 Ty::InferenceVar(tv, kind) => {
444 let inner = tv.to_inner(); 442 let inner = tv.to_inner();
445 if tv_stack.contains(&inner) { 443 if tv_stack.contains(&inner) {
446 mark::hit!(type_var_cycles_resolve_completely); 444 cov_mark::hit!(type_var_cycles_resolve_completely);
447 // recursive type 445 // recursive type
448 return self.type_variable_table.fallback_value(tv, kind); 446 return self.type_variable_table.fallback_value(tv, kind);
449 } 447 }
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index c2a20c480..e77f24e4e 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -27,9 +27,9 @@ use std::{iter, mem, ops::Deref, sync::Arc};
27 27
28use base_db::salsa; 28use base_db::salsa;
29use hir_def::{ 29use hir_def::{
30 builtin_type::BuiltinType, expr::ExprId, type_ref::Rawness, AdtId, AssocContainerId, 30 builtin_type::BuiltinType, expr::ExprId, type_ref::Rawness, AssocContainerId, DefWithBodyId,
31 DefWithBodyId, FunctionId, GenericDefId, HasModule, LifetimeParamId, Lookup, TraitId, 31 FunctionId, GenericDefId, HasModule, LifetimeParamId, Lookup, TraitId, TypeAliasId,
32 TypeAliasId, TypeParamId, 32 TypeParamId,
33}; 33};
34use itertools::Itertools; 34use itertools::Itertools;
35 35
@@ -47,7 +47,9 @@ pub use lower::{
47}; 47};
48pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; 48pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment};
49 49
50pub use chalk_ir::{BoundVar, DebruijnIndex, Mutability, Scalar, TyVariableKind}; 50pub use chalk_ir::{AdtId, BoundVar, DebruijnIndex, Mutability, Scalar, TyVariableKind};
51
52pub(crate) use crate::traits::chalk::Interner;
51 53
52#[derive(Clone, PartialEq, Eq, Debug, Hash)] 54#[derive(Clone, PartialEq, Eq, Debug, Hash)]
53pub enum Lifetime { 55pub enum Lifetime {
@@ -131,7 +133,7 @@ pub enum AliasTy {
131#[derive(Clone, PartialEq, Eq, Debug, Hash)] 133#[derive(Clone, PartialEq, Eq, Debug, Hash)]
132pub enum Ty { 134pub enum Ty {
133 /// Structures, enumerations and unions. 135 /// Structures, enumerations and unions.
134 Adt(AdtId, Substs), 136 Adt(AdtId<Interner>, Substs),
135 137
136 /// Represents an associated item like `Iterator::Item`. This is used 138 /// Represents an associated item like `Iterator::Item`. This is used
137 /// when we have tried to normalize a projection like `T::Item` but 139 /// when we have tried to normalize a projection like `T::Item` but
@@ -602,6 +604,10 @@ impl Ty {
602 Ty::Tuple(0, Substs::empty()) 604 Ty::Tuple(0, Substs::empty())
603 } 605 }
604 606
607 pub fn adt_ty(adt: hir_def::AdtId, substs: Substs) -> Ty {
608 Ty::Adt(AdtId(adt), substs)
609 }
610
605 pub fn fn_ptr(sig: CallableSig) -> Self { 611 pub fn fn_ptr(sig: CallableSig) -> Self {
606 Ty::Function(FnPointer { 612 Ty::Function(FnPointer {
607 num_args: sig.params().len(), 613 num_args: sig.params().len(),
@@ -650,9 +656,9 @@ impl Ty {
650 t 656 t
651 } 657 }
652 658
653 pub fn as_adt(&self) -> Option<(AdtId, &Substs)> { 659 pub fn as_adt(&self) -> Option<(hir_def::AdtId, &Substs)> {
654 match self { 660 match self {
655 Ty::Adt(adt_def, parameters) => Some((*adt_def, parameters)), 661 Ty::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),
656 _ => None, 662 _ => None,
657 } 663 }
658 } 664 }
@@ -666,7 +672,7 @@ impl Ty {
666 672
667 pub fn as_generic_def(&self) -> Option<GenericDefId> { 673 pub fn as_generic_def(&self) -> Option<GenericDefId> {
668 match *self { 674 match *self {
669 Ty::Adt(adt, ..) => Some(adt.into()), 675 Ty::Adt(AdtId(adt), ..) => Some(adt.into()),
670 Ty::FnDef(callable, ..) => Some(callable.into()), 676 Ty::FnDef(callable, ..) => Some(callable.into()),
671 Ty::AssociatedType(type_alias, ..) => Some(type_alias.into()), 677 Ty::AssociatedType(type_alias, ..) => Some(type_alias.into()),
672 Ty::ForeignType(type_alias, ..) => Some(type_alias.into()), 678 Ty::ForeignType(type_alias, ..) => Some(type_alias.into()),
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 1b5843d48..5fa83567b 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -24,7 +24,6 @@ use hir_expand::name::Name;
24use la_arena::ArenaMap; 24use la_arena::ArenaMap;
25use smallvec::SmallVec; 25use smallvec::SmallVec;
26use stdx::impl_from; 26use stdx::impl_from;
27use test_utils::mark;
28 27
29use crate::{ 28use crate::{
30 db::HirDatabase, 29 db::HirDatabase,
@@ -760,7 +759,7 @@ fn assoc_type_bindings_from_type_bound<'a>(
760 759
761impl ReturnTypeImplTrait { 760impl ReturnTypeImplTrait {
762 fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self { 761 fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self {
763 mark::hit!(lower_rpit); 762 cov_mark::hit!(lower_rpit);
764 let self_ty = Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)); 763 let self_ty = Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0));
765 let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { 764 let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| {
766 bounds 765 bounds
@@ -935,7 +934,7 @@ impl TraitEnvironment {
935 // add `Self: Trait<T1, T2, ...>` to the environment in trait 934 // add `Self: Trait<T1, T2, ...>` to the environment in trait
936 // function default implementations (and hypothetical code 935 // function default implementations (and hypothetical code
937 // inside consts or type aliases) 936 // inside consts or type aliases)
938 test_utils::mark::hit!(trait_self_implements_self); 937 cov_mark::hit!(trait_self_implements_self);
939 let substs = Substs::type_params(db, trait_id); 938 let substs = Substs::type_params(db, trait_id);
940 let trait_ref = TraitRef { trait_: trait_id, substs }; 939 let trait_ref = TraitRef { trait_: trait_id, substs };
941 let pred = GenericPredicate::Implemented(trait_ref); 940 let pred = GenericPredicate::Implemented(trait_ref);
@@ -1100,7 +1099,7 @@ fn type_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -
1100fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> { 1099fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
1101 let generics = generics(db.upcast(), adt.into()); 1100 let generics = generics(db.upcast(), adt.into());
1102 let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); 1101 let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
1103 Binders::new(substs.len(), Ty::Adt(adt, substs)) 1102 Binders::new(substs.len(), Ty::adt_ty(adt, substs))
1104} 1103}
1105 1104
1106fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> { 1105fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
@@ -1131,8 +1130,8 @@ impl CallableDefId {
1131 let db = db.upcast(); 1130 let db = db.upcast();
1132 match self { 1131 match self {
1133 CallableDefId::FunctionId(f) => f.lookup(db).module(db), 1132 CallableDefId::FunctionId(f) => f.lookup(db).module(db),
1134 CallableDefId::StructId(s) => s.lookup(db).container.module(db), 1133 CallableDefId::StructId(s) => s.lookup(db).container,
1135 CallableDefId::EnumVariantId(e) => e.parent.lookup(db).container.module(db), 1134 CallableDefId::EnumVariantId(e) => e.parent.lookup(db).container,
1136 } 1135 }
1137 .krate() 1136 .krate()
1138 } 1137 }
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index f301a8477..ccc12c075 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -8,8 +8,8 @@ use arrayvec::ArrayVec;
8use base_db::CrateId; 8use base_db::CrateId;
9use chalk_ir::Mutability; 9use chalk_ir::Mutability;
10use hir_def::{ 10use hir_def::{
11 lang_item::LangItemTarget, AdtId, AssocContainerId, AssocItemId, FunctionId, GenericDefId, 11 lang_item::LangItemTarget, AssocContainerId, AssocItemId, FunctionId, GenericDefId, HasModule,
12 HasModule, ImplId, Lookup, ModuleId, TraitId, TypeAliasId, 12 ImplId, Lookup, ModuleId, TraitId, TypeAliasId,
13}; 13};
14use hir_expand::name::Name; 14use hir_expand::name::Name;
15use rustc_hash::{FxHashMap, FxHashSet}; 15use rustc_hash::{FxHashMap, FxHashSet};
@@ -19,8 +19,8 @@ use crate::{
19 db::HirDatabase, 19 db::HirDatabase,
20 primitive::{self, FloatTy, IntTy, UintTy}, 20 primitive::{self, FloatTy, IntTy, UintTy},
21 utils::all_super_traits, 21 utils::all_super_traits,
22 Canonical, DebruijnIndex, FnPointer, FnSig, InEnvironment, Scalar, Substs, TraitEnvironment, 22 AdtId, Canonical, DebruijnIndex, FnPointer, FnSig, InEnvironment, Scalar, Substs,
23 TraitRef, Ty, TypeWalk, 23 TraitEnvironment, TraitRef, Ty, TypeWalk,
24}; 24};
25 25
26/// This is used as a key for indexing impls. 26/// This is used as a key for indexing impls.
@@ -32,7 +32,7 @@ pub enum TyFingerprint {
32 Never, 32 Never,
33 RawPtr(Mutability), 33 RawPtr(Mutability),
34 Scalar(Scalar), 34 Scalar(Scalar),
35 Adt(AdtId), 35 Adt(hir_def::AdtId),
36 Dyn(TraitId), 36 Dyn(TraitId),
37 Tuple(usize), 37 Tuple(usize),
38 ForeignType(TypeAliasId), 38 ForeignType(TypeAliasId),
@@ -50,7 +50,7 @@ impl TyFingerprint {
50 &Ty::Slice(..) => TyFingerprint::Slice, 50 &Ty::Slice(..) => TyFingerprint::Slice,
51 &Ty::Array(..) => TyFingerprint::Array, 51 &Ty::Array(..) => TyFingerprint::Array,
52 &Ty::Scalar(scalar) => TyFingerprint::Scalar(scalar), 52 &Ty::Scalar(scalar) => TyFingerprint::Scalar(scalar),
53 &Ty::Adt(adt, _) => TyFingerprint::Adt(adt), 53 &Ty::Adt(AdtId(adt), _) => TyFingerprint::Adt(adt),
54 &Ty::Tuple(cardinality, _) => TyFingerprint::Tuple(cardinality), 54 &Ty::Tuple(cardinality, _) => TyFingerprint::Tuple(cardinality),
55 &Ty::Raw(mutability, ..) => TyFingerprint::RawPtr(mutability), 55 &Ty::Raw(mutability, ..) => TyFingerprint::RawPtr(mutability),
56 &Ty::ForeignType(alias_id, ..) => TyFingerprint::ForeignType(alias_id), 56 &Ty::ForeignType(alias_id, ..) => TyFingerprint::ForeignType(alias_id),
@@ -231,7 +231,7 @@ impl Ty {
231 let mod_to_crate_ids = |module: ModuleId| Some(std::iter::once(module.krate()).collect()); 231 let mod_to_crate_ids = |module: ModuleId| Some(std::iter::once(module.krate()).collect());
232 232
233 let lang_item_targets = match self { 233 let lang_item_targets = match self {
234 Ty::Adt(def_id, _) => { 234 Ty::Adt(AdtId(def_id), _) => {
235 return mod_to_crate_ids(def_id.module(db.upcast())); 235 return mod_to_crate_ids(def_id.module(db.upcast()));
236 } 236 }
237 Ty::ForeignType(type_alias_id) => { 237 Ty::ForeignType(type_alias_id) => {
@@ -267,7 +267,7 @@ impl Ty {
267 LangItemTarget::ImplDefId(it) => Some(it), 267 LangItemTarget::ImplDefId(it) => Some(it),
268 _ => None, 268 _ => None,
269 }) 269 })
270 .map(|it| it.lookup(db.upcast()).container.module(db.upcast()).krate()) 270 .map(|it| it.lookup(db.upcast()).container.krate())
271 .collect(); 271 .collect();
272 Some(res) 272 Some(res)
273 } 273 }
@@ -588,7 +588,7 @@ fn iterate_inherent_methods(
588 // already happens in `is_valid_candidate` above; if not, we 588 // already happens in `is_valid_candidate` above; if not, we
589 // check it here 589 // check it here
590 if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { 590 if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() {
591 test_utils::mark::hit!(impl_self_type_match_without_receiver); 591 cov_mark::hit!(impl_self_type_match_without_receiver);
592 continue; 592 continue;
593 } 593 }
594 if callback(&self_ty.value, item) { 594 if callback(&self_ty.value, item) {
@@ -715,7 +715,7 @@ fn transform_receiver_ty(
715 .fill_with_unknown() 715 .fill_with_unknown()
716 .build() 716 .build()
717 } 717 }
718 AssocContainerId::ContainerId(_) => unreachable!(), 718 AssocContainerId::ModuleId(_) => unreachable!(),
719 }; 719 };
720 let sig = db.callable_item_signature(function_id.into()); 720 let sig = db.callable_item_signature(function_id.into());
721 Some(sig.value.params()[0].clone().subst_bound_vars(&substs)) 721 Some(sig.value.params()[0].clone().subst_bound_vars(&substs))
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 7386a4e7b..fc770ea60 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -13,7 +13,7 @@ use std::{env, sync::Arc};
13use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; 13use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt};
14use expect_test::Expect; 14use expect_test::Expect;
15use hir_def::{ 15use hir_def::{
16 body::{BodySourceMap, SyntheticSyntax}, 16 body::{Body, BodySourceMap, SyntheticSyntax},
17 child_by_source::ChildBySource, 17 child_by_source::ChildBySource,
18 db::DefDatabase, 18 db::DefDatabase,
19 item_scope::ItemScope, 19 item_scope::ItemScope,
@@ -234,13 +234,13 @@ fn visit_module(
234 let def = it.into(); 234 let def = it.into();
235 cb(def); 235 cb(def);
236 let body = db.body(def); 236 let body = db.body(def);
237 visit_scope(db, crate_def_map, &body.item_scope, cb); 237 visit_body(db, &body, cb);
238 } 238 }
239 AssocItemId::ConstId(it) => { 239 AssocItemId::ConstId(it) => {
240 let def = it.into(); 240 let def = it.into();
241 cb(def); 241 cb(def);
242 let body = db.body(def); 242 let body = db.body(def);
243 visit_scope(db, crate_def_map, &body.item_scope, cb); 243 visit_body(db, &body, cb);
244 } 244 }
245 AssocItemId::TypeAliasId(_) => (), 245 AssocItemId::TypeAliasId(_) => (),
246 } 246 }
@@ -259,19 +259,19 @@ fn visit_module(
259 let def = it.into(); 259 let def = it.into();
260 cb(def); 260 cb(def);
261 let body = db.body(def); 261 let body = db.body(def);
262 visit_scope(db, crate_def_map, &body.item_scope, cb); 262 visit_body(db, &body, cb);
263 } 263 }
264 ModuleDefId::ConstId(it) => { 264 ModuleDefId::ConstId(it) => {
265 let def = it.into(); 265 let def = it.into();
266 cb(def); 266 cb(def);
267 let body = db.body(def); 267 let body = db.body(def);
268 visit_scope(db, crate_def_map, &body.item_scope, cb); 268 visit_body(db, &body, cb);
269 } 269 }
270 ModuleDefId::StaticId(it) => { 270 ModuleDefId::StaticId(it) => {
271 let def = it.into(); 271 let def = it.into();
272 cb(def); 272 cb(def);
273 let body = db.body(def); 273 let body = db.body(def);
274 visit_scope(db, crate_def_map, &body.item_scope, cb); 274 visit_body(db, &body, cb);
275 } 275 }
276 ModuleDefId::TraitId(it) => { 276 ModuleDefId::TraitId(it) => {
277 let trait_data = db.trait_data(it); 277 let trait_data = db.trait_data(it);
@@ -288,6 +288,14 @@ fn visit_module(
288 } 288 }
289 } 289 }
290 } 290 }
291
292 fn visit_body(db: &TestDB, body: &Body, cb: &mut dyn FnMut(DefWithBodyId)) {
293 for def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) {
294 for (mod_id, _) in def_map.modules() {
295 visit_module(db, &def_map, mod_id, cb);
296 }
297 }
298 }
291} 299}
292 300
293fn ellipsize(mut text: String, max_len: usize) -> String { 301fn ellipsize(mut text: String, max_len: usize) -> String {
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs
index 7bc6c79f3..63d9d4e0b 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -1,5 +1,4 @@
1use expect_test::expect; 1use expect_test::expect;
2use test_utils::mark;
3 2
4use super::{check_infer, check_infer_with_mismatches}; 3use super::{check_infer, check_infer_with_mismatches};
5 4
@@ -381,7 +380,7 @@ fn infer_match_second_coerce() {
381 380
382#[test] 381#[test]
383fn coerce_merge_one_by_one1() { 382fn coerce_merge_one_by_one1() {
384 mark::check!(coerce_merge_fail_fallback); 383 cov_mark::check!(coerce_merge_fail_fallback);
385 384
386 check_infer( 385 check_infer(
387 r" 386 r"
@@ -589,7 +588,7 @@ fn coerce_fn_item_to_fn_ptr() {
589 588
590#[test] 589#[test]
591fn coerce_fn_items_in_match_arms() { 590fn coerce_fn_items_in_match_arms() {
592 mark::check!(coerce_fn_reification); 591 cov_mark::check!(coerce_fn_reification);
593 592
594 check_infer_with_mismatches( 593 check_infer_with_mismatches(
595 r" 594 r"
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index a9901d7b8..4e3f9a9b6 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -913,7 +913,7 @@ fn test() { S2.into(); }
913 913
914#[test] 914#[test]
915fn method_resolution_overloaded_method() { 915fn method_resolution_overloaded_method() {
916 test_utils::mark::check!(impl_self_type_match_without_receiver); 916 cov_mark::check!(impl_self_type_match_without_receiver);
917 check_types( 917 check_types(
918 r#" 918 r#"
919struct Wrapper<T>(T); 919struct Wrapper<T>(T);
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 2053d8f56..5da19ba5f 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -1,5 +1,4 @@
1use expect_test::expect; 1use expect_test::expect;
2use test_utils::mark;
3 2
4use super::{check_infer, check_infer_with_mismatches}; 3use super::{check_infer, check_infer_with_mismatches};
5 4
@@ -197,7 +196,7 @@ fn infer_pattern_match_ergonomics() {
197 196
198#[test] 197#[test]
199fn infer_pattern_match_ergonomics_ref() { 198fn infer_pattern_match_ergonomics_ref() {
200 mark::check!(match_ergonomics_ref); 199 cov_mark::check!(match_ergonomics_ref);
201 check_infer( 200 check_infer(
202 r#" 201 r#"
203 fn test() { 202 fn test() {
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index cffe8630b..69314e245 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -1,5 +1,4 @@
1use expect_test::expect; 1use expect_test::expect;
2use test_utils::mark;
3 2
4use super::{check_infer, check_types}; 3use super::{check_infer, check_types};
5 4
@@ -87,8 +86,8 @@ fn bug_651() {
87 86
88#[test] 87#[test]
89fn recursive_vars() { 88fn recursive_vars() {
90 mark::check!(type_var_cycles_resolve_completely); 89 cov_mark::check!(type_var_cycles_resolve_completely);
91 mark::check!(type_var_cycles_resolve_as_possible); 90 cov_mark::check!(type_var_cycles_resolve_as_possible);
92 check_infer( 91 check_infer(
93 r#" 92 r#"
94 fn test() { 93 fn test() {
@@ -166,7 +165,7 @@ fn infer_std_crash_1() {
166 165
167#[test] 166#[test]
168fn infer_std_crash_2() { 167fn infer_std_crash_2() {
169 mark::check!(type_var_resolves_to_int_var); 168 cov_mark::check!(type_var_resolves_to_int_var);
170 // caused "equating two type variables, ...", taken from std 169 // caused "equating two type variables, ...", taken from std
171 check_infer( 170 check_infer(
172 r#" 171 r#"
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 2947857a5..f5069eba5 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -1,5 +1,4 @@
1use expect_test::expect; 1use expect_test::expect;
2use test_utils::mark;
3 2
4use super::{check_infer, check_types}; 3use super::{check_infer, check_types};
5 4
@@ -2314,7 +2313,7 @@ fn generic_default_depending_on_other_type_arg_forward() {
2314 2313
2315#[test] 2314#[test]
2316fn infer_operator_overload() { 2315fn infer_operator_overload() {
2317 mark::check!(infer_expr_inner_binary_operator_overload); 2316 cov_mark::check!(infer_expr_inner_binary_operator_overload);
2318 2317
2319 check_infer( 2318 check_infer(
2320 r#" 2319 r#"
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 1298e5a88..e185b1c0a 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -1,5 +1,4 @@
1use expect_test::expect; 1use expect_test::expect;
2use test_utils::mark;
3 2
4use super::{check_infer, check_infer_with_mismatches, check_types}; 3use super::{check_infer, check_infer_with_mismatches, check_types};
5 4
@@ -319,7 +318,7 @@ fn infer_from_bound_2() {
319 318
320#[test] 319#[test]
321fn trait_default_method_self_bound_implements_trait() { 320fn trait_default_method_self_bound_implements_trait() {
322 mark::check!(trait_self_implements_self); 321 cov_mark::check!(trait_self_implements_self);
323 check_infer( 322 check_infer(
324 r#" 323 r#"
325 trait Trait { 324 trait Trait {
@@ -1189,7 +1188,7 @@ fn impl_trait() {
1189 1188
1190#[test] 1189#[test]
1191fn simple_return_pos_impl_trait() { 1190fn simple_return_pos_impl_trait() {
1192 mark::check!(lower_rpit); 1191 cov_mark::check!(lower_rpit);
1193 check_infer( 1192 check_infer(
1194 r#" 1193 r#"
1195 trait Trait<T> { 1194 trait Trait<T> {
@@ -3175,6 +3174,39 @@ fn f() {
3175} 3174}
3176 3175
3177#[test] 3176#[test]
3177fn trait_in_scope_with_inner_item() {
3178 check_infer(
3179 r#"
3180mod m {
3181 pub trait Tr {
3182 fn method(&self) -> u8 { 0 }
3183 }
3184
3185 impl Tr for () {}
3186}
3187
3188use m::Tr;
3189
3190fn f() {
3191 fn inner() {
3192 ().method();
3193 //^^^^^^^^^^^ u8
3194 }
3195}
3196 "#,
3197 expect![[r#"
3198 46..50 'self': &Self
3199 58..63 '{ 0 }': u8
3200 60..61 '0': u8
3201 115..185 '{ ... } }': ()
3202 132..183 '{ ... }': ()
3203 142..144 '()': ()
3204 142..153 '().method()': u8
3205 "#]],
3206 );
3207}
3208
3209#[test]
3178fn inner_use_in_block() { 3210fn inner_use_in_block() {
3179 check_types( 3211 check_types(
3180 r#" 3212 r#"
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index e513fa8f4..565672b6b 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -315,9 +315,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
315 let id = from_chalk(self.db, trait_id); 315 let id = from_chalk(self.db, trait_id);
316 self.db.trait_data(id).name.to_string() 316 self.db.trait_data(id).name.to_string()
317 } 317 }
318 fn adt_name(&self, adt_id: chalk_ir::AdtId<Interner>) -> String { 318 fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String {
319 let id = from_chalk(self.db, adt_id); 319 match adt_id {
320 match id {
321 hir_def::AdtId::StructId(id) => self.db.struct_data(id).name.to_string(), 320 hir_def::AdtId::StructId(id) => self.db.struct_data(id).name.to_string(),
322 hir_def::AdtId::EnumId(id) => self.db.enum_data(id).name.to_string(), 321 hir_def::AdtId::EnumId(id) => self.db.enum_data(id).name.to_string(),
323 hir_def::AdtId::UnionId(id) => self.db.union_data(id).name.to_string(), 322 hir_def::AdtId::UnionId(id) => self.db.union_data(id).name.to_string(),
@@ -425,7 +424,7 @@ pub(crate) fn trait_datum_query(
425 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); 424 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
426 let flags = rust_ir::TraitFlags { 425 let flags = rust_ir::TraitFlags {
427 auto: trait_data.auto, 426 auto: trait_data.auto,
428 upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate() != krate, 427 upstream: trait_.lookup(db.upcast()).container.krate() != krate,
429 non_enumerable: true, 428 non_enumerable: true,
430 coinductive: false, // only relevant for Chalk testing 429 coinductive: false, // only relevant for Chalk testing
431 // FIXME: set these flags correctly 430 // FIXME: set these flags correctly
@@ -488,8 +487,8 @@ pub(crate) fn struct_datum_query(
488 struct_id: AdtId, 487 struct_id: AdtId,
489) -> Arc<StructDatum> { 488) -> Arc<StructDatum> {
490 debug!("struct_datum {:?}", struct_id); 489 debug!("struct_datum {:?}", struct_id);
491 let adt_id = from_chalk(db, struct_id); 490 let type_ctor = Ty::Adt(struct_id, Substs::empty());
492 let type_ctor = Ty::Adt(adt_id, Substs::empty()); 491 let chalk_ir::AdtId(adt_id) = struct_id;
493 debug!("struct {:?} = {:?}", struct_id, type_ctor); 492 debug!("struct {:?} = {:?}", struct_id, type_ctor);
494 let num_params = generics(db.upcast(), adt_id.into()).len(); 493 let num_params = generics(db.upcast(), adt_id.into()).len();
495 let upstream = adt_id.module(db.upcast()).krate() != krate; 494 let upstream = adt_id.module(db.upcast()).krate() != krate;
@@ -549,7 +548,7 @@ fn impl_def_datum(
549 let generic_params = generics(db.upcast(), impl_id.into()); 548 let generic_params = generics(db.upcast(), impl_id.into());
550 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); 549 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
551 let trait_ = trait_ref.trait_; 550 let trait_ = trait_ref.trait_;
552 let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate() == krate { 551 let impl_type = if impl_id.lookup(db.upcast()).container.krate() == krate {
553 rust_ir::ImplType::Local 552 rust_ir::ImplType::Local
554 } else { 553 } else {
555 rust_ir::ImplType::External 554 rust_ir::ImplType::External
@@ -684,10 +683,9 @@ pub(crate) fn fn_def_variance_query(
684pub(crate) fn adt_variance_query( 683pub(crate) fn adt_variance_query(
685 db: &dyn HirDatabase, 684 db: &dyn HirDatabase,
686 _krate: CrateId, 685 _krate: CrateId,
687 adt_id: AdtId, 686 chalk_ir::AdtId(adt_id): AdtId,
688) -> Variances { 687) -> Variances {
689 let adt: crate::AdtId = from_chalk(db, adt_id); 688 let generic_params = generics(db.upcast(), adt_id.into());
690 let generic_params = generics(db.upcast(), adt.into());
691 Variances::from_iter( 689 Variances::from_iter(
692 &Interner, 690 &Interner,
693 std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()), 691 std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()),
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs
index db1760e6c..3a08b67e9 100644
--- a/crates/hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/hir_ty/src/traits/chalk/mapping.rs
@@ -86,7 +86,7 @@ impl ToChalk for Ty {
86 86
87 Ty::Adt(adt_id, substs) => { 87 Ty::Adt(adt_id, substs) => {
88 let substitution = substs.to_chalk(db); 88 let substitution = substs.to_chalk(db);
89 chalk_ir::TyKind::Adt(chalk_ir::AdtId(adt_id), substitution).intern(&Interner) 89 chalk_ir::TyKind::Adt(adt_id, substitution).intern(&Interner)
90 } 90 }
91 Ty::Alias(AliasTy::Projection(proj_ty)) => { 91 Ty::Alias(AliasTy::Projection(proj_ty)) => {
92 let associated_ty_id = TypeAliasAsAssocType(proj_ty.associated_ty).to_chalk(db); 92 let associated_ty_id = TypeAliasAsAssocType(proj_ty.associated_ty).to_chalk(db);
@@ -183,7 +183,7 @@ impl ToChalk for Ty {
183 Ty::Dyn(predicates) 183 Ty::Dyn(predicates)
184 } 184 }
185 185
186 chalk_ir::TyKind::Adt(struct_id, subst) => Ty::Adt(struct_id.0, from_chalk(db, subst)), 186 chalk_ir::TyKind::Adt(adt_id, subst) => Ty::Adt(adt_id, from_chalk(db, subst)),
187 chalk_ir::TyKind::AssociatedType(type_id, subst) => Ty::AssociatedType( 187 chalk_ir::TyKind::AssociatedType(type_id, subst) => Ty::AssociatedType(
188 from_chalk::<TypeAliasAsAssocType, _>(db, type_id).0, 188 from_chalk::<TypeAliasAsAssocType, _>(db, type_id).0,
189 from_chalk(db, subst), 189 from_chalk(db, subst),
@@ -325,18 +325,6 @@ impl ToChalk for hir_def::ImplId {
325 } 325 }
326} 326}
327 327
328impl ToChalk for hir_def::AdtId {
329 type Chalk = AdtId;
330
331 fn to_chalk(self, _db: &dyn HirDatabase) -> Self::Chalk {
332 chalk_ir::AdtId(self.into())
333 }
334
335 fn from_chalk(_db: &dyn HirDatabase, id: AdtId) -> Self {
336 id.0
337 }
338}
339
340impl ToChalk for CallableDefId { 328impl ToChalk for CallableDefId {
341 type Chalk = FnDefId; 329 type Chalk = FnDefId;
342 330
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index 65b79df0d..7351e4e54 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -259,6 +259,6 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<Generic
259 match container { 259 match container {
260 AssocContainerId::ImplId(it) => Some(it.into()), 260 AssocContainerId::ImplId(it) => Some(it.into()),
261 AssocContainerId::TraitId(it) => Some(it.into()), 261 AssocContainerId::TraitId(it) => Some(it.into()),
262 AssocContainerId::ContainerId(_) => None, 262 AssocContainerId::ModuleId(_) => None,
263 } 263 }
264} 264}
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index f6aaaeda4..107bd8432 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13either = "1.5.3" 14either = "1.5.3"
14indexmap = "1.4.0" 15indexmap = "1.4.0"
15itertools = "0.10.0" 16itertools = "0.10.0"
@@ -26,7 +27,6 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
26ide_db = { path = "../ide_db", version = "0.0.0" } 27ide_db = { path = "../ide_db", version = "0.0.0" }
27cfg = { path = "../cfg", version = "0.0.0" } 28cfg = { path = "../cfg", version = "0.0.0" }
28profile = { path = "../profile", version = "0.0.0" } 29profile = { path = "../profile", version = "0.0.0" }
29test_utils = { path = "../test_utils", version = "0.0.0" }
30ide_assists = { path = "../ide_assists", version = "0.0.0" } 30ide_assists = { path = "../ide_assists", version = "0.0.0" }
31ide_ssr = { path = "../ide_ssr", version = "0.0.0" } 31ide_ssr = { path = "../ide_ssr", version = "0.0.0" }
32ide_completion = { path = "../ide_completion", version = "0.0.0" } 32ide_completion = { path = "../ide_completion", version = "0.0.0" }
@@ -36,4 +36,5 @@ ide_completion = { path = "../ide_completion", version = "0.0.0" }
36hir = { path = "../hir", version = "0.0.0" } 36hir = { path = "../hir", version = "0.0.0" }
37 37
38[dev-dependencies] 38[dev-dependencies]
39test_utils = { path = "../test_utils" }
39expect-test = "1.1" 40expect-test = "1.1"
diff --git a/crates/ide/src/display/short_label.rs b/crates/ide/src/display/short_label.rs
index 84b8883de..2df9266b4 100644
--- a/crates/ide/src/display/short_label.rs
+++ b/crates/ide/src/display/short_label.rs
@@ -71,11 +71,7 @@ impl ShortLabel for ast::TypeAlias {
71 71
72impl ShortLabel for ast::Const { 72impl ShortLabel for ast::Const {
73 fn short_label(&self) -> Option<String> { 73 fn short_label(&self) -> Option<String> {
74 let mut new_buf = short_label_from_ty(self, self.ty(), "const ")?; 74 short_label_from_ty(self, self.ty(), "const ")
75 if let Some(expr) = self.body() {
76 format_to!(new_buf, " = {}", expr.syntax());
77 }
78 Some(new_buf)
79 } 75 }
80} 76}
81 77
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index a9454cfa3..ea45086ce 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1,3 +1,4 @@
1use either::Either;
1use hir::{ 2use hir::{
2 Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, 3 Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource,
3 HirDisplay, Module, ModuleDef, ModuleSource, Semantics, 4 HirDisplay, Module, ModuleDef, ModuleSource, Semantics,
@@ -11,7 +12,6 @@ use ide_db::{
11use itertools::Itertools; 12use itertools::Itertools;
12use stdx::format_to; 13use stdx::format_to;
13use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 14use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
14use test_utils::mark;
15 15
16use crate::{ 16use crate::{
17 display::{macro_label, ShortLabel, TryToNav}, 17 display::{macro_label, ShortLabel, TryToNav},
@@ -193,8 +193,8 @@ fn runnable_action(
193 ModuleDef::Function(func) => { 193 ModuleDef::Function(func) => {
194 let src = func.source(sema.db)?; 194 let src = func.source(sema.db)?;
195 if src.file_id != file_id.into() { 195 if src.file_id != file_id.into() {
196 mark::hit!(hover_macro_generated_struct_fn_doc_comment); 196 cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment);
197 mark::hit!(hover_macro_generated_struct_fn_doc_attr); 197 cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr);
198 return None; 198 return None;
199 } 199 }
200 200
@@ -367,7 +367,7 @@ fn hover_for_definition(
367 .and_then(|fd| hover_for_builtin(fd, it)) 367 .and_then(|fd| hover_for_builtin(fd, it))
368 .or_else(|| Some(Markup::fenced_block(&it.name()))), 368 .or_else(|| Some(Markup::fenced_block(&it.name()))),
369 }, 369 },
370 Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))), 370 Definition::Local(it) => hover_for_local(it, db),
371 Definition::SelfType(impl_def) => { 371 Definition::SelfType(impl_def) => {
372 impl_def.target_ty(db).as_adt().and_then(|adt| match adt { 372 impl_def.target_ty(db).as_adt().and_then(|adt| match adt {
373 Adt::Struct(it) => from_def_source(db, it, mod_path), 373 Adt::Struct(it) => from_def_source(db, it, mod_path),
@@ -406,6 +406,29 @@ fn hover_for_definition(
406 } 406 }
407} 407}
408 408
409fn hover_for_local(it: hir::Local, db: &RootDatabase) -> Option<Markup> {
410 let ty = it.ty(db);
411 let ty = ty.display(db);
412 let is_mut = if it.is_mut(db) { "mut " } else { "" };
413 let desc = match it.source(db).value {
414 Either::Left(ident) => {
415 let name = it.name(db).unwrap();
416 let let_kw = if ident
417 .syntax()
418 .parent()
419 .map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION)
420 {
421 "let "
422 } else {
423 ""
424 };
425 format!("{}{}{}: {}", let_kw, is_mut, name, ty)
426 }
427 Either::Right(_) => format!("{}self: {}", is_mut, ty),
428 };
429 hover_markup(None, Some(desc), None)
430}
431
409fn hover_for_keyword( 432fn hover_for_keyword(
410 sema: &Semantics<RootDatabase>, 433 sema: &Semantics<RootDatabase>,
411 links_in_hover: bool, 434 links_in_hover: bool,
@@ -575,7 +598,7 @@ fn main() {
575 *iter* 598 *iter*
576 599
577 ```rust 600 ```rust
578 Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>> 601 let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
579 ``` 602 ```
580 "#]], 603 "#]],
581 ); 604 );
@@ -799,7 +822,7 @@ fn main() {
799 ``` 822 ```
800 823
801 ```rust 824 ```rust
802 const foo: u32 = 123 825 const foo: u32
803 ``` 826 ```
804 "#]], 827 "#]],
805 ); 828 );
@@ -832,7 +855,7 @@ fn main() {
832 *zz* 855 *zz*
833 856
834 ```rust 857 ```rust
835 Test<i32, u8> 858 let zz: Test<i32, u8>
836 ``` 859 ```
837 "#]], 860 "#]],
838 ); 861 );
@@ -871,7 +894,7 @@ fn main() { let b$0ar = Some(12); }
871 *bar* 894 *bar*
872 895
873 ```rust 896 ```rust
874 Option<i32> 897 let bar: Option<i32>
875 ``` 898 ```
876 "#]], 899 "#]],
877 ); 900 );
@@ -939,7 +962,7 @@ fn main() {
939 *foo* 962 *foo*
940 963
941 ```rust 964 ```rust
942 i32 965 foo: i32
943 ``` 966 ```
944 "#]], 967 "#]],
945 ) 968 )
@@ -953,7 +976,7 @@ fn main() {
953 *foo* 976 *foo*
954 977
955 ```rust 978 ```rust
956 i32 979 foo: i32
957 ``` 980 ```
958 "#]], 981 "#]],
959 ) 982 )
@@ -967,7 +990,7 @@ fn main() {
967 *foo* 990 *foo*
968 991
969 ```rust 992 ```rust
970 i32 993 foo: i32
971 ``` 994 ```
972 "#]], 995 "#]],
973 ) 996 )
@@ -981,7 +1004,7 @@ fn main() {
981 *foo* 1004 *foo*
982 1005
983 ```rust 1006 ```rust
984 i32 1007 foo: i32
985 ``` 1008 ```
986 "#]], 1009 "#]],
987 ) 1010 )
@@ -1001,7 +1024,7 @@ fn main() {
1001 *_x* 1024 *_x*
1002 1025
1003 ```rust 1026 ```rust
1004 impl Deref<Target = u8> + DerefMut<Target = u8> 1027 _x: impl Deref<Target = u8> + DerefMut<Target = u8>
1005 ``` 1028 ```
1006 "#]], 1029 "#]],
1007 ) 1030 )
@@ -1023,7 +1046,7 @@ fn main() { let foo_$0test = Thing::new(); }
1023 *foo_test* 1046 *foo_test*
1024 1047
1025 ```rust 1048 ```rust
1026 Thing 1049 let foo_test: Thing
1027 ``` 1050 ```
1028 "#]], 1051 "#]],
1029 ) 1052 )
@@ -1082,7 +1105,7 @@ fn main() {
1082 ``` 1105 ```
1083 1106
1084 ```rust 1107 ```rust
1085 const C: u32 = 1 1108 const C: u32
1086 ``` 1109 ```
1087 "#]], 1110 "#]],
1088 ) 1111 )
@@ -1183,7 +1206,7 @@ fn y() {
1183 *x* 1206 *x*
1184 1207
1185 ```rust 1208 ```rust
1186 i32 1209 let x: i32
1187 ``` 1210 ```
1188 "#]], 1211 "#]],
1189 ) 1212 )
@@ -1260,7 +1283,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); }
1260 *bar* 1283 *bar*
1261 1284
1262 ```rust 1285 ```rust
1263 u32 1286 bar: u32
1264 ``` 1287 ```
1265 "#]], 1288 "#]],
1266 ); 1289 );
@@ -1278,7 +1301,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); }
1278 *bar* 1301 *bar*
1279 1302
1280 ```rust 1303 ```rust
1281 u32 1304 bar: u32
1282 ``` 1305 ```
1283 "#]], 1306 "#]],
1284 ); 1307 );
@@ -2101,7 +2124,7 @@ pub fn fo$0o() {}
2101 2124
2102 #[test] 2125 #[test]
2103 fn test_hover_macro_generated_struct_fn_doc_comment() { 2126 fn test_hover_macro_generated_struct_fn_doc_comment() {
2104 mark::check!(hover_macro_generated_struct_fn_doc_comment); 2127 cov_mark::check!(hover_macro_generated_struct_fn_doc_comment);
2105 2128
2106 check( 2129 check(
2107 r#" 2130 r#"
@@ -2139,7 +2162,7 @@ fn foo() { let bar = Bar; bar.fo$0o(); }
2139 2162
2140 #[test] 2163 #[test]
2141 fn test_hover_macro_generated_struct_fn_doc_attr() { 2164 fn test_hover_macro_generated_struct_fn_doc_attr() {
2142 mark::check!(hover_macro_generated_struct_fn_doc_attr); 2165 cov_mark::check!(hover_macro_generated_struct_fn_doc_attr);
2143 2166
2144 check( 2167 check(
2145 r#" 2168 r#"
@@ -3303,7 +3326,7 @@ fn main() {
3303 *f* 3326 *f*
3304 3327
3305 ```rust 3328 ```rust
3306 &i32 3329 f: &i32
3307 ``` 3330 ```
3308 "#]], 3331 "#]],
3309 ); 3332 );
@@ -3322,7 +3345,7 @@ impl Foo {
3322 *self* 3345 *self*
3323 3346
3324 ```rust 3347 ```rust
3325 &Foo 3348 self: &Foo
3326 ``` 3349 ```
3327 "#]], 3350 "#]],
3328 ); 3351 );
@@ -3342,7 +3365,7 @@ impl Foo {
3342 *self* 3365 *self*
3343 3366
3344 ```rust 3367 ```rust
3345 Arc<Foo> 3368 self: Arc<Foo>
3346 ``` 3369 ```
3347 "#]], 3370 "#]],
3348 ); 3371 );
@@ -3538,7 +3561,7 @@ fn foo() {
3538 ``` 3561 ```
3539 3562
3540 ```rust 3563 ```rust
3541 const FOO: usize = 3 3564 const FOO: usize
3542 ``` 3565 ```
3543 3566
3544 --- 3567 ---
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index 7fcae13e0..20a920ddb 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -7,7 +7,7 @@ use syntax::{
7 SyntaxKind::{self, USE_TREE, WHITESPACE}, 7 SyntaxKind::{self, USE_TREE, WHITESPACE},
8 SyntaxNode, SyntaxToken, TextRange, TextSize, T, 8 SyntaxNode, SyntaxToken, TextRange, TextSize, T,
9}; 9};
10use test_utils::mark; 10
11use text_edit::{TextEdit, TextEditBuilder}; 11use text_edit::{TextEdit, TextEditBuilder};
12 12
13// Feature: Join Lines 13// Feature: Join Lines
@@ -60,7 +60,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS
60 let mut string_open_quote = false; 60 let mut string_open_quote = false;
61 if let Some(string) = ast::String::cast(token.clone()) { 61 if let Some(string) = ast::String::cast(token.clone()) {
62 if let Some(range) = string.open_quote_text_range() { 62 if let Some(range) = string.open_quote_text_range() {
63 mark::hit!(join_string_literal); 63 cov_mark::hit!(join_string_literal);
64 string_open_quote = range.end() == offset; 64 string_open_quote = range.end() == offset;
65 } 65 }
66 } 66 }
@@ -206,7 +206,7 @@ fn compute_ws(left: SyntaxKind, right: SyntaxKind) -> &'static str {
206#[cfg(test)] 206#[cfg(test)]
207mod tests { 207mod tests {
208 use syntax::SourceFile; 208 use syntax::SourceFile;
209 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range, mark}; 209 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
210 210
211 use super::*; 211 use super::*;
212 212
@@ -786,7 +786,7 @@ fn foo() {
786 786
787 #[test] 787 #[test]
788 fn join_string_literal() { 788 fn join_string_literal() {
789 mark::check!(join_string_literal); 789 cov_mark::check!(join_string_literal);
790 check_join_lines( 790 check_join_lines(
791 r#" 791 r#"
792fn main() { 792fn main() {
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index b600178ee..f83ed65d5 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -478,7 +478,6 @@ impl Analysis {
478 position: FilePosition, 478 position: FilePosition,
479 full_import_path: &str, 479 full_import_path: &str,
480 imported_name: String, 480 imported_name: String,
481 import_for_trait_assoc_item: bool,
482 ) -> Cancelable<Vec<TextEdit>> { 481 ) -> Cancelable<Vec<TextEdit>> {
483 Ok(self 482 Ok(self
484 .with_db(|db| { 483 .with_db(|db| {
@@ -488,7 +487,6 @@ impl Analysis {
488 position, 487 position,
489 full_import_path, 488 full_import_path,
490 imported_name, 489 imported_name,
491 import_for_trait_assoc_item,
492 ) 490 )
493 })? 491 })?
494 .unwrap_or_default()) 492 .unwrap_or_default())
diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs
index 1bfa1439d..000c412d9 100644
--- a/crates/ide/src/matching_brace.rs
+++ b/crates/ide/src/matching_brace.rs
@@ -2,7 +2,6 @@ use syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode},
3 SourceFile, SyntaxKind, TextSize, T, 3 SourceFile, SyntaxKind, TextSize, T,
4}; 4};
5use test_utils::mark;
6 5
7// Feature: Matching Brace 6// Feature: Matching Brace
8// 7//
@@ -28,7 +27,7 @@ pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<Text
28 .next()?; 27 .next()?;
29 let parent = brace_token.parent(); 28 let parent = brace_token.parent();
30 if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { 29 if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) {
31 mark::hit!(pipes_not_braces); 30 cov_mark::hit!(pipes_not_braces);
32 return None; 31 return None;
33 } 32 }
34 let matching_kind = BRACES[brace_idx ^ 1]; 33 let matching_kind = BRACES[brace_idx ^ 1];
@@ -63,7 +62,7 @@ mod tests {
63 do_check("fn main() { $0|x: i32| x * 2;}", "fn main() { |x: i32$0| x * 2;}"); 62 do_check("fn main() { $0|x: i32| x * 2;}", "fn main() { |x: i32$0| x * 2;}");
64 63
65 { 64 {
66 mark::check!(pipes_not_braces); 65 cov_mark::check!(pipes_not_braces);
67 do_check( 66 do_check(
68 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", 67 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }",
69 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", 68 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }",
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs
index ddbaf22b7..03d71b380 100644
--- a/crates/ide/src/parent_module.rs
+++ b/crates/ide/src/parent_module.rs
@@ -5,7 +5,6 @@ use syntax::{
5 algo::find_node_at_offset, 5 algo::find_node_at_offset,
6 ast::{self, AstNode}, 6 ast::{self, AstNode},
7}; 7};
8use test_utils::mark;
9 8
10use crate::NavigationTarget; 9use crate::NavigationTarget;
11 10
@@ -33,7 +32,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
33 .item_list() 32 .item_list()
34 .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset)) 33 .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset))
35 { 34 {
36 mark::hit!(test_resolve_parent_module_on_module_decl); 35 cov_mark::hit!(test_resolve_parent_module_on_module_decl);
37 module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); 36 module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast);
38 } 37 }
39 } 38 }
@@ -64,7 +63,6 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
64#[cfg(test)] 63#[cfg(test)]
65mod tests { 64mod tests {
66 use ide_db::base_db::FileRange; 65 use ide_db::base_db::FileRange;
67 use test_utils::mark;
68 66
69 use crate::fixture; 67 use crate::fixture;
70 68
@@ -92,7 +90,7 @@ $0// empty
92 90
93 #[test] 91 #[test]
94 fn test_resolve_parent_module_on_module_decl() { 92 fn test_resolve_parent_module_on_module_decl() {
95 mark::check!(test_resolve_parent_module_on_module_decl); 93 cov_mark::check!(test_resolve_parent_module_on_module_decl);
96 check( 94 check(
97 r#" 95 r#"
98//- /lib.rs 96//- /lib.rs
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 1919639a3..05c73de88 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -14,7 +14,7 @@ use syntax::{
14 ast::{self, NameOwner}, 14 ast::{self, NameOwner},
15 lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T, 15 lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T,
16}; 16};
17use test_utils::mark; 17
18use text_edit::TextEdit; 18use text_edit::TextEdit;
19 19
20use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; 20use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange};
@@ -226,34 +226,36 @@ fn rename_reference(
226 | (IdentifierKind::Ident, _) 226 | (IdentifierKind::Ident, _)
227 if def_is_lbl_or_lt => 227 if def_is_lbl_or_lt =>
228 { 228 {
229 mark::hit!(rename_not_a_lifetime_ident_ref); 229 cov_mark::hit!(rename_not_a_lifetime_ident_ref);
230 bail!("Invalid name `{}`: not a lifetime identifier", new_name) 230 bail!("Invalid name `{}`: not a lifetime identifier", new_name)
231 } 231 }
232 (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => mark::hit!(rename_lifetime), 232 (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => cov_mark::hit!(rename_lifetime),
233 (IdentifierKind::Lifetime, _) => { 233 (IdentifierKind::Lifetime, _) => {
234 mark::hit!(rename_not_an_ident_ref); 234 cov_mark::hit!(rename_not_an_ident_ref);
235 bail!("Invalid name `{}`: not an identifier", new_name) 235 bail!("Invalid name `{}`: not an identifier", new_name)
236 } 236 }
237 (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { 237 (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => {
238 // no-op 238 // no-op
239 mark::hit!(rename_self_to_self); 239 cov_mark::hit!(rename_self_to_self);
240 return Ok(SourceChange::default()); 240 return Ok(SourceChange::default());
241 } 241 }
242 (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { 242 (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => {
243 mark::hit!(rename_self_to_param); 243 cov_mark::hit!(rename_self_to_param);
244 return rename_self_to_param(sema, local, new_name, ident_kind); 244 return rename_self_to_param(sema, local, new_name, ident_kind);
245 } 245 }
246 (IdentifierKind::ToSelf, Definition::Local(local)) => { 246 (IdentifierKind::ToSelf, Definition::Local(local)) => {
247 mark::hit!(rename_to_self); 247 cov_mark::hit!(rename_to_self);
248 return rename_to_self(sema, local); 248 return rename_to_self(sema, local);
249 } 249 }
250 (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), 250 (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name),
251 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), 251 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => {
252 cov_mark::hit!(rename_ident)
253 }
252 } 254 }
253 255
254 let usages = def.usages(sema).all(); 256 let usages = def.usages(sema).all();
255 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { 257 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore {
256 mark::hit!(rename_underscore_multiple); 258 cov_mark::hit!(rename_underscore_multiple);
257 bail!("Cannot rename reference to `_` as it is being referenced multiple times"); 259 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
258 } 260 }
259 let mut source_change = SourceChange::default(); 261 let mut source_change = SourceChange::default();
@@ -444,7 +446,7 @@ fn source_edit_from_name_ref(
444 (Some(field_name), Some(init)) => { 446 (Some(field_name), Some(init)) => {
445 if field_name == *name_ref { 447 if field_name == *name_ref {
446 if init.text() == new_name { 448 if init.text() == new_name {
447 mark::hit!(test_rename_field_put_init_shorthand); 449 cov_mark::hit!(test_rename_field_put_init_shorthand);
448 // same names, we can use a shorthand here instead. 450 // same names, we can use a shorthand here instead.
449 // we do not want to erase attributes hence this range start 451 // we do not want to erase attributes hence this range start
450 let s = field_name.syntax().text_range().start(); 452 let s = field_name.syntax().text_range().start();
@@ -453,7 +455,7 @@ fn source_edit_from_name_ref(
453 } 455 }
454 } else if init == *name_ref { 456 } else if init == *name_ref {
455 if field_name.text() == new_name { 457 if field_name.text() == new_name {
456 mark::hit!(test_rename_local_put_init_shorthand); 458 cov_mark::hit!(test_rename_local_put_init_shorthand);
457 // same names, we can use a shorthand here instead. 459 // same names, we can use a shorthand here instead.
458 // we do not want to erase attributes hence this range start 460 // we do not want to erase attributes hence this range start
459 let s = field_name.syntax().text_range().start(); 461 let s = field_name.syntax().text_range().start();
@@ -467,12 +469,12 @@ fn source_edit_from_name_ref(
467 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the 469 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
468 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 470 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
469 (None, Some(_)) if matches!(def, Definition::Field(_)) => { 471 (None, Some(_)) if matches!(def, Definition::Field(_)) => {
470 mark::hit!(test_rename_field_in_field_shorthand); 472 cov_mark::hit!(test_rename_field_in_field_shorthand);
471 let s = name_ref.syntax().text_range().start(); 473 let s = name_ref.syntax().text_range().start();
472 Some((TextRange::empty(s), format!("{}: ", new_name))) 474 Some((TextRange::empty(s), format!("{}: ", new_name)))
473 } 475 }
474 (None, Some(_)) if matches!(def, Definition::Local(_)) => { 476 (None, Some(_)) if matches!(def, Definition::Local(_)) => {
475 mark::hit!(test_rename_local_in_field_shorthand); 477 cov_mark::hit!(test_rename_local_in_field_shorthand);
476 let s = name_ref.syntax().text_range().end(); 478 let s = name_ref.syntax().text_range().end();
477 Some((TextRange::empty(s), format!(": {}", new_name))) 479 Some((TextRange::empty(s), format!(": {}", new_name)))
478 } 480 }
@@ -486,7 +488,7 @@ fn source_edit_from_name_ref(
486 (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { 488 (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => {
487 // field name is being renamed 489 // field name is being renamed
488 if pat.name().map_or(false, |it| it.text() == new_name) { 490 if pat.name().map_or(false, |it| it.text() == new_name) {
489 mark::hit!(test_rename_field_put_init_shorthand_pat); 491 cov_mark::hit!(test_rename_field_put_init_shorthand_pat);
490 // same names, we can use a shorthand here instead/ 492 // same names, we can use a shorthand here instead/
491 // we do not want to erase attributes hence this range start 493 // we do not want to erase attributes hence this range start
492 let s = field_name.syntax().text_range().start(); 494 let s = field_name.syntax().text_range().start();
@@ -538,7 +540,7 @@ fn source_edit_from_def(
538mod tests { 540mod tests {
539 use expect_test::{expect, Expect}; 541 use expect_test::{expect, Expect};
540 use stdx::trim_indent; 542 use stdx::trim_indent;
541 use test_utils::{assert_eq_text, mark}; 543 use test_utils::assert_eq_text;
542 use text_edit::TextEdit; 544 use text_edit::TextEdit;
543 545
544 use crate::{fixture, FileId}; 546 use crate::{fixture, FileId};
@@ -627,7 +629,7 @@ mod tests {
627 629
628 #[test] 630 #[test]
629 fn test_rename_to_invalid_identifier_lifetime() { 631 fn test_rename_to_invalid_identifier_lifetime() {
630 mark::check!(rename_not_an_ident_ref); 632 cov_mark::check!(rename_not_an_ident_ref);
631 check( 633 check(
632 "'foo", 634 "'foo",
633 r#"fn main() { let i$0 = 1; }"#, 635 r#"fn main() { let i$0 = 1; }"#,
@@ -637,7 +639,7 @@ mod tests {
637 639
638 #[test] 640 #[test]
639 fn test_rename_to_invalid_identifier_lifetime2() { 641 fn test_rename_to_invalid_identifier_lifetime2() {
640 mark::check!(rename_not_a_lifetime_ident_ref); 642 cov_mark::check!(rename_not_a_lifetime_ident_ref);
641 check( 643 check(
642 "foo", 644 "foo",
643 r#"fn main<'a>(_: &'a$0 ()) {}"#, 645 r#"fn main<'a>(_: &'a$0 ()) {}"#,
@@ -647,7 +649,7 @@ mod tests {
647 649
648 #[test] 650 #[test]
649 fn test_rename_to_underscore_invalid() { 651 fn test_rename_to_underscore_invalid() {
650 mark::check!(rename_underscore_multiple); 652 cov_mark::check!(rename_underscore_multiple);
651 check( 653 check(
652 "_", 654 "_",
653 r#"fn main(foo$0: ()) {foo;}"#, 655 r#"fn main(foo$0: ()) {foo;}"#,
@@ -666,7 +668,7 @@ mod tests {
666 668
667 #[test] 669 #[test]
668 fn test_rename_for_local() { 670 fn test_rename_for_local() {
669 mark::check!(rename_ident); 671 cov_mark::check!(rename_ident);
670 check( 672 check(
671 "k", 673 "k",
672 r#" 674 r#"
@@ -829,7 +831,7 @@ impl Foo {
829 831
830 #[test] 832 #[test]
831 fn test_rename_field_in_field_shorthand() { 833 fn test_rename_field_in_field_shorthand() {
832 mark::check!(test_rename_field_in_field_shorthand); 834 cov_mark::check!(test_rename_field_in_field_shorthand);
833 check( 835 check(
834 "j", 836 "j",
835 r#" 837 r#"
@@ -855,7 +857,7 @@ impl Foo {
855 857
856 #[test] 858 #[test]
857 fn test_rename_local_in_field_shorthand() { 859 fn test_rename_local_in_field_shorthand() {
858 mark::check!(test_rename_local_in_field_shorthand); 860 cov_mark::check!(test_rename_local_in_field_shorthand);
859 check( 861 check(
860 "j", 862 "j",
861 r#" 863 r#"
@@ -1261,7 +1263,7 @@ fn foo(f: foo::Foo) {
1261 1263
1262 #[test] 1264 #[test]
1263 fn test_parameter_to_self() { 1265 fn test_parameter_to_self() {
1264 mark::check!(rename_to_self); 1266 cov_mark::check!(rename_to_self);
1265 check( 1267 check(
1266 "self", 1268 "self",
1267 r#" 1269 r#"
@@ -1401,7 +1403,7 @@ impl Foo {
1401 1403
1402 #[test] 1404 #[test]
1403 fn test_owned_self_to_parameter() { 1405 fn test_owned_self_to_parameter() {
1404 mark::check!(rename_self_to_param); 1406 cov_mark::check!(rename_self_to_param);
1405 check( 1407 check(
1406 "foo", 1408 "foo",
1407 r#" 1409 r#"
@@ -1454,7 +1456,7 @@ impl Foo {
1454 1456
1455 #[test] 1457 #[test]
1456 fn test_rename_field_put_init_shorthand() { 1458 fn test_rename_field_put_init_shorthand() {
1457 mark::check!(test_rename_field_put_init_shorthand); 1459 cov_mark::check!(test_rename_field_put_init_shorthand);
1458 check( 1460 check(
1459 "bar", 1461 "bar",
1460 r#" 1462 r#"
@@ -1476,7 +1478,7 @@ fn foo(bar: i32) -> Foo {
1476 1478
1477 #[test] 1479 #[test]
1478 fn test_rename_local_put_init_shorthand() { 1480 fn test_rename_local_put_init_shorthand() {
1479 mark::check!(test_rename_local_put_init_shorthand); 1481 cov_mark::check!(test_rename_local_put_init_shorthand);
1480 check( 1482 check(
1481 "i", 1483 "i",
1482 r#" 1484 r#"
@@ -1498,7 +1500,7 @@ fn foo(i: i32) -> Foo {
1498 1500
1499 #[test] 1501 #[test]
1500 fn test_struct_field_pat_into_shorthand() { 1502 fn test_struct_field_pat_into_shorthand() {
1501 mark::check!(test_rename_field_put_init_shorthand_pat); 1503 cov_mark::check!(test_rename_field_put_init_shorthand_pat);
1502 check( 1504 check(
1503 "baz", 1505 "baz",
1504 r#" 1506 r#"
@@ -1610,7 +1612,7 @@ fn foo(foo: Foo) {
1610 1612
1611 #[test] 1613 #[test]
1612 fn test_rename_lifetimes() { 1614 fn test_rename_lifetimes() {
1613 mark::check!(rename_lifetime); 1615 cov_mark::check!(rename_lifetime);
1614 check( 1616 check(
1615 "'yeeee", 1617 "'yeeee",
1616 r#" 1618 r#"
@@ -1698,7 +1700,7 @@ fn foo<'a>() -> &'a () {
1698 1700
1699 #[test] 1701 #[test]
1700 fn test_self_to_self() { 1702 fn test_self_to_self() {
1701 mark::check!(rename_self_to_self); 1703 cov_mark::check!(rename_self_to_self);
1702 check( 1704 check(
1703 "self", 1705 "self",
1704 r#" 1706 r#"
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 1e7baed20..280565563 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -9,7 +9,6 @@ use syntax::{
9 ast::{self, AstNode, AttrsOwner}, 9 ast::{self, AstNode, AttrsOwner},
10 match_ast, SyntaxNode, 10 match_ast, SyntaxNode,
11}; 11};
12use test_utils::mark;
13 12
14use crate::{ 13use crate::{
15 display::{ToNav, TryToNav}, 14 display::{ToNav, TryToNav},
@@ -130,7 +129,9 @@ fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module
130 if let hir::ModuleDef::Module(submodule) = def { 129 if let hir::ModuleDef::Module(submodule) = def {
131 match submodule.definition_source(sema.db).value { 130 match submodule.definition_source(sema.db).value {
132 hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule), 131 hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule),
133 hir::ModuleSource::SourceFile(_) => mark::hit!(dont_recurse_in_outline_submodules), 132 hir::ModuleSource::SourceFile(_) => {
133 cov_mark::hit!(dont_recurse_in_outline_submodules)
134 }
134 hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable 135 hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable
135 } 136 }
136 } 137 }
@@ -189,7 +190,7 @@ pub(crate) fn doc_owner_to_def(
189) -> Option<Definition> { 190) -> Option<Definition> {
190 let res: hir::ModuleDef = match_ast! { 191 let res: hir::ModuleDef = match_ast! {
191 match item { 192 match item {
192 ast::SourceFile(it) => sema.scope(&item).module()?.into(), 193 ast::SourceFile(_it) => sema.scope(&item).module()?.into(),
193 ast::Fn(it) => sema.to_def(&it)?.into(), 194 ast::Fn(it) => sema.to_def(&it)?.into(),
194 ast::Struct(it) => sema.to_def(&it)?.into(), 195 ast::Struct(it) => sema.to_def(&it)?.into(),
195 ast::Enum(it) => sema.to_def(&it)?.into(), 196 ast::Enum(it) => sema.to_def(&it)?.into(),
@@ -328,7 +329,6 @@ fn has_test_function_or_multiple_test_submodules(
328#[cfg(test)] 329#[cfg(test)]
329mod tests { 330mod tests {
330 use expect_test::{expect, Expect}; 331 use expect_test::{expect, Expect};
331 use test_utils::mark;
332 332
333 use crate::fixture; 333 use crate::fixture;
334 334
@@ -1056,7 +1056,7 @@ mod tests {
1056 1056
1057 #[test] 1057 #[test]
1058 fn dont_recurse_in_outline_submodules() { 1058 fn dont_recurse_in_outline_submodules() {
1059 mark::check!(dont_recurse_in_outline_submodules); 1059 cov_mark::check!(dont_recurse_in_outline_submodules);
1060 check( 1060 check(
1061 r#" 1061 r#"
1062//- /lib.rs 1062//- /lib.rs
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 24fcbb584..b0cfdd8b7 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -330,10 +330,11 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
330 HlTag::Symbol(SymbolKind::Local) 330 HlTag::Symbol(SymbolKind::Local)
331 }; 331 };
332 let mut h = Highlight::new(tag); 332 let mut h = Highlight::new(tag);
333 if local.is_mut(db) || local.ty(db).is_mutable_reference() { 333 let ty = local.ty(db);
334 if local.is_mut(db) || ty.is_mutable_reference() {
334 h |= HlMod::Mutable; 335 h |= HlMod::Mutable;
335 } 336 }
336 if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) { 337 if ty.as_callable(db).is_some() || ty.impls_fnonce(db) {
337 h |= HlMod::Callable; 338 h |= HlMod::Callable;
338 } 339 }
339 return h; 340 return h;
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index 63cd51b69..978c479de 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -9,7 +9,7 @@ use syntax::{
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxToken, TextRange, TextSize, TokenAtOffset, 10 SyntaxToken, TextRange, TextSize, TokenAtOffset,
11}; 11};
12use test_utils::mark; 12
13use text_edit::TextEdit; 13use text_edit::TextEdit;
14 14
15// Feature: On Enter 15// Feature: On Enter
@@ -55,7 +55,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text
55 // Continuing single-line non-doc comments (like this one :) ) is annoying 55 // Continuing single-line non-doc comments (like this one :) ) is annoying
56 if prefix == "//" && comment_range.end() == position.offset { 56 if prefix == "//" && comment_range.end() == position.offset {
57 if comment.text().ends_with(' ') { 57 if comment.text().ends_with(' ') {
58 mark::hit!(continues_end_of_line_comment_with_space); 58 cov_mark::hit!(continues_end_of_line_comment_with_space);
59 remove_trailing_whitespace = true; 59 remove_trailing_whitespace = true;
60 } else if !followed_by_comment(&comment) { 60 } else if !followed_by_comment(&comment) {
61 return None; 61 return None;
@@ -109,7 +109,7 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
109#[cfg(test)] 109#[cfg(test)]
110mod tests { 110mod tests {
111 use stdx::trim_indent; 111 use stdx::trim_indent;
112 use test_utils::{assert_eq_text, mark}; 112 use test_utils::assert_eq_text;
113 113
114 use crate::fixture; 114 use crate::fixture;
115 115
@@ -238,7 +238,7 @@ fn main() {
238 238
239 #[test] 239 #[test]
240 fn continues_end_of_line_comment_with_space() { 240 fn continues_end_of_line_comment_with_space() {
241 mark::check!(continues_end_of_line_comment_with_space); 241 cov_mark::check!(continues_end_of_line_comment_with_space);
242 do_check( 242 do_check(
243 r#" 243 r#"
244fn main() { 244fn main() {
diff --git a/crates/ide_assists/Cargo.toml b/crates/ide_assists/Cargo.toml
index a34bdd6c3..dd9aa27c6 100644
--- a/crates/ide_assists/Cargo.toml
+++ b/crates/ide_assists/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
14itertools = "0.10.0" 15itertools = "0.10.0"
15either = "1.6.1" 16either = "1.6.1"
@@ -20,7 +21,7 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
20profile = { path = "../profile", version = "0.0.0" } 21profile = { path = "../profile", version = "0.0.0" }
21ide_db = { path = "../ide_db", version = "0.0.0" } 22ide_db = { path = "../ide_db", version = "0.0.0" }
22hir = { path = "../hir", version = "0.0.0" } 23hir = { path = "../hir", version = "0.0.0" }
23test_utils = { path = "../test_utils", version = "0.0.0" }
24 24
25[dev-dependencies] 25[dev-dependencies]
26test_utils = { path = "../test_utils" }
26expect-test = "1.1" 27expect-test = "1.1"
diff --git a/crates/ide_assists/src/handlers/add_turbo_fish.rs b/crates/ide_assists/src/handlers/add_turbo_fish.rs
index a08b55ebb..3b6efbab4 100644
--- a/crates/ide_assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ide_assists/src/handlers/add_turbo_fish.rs
@@ -1,6 +1,5 @@
1use ide_db::defs::{Definition, NameRefClass}; 1use ide_db::defs::{Definition, NameRefClass};
2use syntax::{ast, AstNode, SyntaxKind, T}; 2use syntax::{ast, AstNode, SyntaxKind, T};
3use test_utils::mark;
4 3
5use crate::{ 4use crate::{
6 assist_context::{AssistContext, Assists}, 5 assist_context::{AssistContext, Assists},
@@ -30,13 +29,13 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
30 if arg_list.args().count() > 0 { 29 if arg_list.args().count() > 0 {
31 return None; 30 return None;
32 } 31 }
33 mark::hit!(add_turbo_fish_after_call); 32 cov_mark::hit!(add_turbo_fish_after_call);
34 mark::hit!(add_type_ascription_after_call); 33 cov_mark::hit!(add_type_ascription_after_call);
35 arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) 34 arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT)
36 })?; 35 })?;
37 let next_token = ident.next_token()?; 36 let next_token = ident.next_token()?;
38 if next_token.kind() == T![::] { 37 if next_token.kind() == T![::] {
39 mark::hit!(add_turbo_fish_one_fish_is_enough); 38 cov_mark::hit!(add_turbo_fish_one_fish_is_enough);
40 return None; 39 return None;
41 } 40 }
42 let name_ref = ast::NameRef::cast(ident.parent())?; 41 let name_ref = ast::NameRef::cast(ident.parent())?;
@@ -50,7 +49,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
50 }; 49 };
51 let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); 50 let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
52 if generics.is_empty() { 51 if generics.is_empty() {
53 mark::hit!(add_turbo_fish_non_generic); 52 cov_mark::hit!(add_turbo_fish_non_generic);
54 return None; 53 return None;
55 } 54 }
56 55
@@ -67,7 +66,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
67 }, 66 },
68 )? 67 )?
69 } else { 68 } else {
70 mark::hit!(add_type_ascription_already_typed); 69 cov_mark::hit!(add_type_ascription_already_typed);
71 } 70 }
72 } 71 }
73 72
@@ -87,7 +86,6 @@ mod tests {
87 use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable}; 86 use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable};
88 87
89 use super::*; 88 use super::*;
90 use test_utils::mark;
91 89
92 #[test] 90 #[test]
93 fn add_turbo_fish_function() { 91 fn add_turbo_fish_function() {
@@ -110,7 +108,7 @@ fn main() {
110 108
111 #[test] 109 #[test]
112 fn add_turbo_fish_after_call() { 110 fn add_turbo_fish_after_call() {
113 mark::check!(add_turbo_fish_after_call); 111 cov_mark::check!(add_turbo_fish_after_call);
114 check_assist( 112 check_assist(
115 add_turbo_fish, 113 add_turbo_fish,
116 r#" 114 r#"
@@ -155,7 +153,7 @@ fn main() {
155 153
156 #[test] 154 #[test]
157 fn add_turbo_fish_one_fish_is_enough() { 155 fn add_turbo_fish_one_fish_is_enough() {
158 mark::check!(add_turbo_fish_one_fish_is_enough); 156 cov_mark::check!(add_turbo_fish_one_fish_is_enough);
159 check_assist_not_applicable( 157 check_assist_not_applicable(
160 add_turbo_fish, 158 add_turbo_fish,
161 r#" 159 r#"
@@ -169,7 +167,7 @@ fn main() {
169 167
170 #[test] 168 #[test]
171 fn add_turbo_fish_non_generic() { 169 fn add_turbo_fish_non_generic() {
172 mark::check!(add_turbo_fish_non_generic); 170 cov_mark::check!(add_turbo_fish_non_generic);
173 check_assist_not_applicable( 171 check_assist_not_applicable(
174 add_turbo_fish, 172 add_turbo_fish,
175 r#" 173 r#"
@@ -203,7 +201,7 @@ fn main() {
203 201
204 #[test] 202 #[test]
205 fn add_type_ascription_after_call() { 203 fn add_type_ascription_after_call() {
206 mark::check!(add_type_ascription_after_call); 204 cov_mark::check!(add_type_ascription_after_call);
207 check_assist_by_label( 205 check_assist_by_label(
208 add_turbo_fish, 206 add_turbo_fish,
209 r#" 207 r#"
@@ -250,7 +248,7 @@ fn main() {
250 248
251 #[test] 249 #[test]
252 fn add_type_ascription_already_typed() { 250 fn add_type_ascription_already_typed() {
253 mark::check!(add_type_ascription_already_typed); 251 cov_mark::check!(add_type_ascription_already_typed);
254 check_assist( 252 check_assist(
255 add_turbo_fish, 253 add_turbo_fish,
256 r#" 254 r#"
diff --git a/crates/ide_assists/src/handlers/apply_demorgan.rs b/crates/ide_assists/src/handlers/apply_demorgan.rs
index 128b1eb56..a1c339603 100644
--- a/crates/ide_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide_assists/src/handlers/apply_demorgan.rs
@@ -1,5 +1,4 @@
1use syntax::ast::{self, AstNode}; 1use syntax::ast::{self, AstNode};
2use test_utils::mark;
3 2
4use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; 3use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
5 4
@@ -64,10 +63,10 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
64 edit.replace(lhs_range, not_lhs.syntax().text()); 63 edit.replace(lhs_range, not_lhs.syntax().text());
65 edit.replace(rhs_range, not_rhs.syntax().text()); 64 edit.replace(rhs_range, not_rhs.syntax().text());
66 if let Some(neg_expr) = neg_expr { 65 if let Some(neg_expr) = neg_expr {
67 mark::hit!(demorgan_double_negation); 66 cov_mark::hit!(demorgan_double_negation);
68 edit.replace(neg_expr.op_token().unwrap().text_range(), ""); 67 edit.replace(neg_expr.op_token().unwrap().text_range(), "");
69 } else { 68 } else {
70 mark::hit!(demorgan_double_parens); 69 cov_mark::hit!(demorgan_double_parens);
71 edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!("); 70 edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!(");
72 } 71 }
73 } else { 72 } else {
@@ -90,7 +89,6 @@ fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> {
90#[cfg(test)] 89#[cfg(test)]
91mod tests { 90mod tests {
92 use ide_db::helpers::FamousDefs; 91 use ide_db::helpers::FamousDefs;
93 use test_utils::mark;
94 92
95 use super::*; 93 use super::*;
96 94
@@ -188,13 +186,13 @@ fn f() {
188 186
189 #[test] 187 #[test]
190 fn demorgan_doesnt_double_negation() { 188 fn demorgan_doesnt_double_negation() {
191 mark::check!(demorgan_double_negation); 189 cov_mark::check!(demorgan_double_negation);
192 check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }") 190 check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }")
193 } 191 }
194 192
195 #[test] 193 #[test]
196 fn demorgan_doesnt_double_parens() { 194 fn demorgan_doesnt_double_parens() {
197 mark::check!(demorgan_double_parens); 195 cov_mark::check!(demorgan_double_parens);
198 check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }") 196 check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }")
199 } 197 }
200} 198}
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index dc38f90e9..7caee8df0 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -1,7 +1,7 @@
1use ide_db::helpers::{ 1use ide_db::helpers::{
2 import_assets::{ImportAssets, ImportCandidate}, 2 import_assets::{ImportAssets, ImportCandidate},
3 insert_use::{insert_use, ImportScope}, 3 insert_use::{insert_use, ImportScope},
4 mod_path_to_ast, 4 item_name, mod_path_to_ast,
5}; 5};
6use syntax::{ast, AstNode, SyntaxNode}; 6use syntax::{ast, AstNode, SyntaxNode};
7 7
@@ -92,15 +92,19 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
92 let range = ctx.sema.original_range(&syntax_under_caret).range; 92 let range = ctx.sema.original_range(&syntax_under_caret).range;
93 let group = import_group_message(import_assets.import_candidate()); 93 let group = import_group_message(import_assets.import_candidate());
94 let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?; 94 let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?;
95 for (import, _) in proposed_imports { 95 for import in proposed_imports {
96 let name = match item_name(ctx.db(), import.original_item) {
97 Some(name) => name,
98 None => continue,
99 };
96 acc.add_group( 100 acc.add_group(
97 &group, 101 &group,
98 AssistId("auto_import", AssistKind::QuickFix), 102 AssistId("auto_import", AssistKind::QuickFix),
99 format!("Import `{}`", &import), 103 format!("Import `{}`", name),
100 range, 104 range,
101 |builder| { 105 |builder| {
102 let rewriter = 106 let rewriter =
103 insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use.merge); 107 insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use);
104 builder.rewrite(rewriter); 108 builder.rewrite(rewriter);
105 }, 109 },
106 ); 110 );
@@ -126,10 +130,10 @@ fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel {
126 let name = match import_candidate { 130 let name = match import_candidate {
127 ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()), 131 ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()),
128 ImportCandidate::TraitAssocItem(candidate) => { 132 ImportCandidate::TraitAssocItem(candidate) => {
129 format!("Import a trait for item {}", candidate.name.text()) 133 format!("Import a trait for item {}", candidate.assoc_item_name.text())
130 } 134 }
131 ImportCandidate::TraitMethod(candidate) => { 135 ImportCandidate::TraitMethod(candidate) => {
132 format!("Import a trait for method {}", candidate.name.text()) 136 format!("Import a trait for method {}", candidate.assoc_item_name.text())
133 } 137 }
134 }; 138 };
135 GroupLabel(name) 139 GroupLabel(name)
@@ -222,41 +226,6 @@ mod tests {
222 } 226 }
223 227
224 #[test] 228 #[test]
225 fn auto_imports_are_merged() {
226 check_assist(
227 auto_import,
228 r"
229 use PubMod::PubStruct1;
230
231 struct Test {
232 test: Pub$0Struct2<u8>,
233 }
234
235 pub mod PubMod {
236 pub struct PubStruct1;
237 pub struct PubStruct2<T> {
238 _t: T,
239 }
240 }
241 ",
242 r"
243 use PubMod::{PubStruct1, PubStruct2};
244
245 struct Test {
246 test: PubStruct2<u8>,
247 }
248
249 pub mod PubMod {
250 pub struct PubStruct1;
251 pub struct PubStruct2<T> {
252 _t: T,
253 }
254 }
255 ",
256 );
257 }
258
259 #[test]
260 fn applicable_when_found_multiple_imports() { 229 fn applicable_when_found_multiple_imports() {
261 check_assist( 230 check_assist(
262 auto_import, 231 auto_import,
diff --git a/crates/ide_assists/src/handlers/change_visibility.rs b/crates/ide_assists/src/handlers/change_visibility.rs
index ac8c44124..ec99a5505 100644
--- a/crates/ide_assists/src/handlers/change_visibility.rs
+++ b/crates/ide_assists/src/handlers/change_visibility.rs
@@ -4,7 +4,6 @@ use syntax::{
4 SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY}, 4 SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY},
5 T, 5 T,
6}; 6};
7use test_utils::mark;
8 7
9use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; 8use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
10 9
@@ -56,7 +55,7 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
56 } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { 55 } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() {
57 let field = field_name.syntax().ancestors().find_map(ast::RecordField::cast)?; 56 let field = field_name.syntax().ancestors().find_map(ast::RecordField::cast)?;
58 if field.name()? != field_name { 57 if field.name()? != field_name {
59 mark::hit!(change_visibility_field_false_positive); 58 cov_mark::hit!(change_visibility_field_false_positive);
60 return None; 59 return None;
61 } 60 }
62 if field.visibility().is_some() { 61 if field.visibility().is_some() {
@@ -110,8 +109,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
110 109
111#[cfg(test)] 110#[cfg(test)]
112mod tests { 111mod tests {
113 use test_utils::mark;
114
115 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 112 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
116 113
117 use super::*; 114 use super::*;
@@ -139,7 +136,7 @@ mod tests {
139 136
140 #[test] 137 #[test]
141 fn change_visibility_field_false_positive() { 138 fn change_visibility_field_false_positive() {
142 mark::check!(change_visibility_field_false_positive); 139 cov_mark::check!(change_visibility_field_false_positive);
143 check_assist_not_applicable( 140 check_assist_not_applicable(
144 change_visibility, 141 change_visibility,
145 r"struct S { field: [(); { let $0x = ();}] }", 142 r"struct S { field: [(); { let $0x = ();}] }",
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 9f34cc725..dd4501709 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -20,7 +20,6 @@ use syntax::{
20 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, 20 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR},
21 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, 21 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T,
22}; 22};
23use test_utils::mark;
24 23
25use crate::{ 24use crate::{
26 assist_context::{AssistContext, Assists}, 25 assist_context::{AssistContext, Assists},
@@ -59,7 +58,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
59 58
60 let node = ctx.covering_element(); 59 let node = ctx.covering_element();
61 if node.kind() == COMMENT { 60 if node.kind() == COMMENT {
62 mark::hit!(extract_function_in_comment_is_not_applicable); 61 cov_mark::hit!(extract_function_in_comment_is_not_applicable);
63 return None; 62 return None;
64 } 63 }
65 64
@@ -112,7 +111,10 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
112 111
113 let fn_def = format_function(ctx, module, &fun, old_indent, new_indent); 112 let fn_def = format_function(ctx, module, &fun, old_indent, new_indent);
114 let insert_offset = insert_after.text_range().end(); 113 let insert_offset = insert_after.text_range().end();
115 builder.insert(insert_offset, fn_def); 114 match ctx.config.snippet_cap {
115 Some(cap) => builder.insert_snippet(cap, insert_offset, fn_def),
116 None => builder.insert(insert_offset, fn_def),
117 }
116 }, 118 },
117 ) 119 )
118} 120}
@@ -194,14 +196,14 @@ fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<Con
194 if let Some(kind) = expr_err_kind(&expr, ctx) { 196 if let Some(kind) = expr_err_kind(&expr, ctx) {
195 Some(FlowKind::TryReturn { expr, kind }) 197 Some(FlowKind::TryReturn { expr, kind })
196 } else { 198 } else {
197 mark::hit!(external_control_flow_try_and_return_non_err); 199 cov_mark::hit!(external_control_flow_try_and_return_non_err);
198 return None; 200 return None;
199 } 201 }
200 } 202 }
201 None => return None, 203 None => return None,
202 }, 204 },
203 (Some(_), _, _, _) => { 205 (Some(_), _, _, _) => {
204 mark::hit!(external_control_flow_try_and_bc); 206 cov_mark::hit!(external_control_flow_try_and_bc);
205 return None; 207 return None;
206 } 208 }
207 (None, Some(r), None, None) => match r.expr() { 209 (None, Some(r), None, None) => match r.expr() {
@@ -209,11 +211,11 @@ fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<Con
209 None => Some(FlowKind::Return), 211 None => Some(FlowKind::Return),
210 }, 212 },
211 (None, Some(_), _, _) => { 213 (None, Some(_), _, _) => {
212 mark::hit!(external_control_flow_return_and_bc); 214 cov_mark::hit!(external_control_flow_return_and_bc);
213 return None; 215 return None;
214 } 216 }
215 (None, None, Some(_), Some(_)) => { 217 (None, None, Some(_), Some(_)) => {
216 mark::hit!(external_control_flow_break_and_continue); 218 cov_mark::hit!(external_control_flow_break_and_continue);
217 return None; 219 return None;
218 } 220 }
219 (None, None, Some(b), None) => match b.expr() { 221 (None, None, Some(b), None) => match b.expr() {
@@ -1079,7 +1081,10 @@ fn format_function(
1079 let params = make_param_list(ctx, module, fun); 1081 let params = make_param_list(ctx, module, fun);
1080 let ret_ty = make_ret_ty(ctx, module, fun); 1082 let ret_ty = make_ret_ty(ctx, module, fun);
1081 let body = make_body(ctx, old_indent, new_indent, fun); 1083 let body = make_body(ctx, old_indent, new_indent, fun);
1082 format_to!(fn_def, "\n\n{}fn $0{}{}", new_indent, fun.name, params); 1084 match ctx.config.snippet_cap {
1085 Some(_) => format_to!(fn_def, "\n\n{}fn $0{}{}", new_indent, fun.name, params),
1086 None => format_to!(fn_def, "\n\n{}fn {}{}", new_indent, fun.name, params),
1087 }
1083 if let Some(ret_ty) = ret_ty { 1088 if let Some(ret_ty) = ret_ty {
1084 format_to!(fn_def, " {}", ret_ty); 1089 format_to!(fn_def, " {}", ret_ty);
1085 } 1090 }
@@ -1831,7 +1836,7 @@ fn $0fun_name(n: u32) -> u32 {
1831 1836
1832 #[test] 1837 #[test]
1833 fn in_comment_is_not_applicable() { 1838 fn in_comment_is_not_applicable() {
1834 mark::check!(extract_function_in_comment_is_not_applicable); 1839 cov_mark::check!(extract_function_in_comment_is_not_applicable);
1835 check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }"); 1840 check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }");
1836 } 1841 }
1837 1842
@@ -2816,7 +2821,7 @@ fn $0fun_name(n: i32) -> Result<i32, i64> {
2816 2821
2817 #[test] 2822 #[test]
2818 fn break_and_continue() { 2823 fn break_and_continue() {
2819 mark::check!(external_control_flow_break_and_continue); 2824 cov_mark::check!(external_control_flow_break_and_continue);
2820 check_assist_not_applicable( 2825 check_assist_not_applicable(
2821 extract_function, 2826 extract_function,
2822 r##" 2827 r##"
@@ -2836,7 +2841,7 @@ fn foo() {
2836 2841
2837 #[test] 2842 #[test]
2838 fn return_and_break() { 2843 fn return_and_break() {
2839 mark::check!(external_control_flow_return_and_bc); 2844 cov_mark::check!(external_control_flow_return_and_bc);
2840 check_assist_not_applicable( 2845 check_assist_not_applicable(
2841 extract_function, 2846 extract_function,
2842 r##" 2847 r##"
@@ -3335,7 +3340,7 @@ fn $0fun_name() -> Result<i32, i64> {
3335 3340
3336 #[test] 3341 #[test]
3337 fn try_and_break() { 3342 fn try_and_break() {
3338 mark::check!(external_control_flow_try_and_bc); 3343 cov_mark::check!(external_control_flow_try_and_bc);
3339 check_assist_not_applicable( 3344 check_assist_not_applicable(
3340 extract_function, 3345 extract_function,
3341 r##" 3346 r##"
@@ -3357,7 +3362,7 @@ fn foo() -> Option<()> {
3357 3362
3358 #[test] 3363 #[test]
3359 fn try_and_return_ok() { 3364 fn try_and_return_ok() {
3360 mark::check!(external_control_flow_try_and_return_non_err); 3365 cov_mark::check!(external_control_flow_try_and_return_non_err);
3361 check_assist_not_applicable( 3366 check_assist_not_applicable(
3362 extract_function, 3367 extract_function,
3363 r##" 3368 r##"
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
index 5c7678b53..335e0ed95 100644
--- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -1,7 +1,7 @@
1use std::iter; 1use std::iter;
2 2
3use either::Either; 3use either::Either;
4use hir::{AsName, Module, ModuleDef, Name, Variant}; 4use hir::{Module, ModuleDef, Name, Variant};
5use ide_db::{ 5use ide_db::{
6 defs::Definition, 6 defs::Definition,
7 helpers::{ 7 helpers::{
@@ -133,7 +133,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
133 ), 133 ),
134 _ => false, 134 _ => false,
135 }) 135 })
136 .any(|(name, _)| name == variant_name.as_name()) 136 .any(|(name, _)| name.to_string() == variant_name.to_string())
137} 137}
138 138
139fn insert_import( 139fn insert_import(
@@ -154,7 +154,7 @@ fn insert_import(
154 mod_path.pop_segment(); 154 mod_path.pop_segment();
155 mod_path.push_segment(variant_hir_name.clone()); 155 mod_path.push_segment(variant_hir_name.clone());
156 let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?; 156 let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?;
157 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); 157 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use);
158 } 158 }
159 Some(()) 159 Some(())
160} 160}
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs
index 312ac7ac4..7a32483dc 100644
--- a/crates/ide_assists/src/handlers/extract_variable.rs
+++ b/crates/ide_assists/src/handlers/extract_variable.rs
@@ -6,7 +6,6 @@ use syntax::{
6 }, 6 },
7 SyntaxNode, 7 SyntaxNode,
8}; 8};
9use test_utils::mark;
10 9
11use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; 10use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
12 11
@@ -32,7 +31,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
32 } 31 }
33 let node = ctx.covering_element(); 32 let node = ctx.covering_element();
34 if node.kind() == COMMENT { 33 if node.kind() == COMMENT {
35 mark::hit!(extract_var_in_comment_is_not_applicable); 34 cov_mark::hit!(extract_var_in_comment_is_not_applicable);
36 return None; 35 return None;
37 } 36 }
38 let to_extract = node.ancestors().find_map(valid_target_expr)?; 37 let to_extract = node.ancestors().find_map(valid_target_expr)?;
@@ -69,7 +68,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
69 format_to!(buf, "{}", to_extract.syntax()); 68 format_to!(buf, "{}", to_extract.syntax());
70 69
71 if let Anchor::Replace(stmt) = anchor { 70 if let Anchor::Replace(stmt) = anchor {
72 mark::hit!(test_extract_var_expr_stmt); 71 cov_mark::hit!(test_extract_var_expr_stmt);
73 if stmt.semicolon_token().is_none() { 72 if stmt.semicolon_token().is_none() {
74 buf.push_str(";"); 73 buf.push_str(";");
75 } 74 }
@@ -142,7 +141,7 @@ impl Anchor {
142 node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.tail_expr()) 141 node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.tail_expr())
143 { 142 {
144 if expr.syntax() == &node { 143 if expr.syntax() == &node {
145 mark::hit!(test_extract_var_last_expr); 144 cov_mark::hit!(test_extract_var_last_expr);
146 return Some(Anchor::Before(node)); 145 return Some(Anchor::Before(node));
147 } 146 }
148 } 147 }
@@ -175,8 +174,6 @@ impl Anchor {
175 174
176#[cfg(test)] 175#[cfg(test)]
177mod tests { 176mod tests {
178 use test_utils::mark;
179
180 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 177 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
181 178
182 use super::*; 179 use super::*;
@@ -199,13 +196,13 @@ fn foo() {
199 196
200 #[test] 197 #[test]
201 fn extract_var_in_comment_is_not_applicable() { 198 fn extract_var_in_comment_is_not_applicable() {
202 mark::check!(extract_var_in_comment_is_not_applicable); 199 cov_mark::check!(extract_var_in_comment_is_not_applicable);
203 check_assist_not_applicable(extract_variable, "fn main() { 1 + /* $0comment$0 */ 1; }"); 200 check_assist_not_applicable(extract_variable, "fn main() { 1 + /* $0comment$0 */ 1; }");
204 } 201 }
205 202
206 #[test] 203 #[test]
207 fn test_extract_var_expr_stmt() { 204 fn test_extract_var_expr_stmt() {
208 mark::check!(test_extract_var_expr_stmt); 205 cov_mark::check!(test_extract_var_expr_stmt);
209 check_assist( 206 check_assist(
210 extract_variable, 207 extract_variable,
211 r#" 208 r#"
@@ -250,7 +247,7 @@ fn foo() {
250 247
251 #[test] 248 #[test]
252 fn test_extract_var_last_expr() { 249 fn test_extract_var_last_expr() {
253 mark::check!(test_extract_var_last_expr); 250 cov_mark::check!(test_extract_var_last_expr);
254 check_assist( 251 check_assist(
255 extract_variable, 252 extract_variable,
256 r#" 253 r#"
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index 7086e47d2..878b3a3fa 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -5,7 +5,6 @@ use ide_db::helpers::{mod_path_to_ast, FamousDefs};
5use ide_db::RootDatabase; 5use ide_db::RootDatabase;
6use itertools::Itertools; 6use itertools::Itertools;
7use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; 7use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
8use test_utils::mark;
9 8
10use crate::{ 9use crate::{
11 utils::{does_pat_match_variant, render_snippet, Cursor}, 10 utils::{does_pat_match_variant, render_snippet, Cursor},
@@ -62,7 +61,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
62 .collect::<Vec<_>>(); 61 .collect::<Vec<_>>();
63 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { 62 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() {
64 // Match `Some` variant first. 63 // Match `Some` variant first.
65 mark::hit!(option_order); 64 cov_mark::hit!(option_order);
66 variants.reverse() 65 variants.reverse()
67 } 66 }
68 variants 67 variants
@@ -195,7 +194,6 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Optio
195#[cfg(test)] 194#[cfg(test)]
196mod tests { 195mod tests {
197 use ide_db::helpers::FamousDefs; 196 use ide_db::helpers::FamousDefs;
198 use test_utils::mark;
199 197
200 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 198 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
201 199
@@ -730,7 +728,7 @@ fn main() {
730 728
731 #[test] 729 #[test]
732 fn option_order() { 730 fn option_order() {
733 mark::check!(option_order); 731 cov_mark::check!(option_order);
734 let before = r#" 732 let before = r#"
735fn foo(opt: Option<i32>) { 733fn foo(opt: Option<i32>) {
736 match opt$0 { 734 match opt$0 {
diff --git a/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs b/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs
index 6a2ab9596..588ee1350 100644
--- a/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs
+++ b/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs
@@ -1,7 +1,6 @@
1use ide_db::helpers::FamousDefs; 1use ide_db::helpers::FamousDefs;
2use ide_db::RootDatabase; 2use ide_db::RootDatabase;
3use syntax::ast::{self, AstNode, NameOwner}; 3use syntax::ast::{self, AstNode, NameOwner};
4use test_utils::mark;
5 4
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 5use crate::{AssistContext, AssistId, AssistKind, Assists};
7 6
@@ -38,12 +37,12 @@ pub(crate) fn generate_default_from_enum_variant(
38 let variant_name = variant.name()?; 37 let variant_name = variant.name()?;
39 let enum_name = variant.parent_enum().name()?; 38 let enum_name = variant.parent_enum().name()?;
40 if !matches!(variant.kind(), ast::StructKind::Unit) { 39 if !matches!(variant.kind(), ast::StructKind::Unit) {
41 mark::hit!(test_gen_default_on_non_unit_variant_not_implemented); 40 cov_mark::hit!(test_gen_default_on_non_unit_variant_not_implemented);
42 return None; 41 return None;
43 } 42 }
44 43
45 if existing_default_impl(&ctx.sema, &variant).is_some() { 44 if existing_default_impl(&ctx.sema, &variant).is_some() {
46 mark::hit!(test_gen_default_impl_already_exists); 45 cov_mark::hit!(test_gen_default_impl_already_exists);
47 return None; 46 return None;
48 } 47 }
49 48
@@ -89,8 +88,6 @@ fn existing_default_impl(
89 88
90#[cfg(test)] 89#[cfg(test)]
91mod tests { 90mod tests {
92 use test_utils::mark;
93
94 use crate::tests::{check_assist, check_assist_not_applicable}; 91 use crate::tests::{check_assist, check_assist_not_applicable};
95 92
96 use super::*; 93 use super::*;
@@ -127,7 +124,7 @@ impl Default for Variant {
127 124
128 #[test] 125 #[test]
129 fn test_generate_default_already_implemented() { 126 fn test_generate_default_already_implemented() {
130 mark::check!(test_gen_default_impl_already_exists); 127 cov_mark::check!(test_gen_default_impl_already_exists);
131 check_not_applicable( 128 check_not_applicable(
132 r#" 129 r#"
133enum Variant { 130enum Variant {
@@ -146,7 +143,7 @@ impl Default for Variant {
146 143
147 #[test] 144 #[test]
148 fn test_add_from_impl_no_element() { 145 fn test_add_from_impl_no_element() {
149 mark::check!(test_gen_default_on_non_unit_variant_not_implemented); 146 cov_mark::check!(test_gen_default_on_non_unit_variant_not_implemented);
150 check_not_applicable( 147 check_not_applicable(
151 r#" 148 r#"
152enum Variant { 149enum Variant {
diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs
new file mode 100644
index 000000000..81c54ba3e
--- /dev/null
+++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs
@@ -0,0 +1,373 @@
1use crate::{
2 assist_context::{AssistContext, Assists},
3 AssistId,
4};
5use ide_db::helpers::FamousDefs;
6use syntax::{
7 ast::{self, Impl, NameOwner},
8 AstNode,
9};
10
11// Assist: generate_default_from_new
12//
13// Generates default implementation from new method.
14//
15// ```
16// struct Example { _inner: () }
17//
18// impl Example {
19// pub fn n$0ew() -> Self {
20// Self { _inner: () }
21// }
22// }
23// ```
24// ->
25// ```
26// struct Example { _inner: () }
27//
28// impl Example {
29// pub fn new() -> Self {
30// Self { _inner: () }
31// }
32// }
33//
34// impl Default for Example {
35// fn default() -> Self {
36// Self::new()
37// }
38// }
39// ```
40pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
41 let fn_node = ctx.find_node_at_offset::<ast::Fn>()?;
42 let fn_name = fn_node.name()?;
43
44 if fn_name.text() != "new" {
45 cov_mark::hit!(other_function_than_new);
46 return None;
47 }
48
49 if fn_node.param_list()?.params().next().is_some() {
50 cov_mark::hit!(new_function_with_parameters);
51 return None;
52 }
53
54 let impl_ = fn_node.syntax().ancestors().into_iter().find_map(ast::Impl::cast)?;
55 if is_default_implemented(ctx, &impl_) {
56 cov_mark::hit!(default_block_is_already_present);
57 cov_mark::hit!(struct_in_module_with_default);
58 return None;
59 }
60
61 let insert_location = impl_.syntax().text_range();
62
63 acc.add(
64 AssistId("generate_default_from_new", crate::AssistKind::Generate),
65 "Generate a Default impl from a new fn",
66 insert_location,
67 move |builder| {
68 let code = default_fn_node_for_new(impl_);
69 builder.insert(insert_location.end(), code);
70 },
71 )
72}
73
74fn default_fn_node_for_new(impl_: Impl) -> String {
75 format!(
76 "
77
78impl Default for {} {{
79 fn default() -> Self {{
80 Self::new()
81 }}
82}}",
83 impl_.self_ty().unwrap().syntax().text()
84 )
85}
86
87fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool {
88 let db = ctx.sema.db;
89 let impl_ = ctx.sema.to_def(impl_);
90 let impl_def = match impl_ {
91 Some(value) => value,
92 None => return false,
93 };
94
95 let ty = impl_def.target_ty(db);
96 let krate = impl_def.module(db).krate();
97 let default = FamousDefs(&ctx.sema, Some(krate)).core_default_Default();
98 let default_trait = match default {
99 Some(value) => value,
100 None => return false,
101 };
102
103 ty.impls_trait(db, default_trait, &[])
104}
105
106#[cfg(test)]
107mod tests {
108 use ide_db::helpers::FamousDefs;
109
110 use crate::tests::{check_assist, check_assist_not_applicable};
111
112 use super::*;
113
114 #[test]
115 fn generate_default() {
116 check_pass(
117 r#"
118struct Example { _inner: () }
119
120impl Example {
121 pub fn ne$0w() -> Self {
122 Self { _inner: () }
123 }
124}
125
126fn main() {}
127"#,
128 r#"
129struct Example { _inner: () }
130
131impl Example {
132 pub fn new() -> Self {
133 Self { _inner: () }
134 }
135}
136
137impl Default for Example {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143fn main() {}
144"#,
145 );
146 }
147
148 #[test]
149 fn generate_default2() {
150 check_pass(
151 r#"
152struct Test { value: u32 }
153
154impl Test {
155 pub fn ne$0w() -> Self {
156 Self { value: 0 }
157 }
158}
159"#,
160 r#"
161struct Test { value: u32 }
162
163impl Test {
164 pub fn new() -> Self {
165 Self { value: 0 }
166 }
167}
168
169impl Default for Test {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174"#,
175 );
176 }
177
178 #[test]
179 fn new_function_with_parameters() {
180 cov_mark::check!(new_function_with_parameters);
181 check_not_applicable(
182 r#"
183struct Example { _inner: () }
184
185impl Example {
186 pub fn $0new(value: ()) -> Self {
187 Self { _inner: value }
188 }
189}
190"#,
191 );
192 }
193
194 #[test]
195 fn other_function_than_new() {
196 cov_mark::check!(other_function_than_new);
197 check_not_applicable(
198 r#"
199struct Example { _inner: () }
200
201impl Example {
202 pub fn a$0dd() -> Self {
203 Self { _inner: () }
204 }
205}
206
207"#,
208 );
209 }
210
211 #[test]
212 fn default_block_is_already_present() {
213 cov_mark::check!(default_block_is_already_present);
214 check_not_applicable(
215 r#"
216struct Example { _inner: () }
217
218impl Example {
219 pub fn n$0ew() -> Self {
220 Self { _inner: () }
221 }
222}
223
224impl Default for Example {
225 fn default() -> Self {
226 Self::new()
227 }
228}
229"#,
230 );
231 }
232
233 #[test]
234 fn standalone_new_function() {
235 check_not_applicable(
236 r#"
237fn n$0ew() -> u32 {
238 0
239}
240"#,
241 );
242 }
243
244 #[test]
245 fn multiple_struct_blocks() {
246 check_pass(
247 r#"
248struct Example { _inner: () }
249struct Test { value: u32 }
250
251impl Example {
252 pub fn new$0() -> Self {
253 Self { _inner: () }
254 }
255}
256"#,
257 r#"
258struct Example { _inner: () }
259struct Test { value: u32 }
260
261impl Example {
262 pub fn new() -> Self {
263 Self { _inner: () }
264 }
265}
266
267impl Default for Example {
268 fn default() -> Self {
269 Self::new()
270 }
271}
272"#,
273 );
274 }
275
276 #[test]
277 fn when_struct_is_after_impl() {
278 check_pass(
279 r#"
280impl Example {
281 pub fn $0new() -> Self {
282 Self { _inner: () }
283 }
284}
285
286struct Example { _inner: () }
287"#,
288 r#"
289impl Example {
290 pub fn new() -> Self {
291 Self { _inner: () }
292 }
293}
294
295impl Default for Example {
296 fn default() -> Self {
297 Self::new()
298 }
299}
300
301struct Example { _inner: () }
302"#,
303 );
304 }
305
306 #[test]
307 fn struct_in_module() {
308 check_pass(
309 r#"
310mod test {
311 struct Example { _inner: () }
312
313 impl Example {
314 pub fn n$0ew() -> Self {
315 Self { _inner: () }
316 }
317 }
318}
319"#,
320 r#"
321mod test {
322 struct Example { _inner: () }
323
324 impl Example {
325 pub fn new() -> Self {
326 Self { _inner: () }
327 }
328 }
329
330impl Default for Example {
331 fn default() -> Self {
332 Self::new()
333 }
334}
335}
336"#,
337 );
338 }
339
340 #[test]
341 fn struct_in_module_with_default() {
342 cov_mark::check!(struct_in_module_with_default);
343 check_not_applicable(
344 r#"
345mod test {
346 struct Example { _inner: () }
347
348 impl Example {
349 pub fn n$0ew() -> Self {
350 Self { _inner: () }
351 }
352 }
353
354 impl Default for Example {
355 fn default() -> Self {
356 Self::new()
357 }
358 }
359}
360"#,
361 );
362 }
363
364 fn check_pass(before: &str, after: &str) {
365 let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
366 check_assist(generate_default_from_new, before, after);
367 }
368
369 fn check_not_applicable(before: &str) {
370 let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
371 check_assist_not_applicable(generate_default_from_new, before);
372 }
373}
diff --git a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs
index d9388a737..c13c6eebe 100644
--- a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs
+++ b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs
@@ -1,7 +1,6 @@
1use ide_db::helpers::FamousDefs; 1use ide_db::helpers::FamousDefs;
2use ide_db::RootDatabase; 2use ide_db::RootDatabase;
3use syntax::ast::{self, AstNode, NameOwner}; 3use syntax::ast::{self, AstNode, NameOwner};
4use test_utils::mark;
5 4
6use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists}; 5use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists};
7 6
@@ -44,7 +43,7 @@ pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext
44 }; 43 };
45 44
46 if existing_from_impl(&ctx.sema, &variant).is_some() { 45 if existing_from_impl(&ctx.sema, &variant).is_some() {
47 mark::hit!(test_add_from_impl_already_exists); 46 cov_mark::hit!(test_add_from_impl_already_exists);
48 return None; 47 return None;
49 } 48 }
50 49
@@ -103,8 +102,6 @@ fn existing_from_impl(
103 102
104#[cfg(test)] 103#[cfg(test)]
105mod tests { 104mod tests {
106 use test_utils::mark;
107
108 use crate::tests::{check_assist, check_assist_not_applicable}; 105 use crate::tests::{check_assist, check_assist_not_applicable};
109 106
110 use super::*; 107 use super::*;
@@ -172,7 +169,7 @@ impl From<u32> for A {
172 169
173 #[test] 170 #[test]
174 fn test_add_from_impl_already_exists() { 171 fn test_add_from_impl_already_exists() {
175 mark::check!(test_add_from_impl_already_exists); 172 cov_mark::check!(test_add_from_impl_already_exists);
176 check_not_applicable( 173 check_not_applicable(
177 r#" 174 r#"
178enum A { $0One(u32), } 175enum A { $0One(u32), }
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
index 959824981..6f95b1a07 100644
--- a/crates/ide_assists/src/handlers/generate_function.rs
+++ b/crates/ide_assists/src/handlers/generate_function.rs
@@ -1,6 +1,7 @@
1use hir::HirDisplay; 1use hir::HirDisplay;
2use ide_db::{base_db::FileId, helpers::SnippetCap}; 2use ide_db::{base_db::FileId, helpers::SnippetCap};
3use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::{FxHashMap, FxHashSet};
4use stdx::to_lower_snake_case;
4use syntax::{ 5use syntax::{
5 ast::{ 6 ast::{
6 self, 7 self,
@@ -82,17 +83,18 @@ struct FunctionTemplate {
82 leading_ws: String, 83 leading_ws: String,
83 fn_def: ast::Fn, 84 fn_def: ast::Fn,
84 ret_type: ast::RetType, 85 ret_type: ast::RetType,
86 should_render_snippet: bool,
85 trailing_ws: String, 87 trailing_ws: String,
86 file: FileId, 88 file: FileId,
87} 89}
88 90
89impl FunctionTemplate { 91impl FunctionTemplate {
90 fn to_string(&self, cap: Option<SnippetCap>) -> String { 92 fn to_string(&self, cap: Option<SnippetCap>) -> String {
91 let f = match cap { 93 let f = match (cap, self.should_render_snippet) {
92 Some(cap) => { 94 (Some(cap), true) => {
93 render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax())) 95 render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax()))
94 } 96 }
95 None => self.fn_def.to_string(), 97 _ => self.fn_def.to_string(),
96 }; 98 };
97 format!("{}{}{}", self.leading_ws, f, self.trailing_ws) 99 format!("{}{}{}", self.leading_ws, f, self.trailing_ws)
98 } 100 }
@@ -103,6 +105,8 @@ struct FunctionBuilder {
103 fn_name: ast::Name, 105 fn_name: ast::Name,
104 type_params: Option<ast::GenericParamList>, 106 type_params: Option<ast::GenericParamList>,
105 params: ast::ParamList, 107 params: ast::ParamList,
108 ret_type: ast::RetType,
109 should_render_snippet: bool,
106 file: FileId, 110 file: FileId,
107 needs_pub: bool, 111 needs_pub: bool,
108} 112}
@@ -131,7 +135,43 @@ impl FunctionBuilder {
131 let fn_name = fn_name(&path)?; 135 let fn_name = fn_name(&path)?;
132 let (type_params, params) = fn_args(ctx, target_module, &call)?; 136 let (type_params, params) = fn_args(ctx, target_module, &call)?;
133 137
134 Some(Self { target, fn_name, type_params, params, file, needs_pub }) 138 // should_render_snippet intends to express a rough level of confidence about
139 // the correctness of the return type.
140 //
141 // If we are able to infer some return type, and that return type is not unit, we
142 // don't want to render the snippet. The assumption here is in this situation the
143 // return type is just as likely to be correct as any other part of the generated
144 // function.
145 //
146 // In the case where the return type is inferred as unit it is likely that the
147 // user does in fact intend for this generated function to return some non unit
148 // type, but that the current state of their code doesn't allow that return type
149 // to be accurately inferred.
150 let (ret_ty, should_render_snippet) = {
151 match ctx.sema.type_of_expr(&ast::Expr::CallExpr(call.clone())) {
152 Some(ty) if ty.is_unknown() || ty.is_unit() => (make::ty_unit(), true),
153 Some(ty) => {
154 let rendered = ty.display_source_code(ctx.db(), target_module.into());
155 match rendered {
156 Ok(rendered) => (make::ty(&rendered), false),
157 Err(_) => (make::ty_unit(), true),
158 }
159 }
160 None => (make::ty_unit(), true),
161 }
162 };
163 let ret_type = make::ret_type(ret_ty);
164
165 Some(Self {
166 target,
167 fn_name,
168 type_params,
169 params,
170 ret_type,
171 should_render_snippet,
172 file,
173 needs_pub,
174 })
135 } 175 }
136 176
137 fn render(self) -> FunctionTemplate { 177 fn render(self) -> FunctionTemplate {
@@ -144,7 +184,7 @@ impl FunctionBuilder {
144 self.type_params, 184 self.type_params,
145 self.params, 185 self.params,
146 fn_body, 186 fn_body,
147 Some(make::ret_type(make::ty_unit())), 187 Some(self.ret_type),
148 ); 188 );
149 let leading_ws; 189 let leading_ws;
150 let trailing_ws; 190 let trailing_ws;
@@ -170,6 +210,7 @@ impl FunctionBuilder {
170 insert_offset, 210 insert_offset,
171 leading_ws, 211 leading_ws,
172 ret_type: fn_def.ret_type().unwrap(), 212 ret_type: fn_def.ret_type().unwrap(),
213 should_render_snippet: self.should_render_snippet,
173 fn_def, 214 fn_def,
174 trailing_ws, 215 trailing_ws,
175 file: self.file, 216 file: self.file,
@@ -257,14 +298,15 @@ fn deduplicate_arg_names(arg_names: &mut Vec<String>) {
257fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> { 298fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> {
258 match fn_arg { 299 match fn_arg {
259 ast::Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?), 300 ast::Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?),
260 _ => Some( 301 _ => {
261 fn_arg 302 let s = fn_arg
262 .syntax() 303 .syntax()
263 .descendants() 304 .descendants()
264 .filter(|d| ast::NameRef::can_cast(d.kind())) 305 .filter(|d| ast::NameRef::can_cast(d.kind()))
265 .last()? 306 .last()?
266 .to_string(), 307 .to_string();
267 ), 308 Some(to_lower_snake_case(&s))
309 }
268 } 310 }
269} 311}
270 312
@@ -448,6 +490,52 @@ mod baz {
448 } 490 }
449 491
450 #[test] 492 #[test]
493 fn add_function_with_upper_camel_case_arg() {
494 check_assist(
495 generate_function,
496 r"
497struct BazBaz;
498fn foo() {
499 bar$0(BazBaz);
500}
501",
502 r"
503struct BazBaz;
504fn foo() {
505 bar(BazBaz);
506}
507
508fn bar(baz_baz: BazBaz) ${0:-> ()} {
509 todo!()
510}
511",
512 );
513 }
514
515 #[test]
516 fn add_function_with_upper_camel_case_arg_as_cast() {
517 check_assist(
518 generate_function,
519 r"
520struct BazBaz;
521fn foo() {
522 bar$0(&BazBaz as *const BazBaz);
523}
524",
525 r"
526struct BazBaz;
527fn foo() {
528 bar(&BazBaz as *const BazBaz);
529}
530
531fn bar(baz_baz: *const BazBaz) ${0:-> ()} {
532 todo!()
533}
534",
535 );
536 }
537
538 #[test]
451 fn add_function_with_function_call_arg() { 539 fn add_function_with_function_call_arg() {
452 check_assist( 540 check_assist(
453 generate_function, 541 generate_function,
@@ -498,7 +586,7 @@ impl Baz {
498 } 586 }
499} 587}
500 588
501fn bar(baz: Baz) ${0:-> ()} { 589fn bar(baz: Baz) -> Baz {
502 todo!() 590 todo!()
503} 591}
504", 592",
@@ -1012,6 +1100,27 @@ pub(crate) fn bar() ${0:-> ()} {
1012 } 1100 }
1013 1101
1014 #[test] 1102 #[test]
1103 fn add_function_with_return_type() {
1104 check_assist(
1105 generate_function,
1106 r"
1107fn main() {
1108 let x: u32 = foo$0();
1109}
1110",
1111 r"
1112fn main() {
1113 let x: u32 = foo();
1114}
1115
1116fn foo() -> u32 {
1117 todo!()
1118}
1119",
1120 )
1121 }
1122
1123 #[test]
1015 fn add_function_not_applicable_if_function_already_exists() { 1124 fn add_function_not_applicable_if_function_already_exists() {
1016 check_assist_not_applicable( 1125 check_assist_not_applicable(
1017 generate_function, 1126 generate_function,
diff --git a/crates/ide_assists/src/handlers/infer_function_return_type.rs b/crates/ide_assists/src/handlers/infer_function_return_type.rs
index 5279af1f3..66113751c 100644
--- a/crates/ide_assists/src/handlers/infer_function_return_type.rs
+++ b/crates/ide_assists/src/handlers/infer_function_return_type.rs
@@ -1,6 +1,5 @@
1use hir::HirDisplay; 1use hir::HirDisplay;
2use syntax::{ast, AstNode, TextRange, TextSize}; 2use syntax::{ast, AstNode, TextRange, TextSize};
3use test_utils::mark;
4 3
5use crate::{AssistContext, AssistId, AssistKind, Assists}; 4use crate::{AssistContext, AssistId, AssistKind, Assists};
6 5
@@ -42,7 +41,7 @@ pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext)
42 } 41 }
43 } 42 }
44 if let FnType::Closure { wrap_expr: true } = fn_type { 43 if let FnType::Closure { wrap_expr: true } = fn_type {
45 mark::hit!(wrap_closure_non_block_expr); 44 cov_mark::hit!(wrap_closure_non_block_expr);
46 // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block 45 // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block
47 builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr)); 46 builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr));
48 } 47 }
@@ -61,13 +60,13 @@ fn ret_ty_to_action(ret_ty: Option<ast::RetType>, insert_pos: TextSize) -> Optio
61 match ret_ty { 60 match ret_ty {
62 Some(ret_ty) => match ret_ty.ty() { 61 Some(ret_ty) => match ret_ty.ty() {
63 Some(ast::Type::InferType(_)) | None => { 62 Some(ast::Type::InferType(_)) | None => {
64 mark::hit!(existing_infer_ret_type); 63 cov_mark::hit!(existing_infer_ret_type);
65 mark::hit!(existing_infer_ret_type_closure); 64 cov_mark::hit!(existing_infer_ret_type_closure);
66 Some(InsertOrReplace::Replace(ret_ty.syntax().text_range())) 65 Some(InsertOrReplace::Replace(ret_ty.syntax().text_range()))
67 } 66 }
68 _ => { 67 _ => {
69 mark::hit!(existing_ret_type); 68 cov_mark::hit!(existing_ret_type);
70 mark::hit!(existing_ret_type_closure); 69 cov_mark::hit!(existing_ret_type_closure);
71 None 70 None
72 } 71 }
73 }, 72 },
@@ -109,11 +108,11 @@ fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrRepla
109 }; 108 };
110 let frange = ctx.frange.range; 109 let frange = ctx.frange.range;
111 if return_type_range.contains_range(frange) { 110 if return_type_range.contains_range(frange) {
112 mark::hit!(cursor_in_ret_position); 111 cov_mark::hit!(cursor_in_ret_position);
113 mark::hit!(cursor_in_ret_position_closure); 112 cov_mark::hit!(cursor_in_ret_position_closure);
114 } else if tail_expr.syntax().text_range().contains_range(frange) { 113 } else if tail_expr.syntax().text_range().contains_range(frange) {
115 mark::hit!(cursor_on_tail); 114 cov_mark::hit!(cursor_on_tail);
116 mark::hit!(cursor_on_tail_closure); 115 cov_mark::hit!(cursor_on_tail_closure);
117 } else { 116 } else {
118 return None; 117 return None;
119 } 118 }
@@ -128,7 +127,7 @@ mod tests {
128 127
129 #[test] 128 #[test]
130 fn infer_return_type_specified_inferred() { 129 fn infer_return_type_specified_inferred() {
131 mark::check!(existing_infer_ret_type); 130 cov_mark::check!(existing_infer_ret_type);
132 check_assist( 131 check_assist(
133 infer_function_return_type, 132 infer_function_return_type,
134 r#"fn foo() -> $0_ { 133 r#"fn foo() -> $0_ {
@@ -142,7 +141,7 @@ mod tests {
142 141
143 #[test] 142 #[test]
144 fn infer_return_type_specified_inferred_closure() { 143 fn infer_return_type_specified_inferred_closure() {
145 mark::check!(existing_infer_ret_type_closure); 144 cov_mark::check!(existing_infer_ret_type_closure);
146 check_assist( 145 check_assist(
147 infer_function_return_type, 146 infer_function_return_type,
148 r#"fn foo() { 147 r#"fn foo() {
@@ -156,7 +155,7 @@ mod tests {
156 155
157 #[test] 156 #[test]
158 fn infer_return_type_cursor_at_return_type_pos() { 157 fn infer_return_type_cursor_at_return_type_pos() {
159 mark::check!(cursor_in_ret_position); 158 cov_mark::check!(cursor_in_ret_position);
160 check_assist( 159 check_assist(
161 infer_function_return_type, 160 infer_function_return_type,
162 r#"fn foo() $0{ 161 r#"fn foo() $0{
@@ -170,7 +169,7 @@ mod tests {
170 169
171 #[test] 170 #[test]
172 fn infer_return_type_cursor_at_return_type_pos_closure() { 171 fn infer_return_type_cursor_at_return_type_pos_closure() {
173 mark::check!(cursor_in_ret_position_closure); 172 cov_mark::check!(cursor_in_ret_position_closure);
174 check_assist( 173 check_assist(
175 infer_function_return_type, 174 infer_function_return_type,
176 r#"fn foo() { 175 r#"fn foo() {
@@ -184,7 +183,7 @@ mod tests {
184 183
185 #[test] 184 #[test]
186 fn infer_return_type() { 185 fn infer_return_type() {
187 mark::check!(cursor_on_tail); 186 cov_mark::check!(cursor_on_tail);
188 check_assist( 187 check_assist(
189 infer_function_return_type, 188 infer_function_return_type,
190 r#"fn foo() { 189 r#"fn foo() {
@@ -219,7 +218,7 @@ mod tests {
219 218
220 #[test] 219 #[test]
221 fn not_applicable_ret_type_specified() { 220 fn not_applicable_ret_type_specified() {
222 mark::check!(existing_ret_type); 221 cov_mark::check!(existing_ret_type);
223 check_assist_not_applicable( 222 check_assist_not_applicable(
224 infer_function_return_type, 223 infer_function_return_type,
225 r#"fn foo() -> i32 { 224 r#"fn foo() -> i32 {
@@ -251,7 +250,7 @@ mod tests {
251 250
252 #[test] 251 #[test]
253 fn infer_return_type_closure_block() { 252 fn infer_return_type_closure_block() {
254 mark::check!(cursor_on_tail_closure); 253 cov_mark::check!(cursor_on_tail_closure);
255 check_assist( 254 check_assist(
256 infer_function_return_type, 255 infer_function_return_type,
257 r#"fn foo() { 256 r#"fn foo() {
@@ -282,7 +281,7 @@ mod tests {
282 281
283 #[test] 282 #[test]
284 fn infer_return_type_closure_wrap() { 283 fn infer_return_type_closure_wrap() {
285 mark::check!(wrap_closure_non_block_expr); 284 cov_mark::check!(wrap_closure_non_block_expr);
286 check_assist( 285 check_assist(
287 infer_function_return_type, 286 infer_function_return_type,
288 r#"fn foo() { 287 r#"fn foo() {
@@ -321,7 +320,7 @@ mod tests {
321 320
322 #[test] 321 #[test]
323 fn not_applicable_ret_type_specified_closure() { 322 fn not_applicable_ret_type_specified_closure() {
324 mark::check!(existing_ret_type_closure); 323 cov_mark::check!(existing_ret_type_closure);
325 check_assist_not_applicable( 324 check_assist_not_applicable(
326 infer_function_return_type, 325 infer_function_return_type,
327 r#"fn foo() { 326 r#"fn foo() {
diff --git a/crates/ide_assists/src/handlers/inline_function.rs b/crates/ide_assists/src/handlers/inline_function.rs
index 6ec99b09b..8e56029cb 100644
--- a/crates/ide_assists/src/handlers/inline_function.rs
+++ b/crates/ide_assists/src/handlers/inline_function.rs
@@ -4,7 +4,6 @@ use syntax::{
4 ast::{self, edit::AstNodeEdit, ArgListOwner}, 4 ast::{self, edit::AstNodeEdit, ArgListOwner},
5 AstNode, 5 AstNode,
6}; 6};
7use test_utils::mark;
8 7
9use crate::{ 8use crate::{
10 assist_context::{AssistContext, Assists}, 9 assist_context::{AssistContext, Assists},
@@ -49,7 +48,7 @@ pub(crate) fn inline_function(acc: &mut Assists, ctx: &AssistContext) -> Option<
49 if arguments.len() != parameters.len() { 48 if arguments.len() != parameters.len() {
50 // Can't inline the function because they've passed the wrong number of 49 // Can't inline the function because they've passed the wrong number of
51 // arguments to this function 50 // arguments to this function
52 mark::hit!(inline_function_incorrect_number_of_arguments); 51 cov_mark::hit!(inline_function_incorrect_number_of_arguments);
53 return None; 52 return None;
54 } 53 }
55 54
@@ -155,7 +154,7 @@ fn main() { Foo.bar$0(); }
155 154
156 #[test] 155 #[test]
157 fn not_applicable_when_incorrect_number_of_parameters_are_provided() { 156 fn not_applicable_when_incorrect_number_of_parameters_are_provided() {
158 mark::check!(inline_function_incorrect_number_of_arguments); 157 cov_mark::check!(inline_function_incorrect_number_of_arguments);
159 check_assist_not_applicable( 158 check_assist_not_applicable(
160 inline_function, 159 inline_function,
161 r#" 160 r#"
diff --git a/crates/ide_assists/src/handlers/inline_local_variable.rs b/crates/ide_assists/src/handlers/inline_local_variable.rs
index da5522670..ea1466dc8 100644
--- a/crates/ide_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ide_assists/src/handlers/inline_local_variable.rs
@@ -4,7 +4,6 @@ use syntax::{
4 ast::{self, AstNode, AstToken}, 4 ast::{self, AstNode, AstToken},
5 TextRange, 5 TextRange,
6}; 6};
7use test_utils::mark;
8 7
9use crate::{ 8use crate::{
10 assist_context::{AssistContext, Assists}, 9 assist_context::{AssistContext, Assists},
@@ -34,11 +33,11 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
34 _ => return None, 33 _ => return None,
35 }; 34 };
36 if bind_pat.mut_token().is_some() { 35 if bind_pat.mut_token().is_some() {
37 mark::hit!(test_not_inline_mut_variable); 36 cov_mark::hit!(test_not_inline_mut_variable);
38 return None; 37 return None;
39 } 38 }
40 if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { 39 if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) {
41 mark::hit!(not_applicable_outside_of_bind_pat); 40 cov_mark::hit!(not_applicable_outside_of_bind_pat);
42 return None; 41 return None;
43 } 42 }
44 let initializer_expr = let_stmt.initializer()?; 43 let initializer_expr = let_stmt.initializer()?;
@@ -47,7 +46,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
47 let def = Definition::Local(def); 46 let def = Definition::Local(def);
48 let usages = def.usages(&ctx.sema).all(); 47 let usages = def.usages(&ctx.sema).all();
49 if usages.is_empty() { 48 if usages.is_empty() {
50 mark::hit!(test_not_applicable_if_variable_unused); 49 cov_mark::hit!(test_not_applicable_if_variable_unused);
51 return None; 50 return None;
52 }; 51 };
53 52
@@ -130,7 +129,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
130 Some(name_ref) 129 Some(name_ref)
131 if ast::RecordExprField::for_field_name(name_ref).is_some() => 130 if ast::RecordExprField::for_field_name(name_ref).is_some() =>
132 { 131 {
133 mark::hit!(inline_field_shorthand); 132 cov_mark::hit!(inline_field_shorthand);
134 builder.insert(reference.range.end(), format!(": {}", replacement)); 133 builder.insert(reference.range.end(), format!(": {}", replacement));
135 } 134 }
136 _ => builder.replace(reference.range, replacement), 135 _ => builder.replace(reference.range, replacement),
@@ -143,8 +142,6 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
143 142
144#[cfg(test)] 143#[cfg(test)]
145mod tests { 144mod tests {
146 use test_utils::mark;
147
148 use crate::tests::{check_assist, check_assist_not_applicable}; 145 use crate::tests::{check_assist, check_assist_not_applicable};
149 146
150 use super::*; 147 use super::*;
@@ -351,7 +348,7 @@ fn foo() {
351 348
352 #[test] 349 #[test]
353 fn test_not_inline_mut_variable() { 350 fn test_not_inline_mut_variable() {
354 mark::check!(test_not_inline_mut_variable); 351 cov_mark::check!(test_not_inline_mut_variable);
355 check_assist_not_applicable( 352 check_assist_not_applicable(
356 inline_local_variable, 353 inline_local_variable,
357 r" 354 r"
@@ -684,7 +681,7 @@ fn foo() {
684 681
685 #[test] 682 #[test]
686 fn inline_field_shorthand() { 683 fn inline_field_shorthand() {
687 mark::check!(inline_field_shorthand); 684 cov_mark::check!(inline_field_shorthand);
688 check_assist( 685 check_assist(
689 inline_local_variable, 686 inline_local_variable,
690 r" 687 r"
@@ -705,7 +702,7 @@ fn main() {
705 702
706 #[test] 703 #[test]
707 fn test_not_applicable_if_variable_unused() { 704 fn test_not_applicable_if_variable_unused() {
708 mark::check!(test_not_applicable_if_variable_unused); 705 cov_mark::check!(test_not_applicable_if_variable_unused);
709 check_assist_not_applicable( 706 check_assist_not_applicable(
710 inline_local_variable, 707 inline_local_variable,
711 r" 708 r"
@@ -718,7 +715,7 @@ fn foo() {
718 715
719 #[test] 716 #[test]
720 fn not_applicable_outside_of_bind_pat() { 717 fn not_applicable_outside_of_bind_pat() {
721 mark::check!(not_applicable_outside_of_bind_pat); 718 cov_mark::check!(not_applicable_outside_of_bind_pat);
722 check_assist_not_applicable( 719 check_assist_not_applicable(
723 inline_local_variable, 720 inline_local_variable,
724 r" 721 r"
diff --git a/crates/ide_assists/src/handlers/move_module_to_file.rs b/crates/ide_assists/src/handlers/move_module_to_file.rs
index 91c395c1b..6e685b4b2 100644
--- a/crates/ide_assists/src/handlers/move_module_to_file.rs
+++ b/crates/ide_assists/src/handlers/move_module_to_file.rs
@@ -5,7 +5,6 @@ use syntax::{
5 ast::{self, edit::AstNodeEdit, NameOwner}, 5 ast::{self, edit::AstNodeEdit, NameOwner},
6 AstNode, TextRange, 6 AstNode, TextRange,
7}; 7};
8use test_utils::mark;
9 8
10use crate::{AssistContext, AssistId, AssistKind, Assists}; 9use crate::{AssistContext, AssistId, AssistKind, Assists};
11 10
@@ -28,7 +27,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt
28 27
29 let l_curly_offset = module_items.syntax().text_range().start(); 28 let l_curly_offset = module_items.syntax().text_range().start();
30 if l_curly_offset <= ctx.offset() { 29 if l_curly_offset <= ctx.offset() {
31 mark::hit!(available_before_curly); 30 cov_mark::hit!(available_before_curly);
32 return None; 31 return None;
33 } 32 }
34 let target = TextRange::new(module_ast.syntax().text_range().start(), l_curly_offset); 33 let target = TextRange::new(module_ast.syntax().text_range().start(), l_curly_offset);
@@ -182,7 +181,7 @@ pub(crate) mod tests;
182 181
183 #[test] 182 #[test]
184 fn available_before_curly() { 183 fn available_before_curly() {
185 mark::check!(available_before_curly); 184 cov_mark::check!(available_before_curly);
186 check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); 185 check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#);
187 } 186 }
188} 187}
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs
index 13e1cb754..04bae4e58 100644
--- a/crates/ide_assists/src/handlers/pull_assignment_up.rs
+++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs
@@ -2,7 +2,6 @@ use syntax::{
2 ast::{self, edit::AstNodeEdit, make}, 2 ast::{self, edit::AstNodeEdit, make},
3 AstNode, 3 AstNode,
4}; 4};
5use test_utils::mark;
6 5
7use crate::{ 6use crate::{
8 assist_context::{AssistContext, Assists}, 7 assist_context::{AssistContext, Assists},
@@ -104,7 +103,7 @@ fn exprify_if(
104 ast::ElseBranch::Block(exprify_block(block, sema, name)?) 103 ast::ElseBranch::Block(exprify_block(block, sema, name)?)
105 } 104 }
106 ast::ElseBranch::IfExpr(expr) => { 105 ast::ElseBranch::IfExpr(expr) => {
107 mark::hit!(test_pull_assignment_up_chained_if); 106 cov_mark::hit!(test_pull_assignment_up_chained_if);
108 ast::ElseBranch::IfExpr(ast::IfExpr::cast( 107 ast::ElseBranch::IfExpr(ast::IfExpr::cast(
109 exprify_if(&expr, sema, name)?.syntax().to_owned(), 108 exprify_if(&expr, sema, name)?.syntax().to_owned(),
110 )?) 109 )?)
@@ -144,7 +143,7 @@ fn is_equivalent(
144) -> bool { 143) -> bool {
145 match (expr0, expr1) { 144 match (expr0, expr1) {
146 (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => { 145 (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => {
147 mark::hit!(test_pull_assignment_up_field_assignment); 146 cov_mark::hit!(test_pull_assignment_up_field_assignment);
148 sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1) 147 sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1)
149 } 148 }
150 (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => { 149 (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => {
@@ -156,6 +155,17 @@ fn is_equivalent(
156 false 155 false
157 } 156 }
158 } 157 }
158 (ast::Expr::PrefixExpr(prefix0), ast::Expr::PrefixExpr(prefix1))
159 if prefix0.op_kind() == Some(ast::PrefixOp::Deref)
160 && prefix1.op_kind() == Some(ast::PrefixOp::Deref) =>
161 {
162 cov_mark::hit!(test_pull_assignment_up_deref);
163 if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) {
164 is_equivalent(sema, &prefix0, &prefix1)
165 } else {
166 false
167 }
168 }
159 _ => false, 169 _ => false,
160 } 170 }
161} 171}
@@ -252,7 +262,7 @@ fn foo() {
252 262
253 #[test] 263 #[test]
254 fn test_pull_assignment_up_chained_if() { 264 fn test_pull_assignment_up_chained_if() {
255 mark::check!(test_pull_assignment_up_chained_if); 265 cov_mark::check!(test_pull_assignment_up_chained_if);
256 check_assist( 266 check_assist(
257 pull_assignment_up, 267 pull_assignment_up,
258 r#" 268 r#"
@@ -368,7 +378,7 @@ fn foo() {
368 378
369 #[test] 379 #[test]
370 fn test_pull_assignment_up_field_assignment() { 380 fn test_pull_assignment_up_field_assignment() {
371 mark::check!(test_pull_assignment_up_field_assignment); 381 cov_mark::check!(test_pull_assignment_up_field_assignment);
372 check_assist( 382 check_assist(
373 pull_assignment_up, 383 pull_assignment_up,
374 r#" 384 r#"
@@ -397,4 +407,36 @@ fn foo() {
397}"#, 407}"#,
398 ) 408 )
399 } 409 }
410
411 #[test]
412 fn test_pull_assignment_up_deref() {
413 cov_mark::check!(test_pull_assignment_up_deref);
414 check_assist(
415 pull_assignment_up,
416 r#"
417fn foo() {
418 let mut a = 1;
419 let b = &mut a;
420
421 if true {
422 $0*b = 2;
423 } else {
424 *b = 3;
425 }
426}
427"#,
428 r#"
429fn foo() {
430 let mut a = 1;
431 let b = &mut a;
432
433 *b = if true {
434 2
435 } else {
436 3
437 };
438}
439"#,
440 )
441 }
400} 442}
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs
index b0b0d31b4..272874ae3 100644
--- a/crates/ide_assists/src/handlers/qualify_path.rs
+++ b/crates/ide_assists/src/handlers/qualify_path.rs
@@ -1,14 +1,16 @@
1use std::iter; 1use std::iter;
2 2
3use hir::{AsAssocItem, AsName}; 3use hir::AsAssocItem;
4use ide_db::helpers::{import_assets::ImportCandidate, mod_path_to_ast}; 4use ide_db::helpers::{
5 import_assets::{ImportCandidate, LocatedImport},
6 item_name, mod_path_to_ast,
7};
5use ide_db::RootDatabase; 8use ide_db::RootDatabase;
6use syntax::{ 9use syntax::{
7 ast, 10 ast,
8 ast::{make, ArgListOwner}, 11 ast::{make, ArgListOwner},
9 AstNode, 12 AstNode,
10}; 13};
11use test_utils::mark;
12 14
13use crate::{ 15use crate::{
14 assist_context::{AssistContext, Assists}, 16 assist_context::{AssistContext, Assists},
@@ -47,42 +49,42 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
47 let qualify_candidate = match candidate { 49 let qualify_candidate = match candidate {
48 ImportCandidate::Path(candidate) => { 50 ImportCandidate::Path(candidate) => {
49 if candidate.qualifier.is_some() { 51 if candidate.qualifier.is_some() {
50 mark::hit!(qualify_path_qualifier_start); 52 cov_mark::hit!(qualify_path_qualifier_start);
51 let path = ast::Path::cast(syntax_under_caret)?; 53 let path = ast::Path::cast(syntax_under_caret)?;
52 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); 54 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
53 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) 55 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
54 } else { 56 } else {
55 mark::hit!(qualify_path_unqualified_name); 57 cov_mark::hit!(qualify_path_unqualified_name);
56 let path = ast::Path::cast(syntax_under_caret)?; 58 let path = ast::Path::cast(syntax_under_caret)?;
57 let generics = path.segment()?.generic_arg_list(); 59 let generics = path.segment()?.generic_arg_list();
58 QualifyCandidate::UnqualifiedName(generics) 60 QualifyCandidate::UnqualifiedName(generics)
59 } 61 }
60 } 62 }
61 ImportCandidate::TraitAssocItem(_) => { 63 ImportCandidate::TraitAssocItem(_) => {
62 mark::hit!(qualify_path_trait_assoc_item); 64 cov_mark::hit!(qualify_path_trait_assoc_item);
63 let path = ast::Path::cast(syntax_under_caret)?; 65 let path = ast::Path::cast(syntax_under_caret)?;
64 let (qualifier, segment) = (path.qualifier()?, path.segment()?); 66 let (qualifier, segment) = (path.qualifier()?, path.segment()?);
65 QualifyCandidate::TraitAssocItem(qualifier, segment) 67 QualifyCandidate::TraitAssocItem(qualifier, segment)
66 } 68 }
67 ImportCandidate::TraitMethod(_) => { 69 ImportCandidate::TraitMethod(_) => {
68 mark::hit!(qualify_path_trait_method); 70 cov_mark::hit!(qualify_path_trait_method);
69 let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?; 71 let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
70 QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) 72 QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
71 } 73 }
72 }; 74 };
73 75
74 let group_label = group_label(candidate); 76 let group_label = group_label(candidate);
75 for (import, item) in proposed_imports { 77 for import in proposed_imports {
76 acc.add_group( 78 acc.add_group(
77 &group_label, 79 &group_label,
78 AssistId("qualify_path", AssistKind::QuickFix), 80 AssistId("qualify_path", AssistKind::QuickFix),
79 label(candidate, &import), 81 label(ctx.db(), candidate, &import),
80 range, 82 range,
81 |builder| { 83 |builder| {
82 qualify_candidate.qualify( 84 qualify_candidate.qualify(
83 |replace_with: String| builder.replace(range, replace_with), 85 |replace_with: String| builder.replace(range, replace_with),
84 import, 86 &import.import_path,
85 item, 87 import.item_to_import,
86 ) 88 )
87 }, 89 },
88 ); 90 );
@@ -98,8 +100,13 @@ enum QualifyCandidate<'db> {
98} 100}
99 101
100impl QualifyCandidate<'_> { 102impl QualifyCandidate<'_> {
101 fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) { 103 fn qualify(
102 let import = mod_path_to_ast(&import); 104 &self,
105 mut replacer: impl FnMut(String),
106 import: &hir::ModPath,
107 item: hir::ItemInNs,
108 ) {
109 let import = mod_path_to_ast(import);
103 match self { 110 match self {
104 QualifyCandidate::QualifierStart(segment, generics) => { 111 QualifyCandidate::QualifierStart(segment, generics) => {
105 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); 112 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
@@ -160,7 +167,9 @@ fn find_trait_method(
160) -> Option<hir::Function> { 167) -> Option<hir::Function> {
161 if let Some(hir::AssocItem::Function(method)) = 168 if let Some(hir::AssocItem::Function(method)) =
162 trait_.items(db).into_iter().find(|item: &hir::AssocItem| { 169 trait_.items(db).into_iter().find(|item: &hir::AssocItem| {
163 item.name(db).map(|name| name == trait_method_name.as_name()).unwrap_or(false) 170 item.name(db)
171 .map(|name| name.to_string() == trait_method_name.to_string())
172 .unwrap_or(false)
164 }) 173 })
165 { 174 {
166 Some(method) 175 Some(method)
@@ -182,23 +191,29 @@ fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
182fn group_label(candidate: &ImportCandidate) -> GroupLabel { 191fn group_label(candidate: &ImportCandidate) -> GroupLabel {
183 let name = match candidate { 192 let name = match candidate {
184 ImportCandidate::Path(it) => &it.name, 193 ImportCandidate::Path(it) => &it.name,
185 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, 194 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => {
195 &it.assoc_item_name
196 }
186 } 197 }
187 .text(); 198 .text();
188 GroupLabel(format!("Qualify {}", name)) 199 GroupLabel(format!("Qualify {}", name))
189} 200}
190 201
191fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String { 202fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String {
203 let display_path = match item_name(db, import.original_item) {
204 Some(display_path) => display_path.to_string(),
205 None => "{unknown}".to_string(),
206 };
192 match candidate { 207 match candidate {
193 ImportCandidate::Path(candidate) => { 208 ImportCandidate::Path(candidate) => {
194 if candidate.qualifier.is_some() { 209 if candidate.qualifier.is_some() {
195 format!("Qualify with `{}`", &import) 210 format!("Qualify with `{}`", display_path)
196 } else { 211 } else {
197 format!("Qualify as `{}`", &import) 212 format!("Qualify as `{}`", display_path)
198 } 213 }
199 } 214 }
200 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import), 215 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", display_path),
201 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import), 216 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", display_path),
202 } 217 }
203} 218}
204 219
@@ -210,7 +225,7 @@ mod tests {
210 225
211 #[test] 226 #[test]
212 fn applicable_when_found_an_import_partial() { 227 fn applicable_when_found_an_import_partial() {
213 mark::check!(qualify_path_unqualified_name); 228 cov_mark::check!(qualify_path_unqualified_name);
214 check_assist( 229 check_assist(
215 qualify_path, 230 qualify_path,
216 r" 231 r"
@@ -502,7 +517,7 @@ fn main() {
502 517
503 #[test] 518 #[test]
504 fn associated_struct_const() { 519 fn associated_struct_const() {
505 mark::check!(qualify_path_qualifier_start); 520 cov_mark::check!(qualify_path_qualifier_start);
506 check_assist( 521 check_assist(
507 qualify_path, 522 qualify_path,
508 r" 523 r"
@@ -603,7 +618,7 @@ fn main() {
603 618
604 #[test] 619 #[test]
605 fn associated_trait_const() { 620 fn associated_trait_const() {
606 mark::check!(qualify_path_trait_assoc_item); 621 cov_mark::check!(qualify_path_trait_assoc_item);
607 check_assist( 622 check_assist(
608 qualify_path, 623 qualify_path,
609 r" 624 r"
@@ -673,7 +688,7 @@ fn main() {
673 688
674 #[test] 689 #[test]
675 fn trait_method() { 690 fn trait_method() {
676 mark::check!(qualify_path_trait_method); 691 cov_mark::check!(qualify_path_trait_method);
677 check_assist( 692 check_assist(
678 qualify_path, 693 qualify_path,
679 r" 694 r"
diff --git a/crates/ide_assists/src/handlers/raw_string.rs b/crates/ide_assists/src/handlers/raw_string.rs
index d95267607..d0f1613f3 100644
--- a/crates/ide_assists/src/handlers/raw_string.rs
+++ b/crates/ide_assists/src/handlers/raw_string.rs
@@ -1,7 +1,6 @@
1use std::borrow::Cow; 1use std::borrow::Cow;
2 2
3use syntax::{ast, AstToken, TextRange, TextSize}; 3use syntax::{ast, AstToken, TextRange, TextSize};
4use test_utils::mark;
5 4
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 5use crate::{AssistContext, AssistId, AssistKind, Assists};
7 6
@@ -149,7 +148,7 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
149 let internal_text = &text[token.text_range_between_quotes()? - text_range.start()]; 148 let internal_text = &text[token.text_range_between_quotes()? - text_range.start()];
150 149
151 if existing_hashes == required_hashes(internal_text) { 150 if existing_hashes == required_hashes(internal_text) {
152 mark::hit!(cant_remove_required_hash); 151 cov_mark::hit!(cant_remove_required_hash);
153 return None; 152 return None;
154 } 153 }
155 154
@@ -182,8 +181,6 @@ fn test_required_hashes() {
182 181
183#[cfg(test)] 182#[cfg(test)]
184mod tests { 183mod tests {
185 use test_utils::mark;
186
187 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 184 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
188 185
189 use super::*; 186 use super::*;
@@ -396,7 +393,7 @@ string"###;
396 393
397 #[test] 394 #[test]
398 fn cant_remove_required_hash() { 395 fn cant_remove_required_hash() {
399 mark::check!(cant_remove_required_hash); 396 cov_mark::check!(cant_remove_required_hash);
400 check_assist_not_applicable( 397 check_assist_not_applicable(
401 remove_hash, 398 remove_hash,
402 r##" 399 r##"
diff --git a/crates/ide_assists/src/handlers/remove_unused_param.rs b/crates/ide_assists/src/handlers/remove_unused_param.rs
index c961680e2..2699d2861 100644
--- a/crates/ide_assists/src/handlers/remove_unused_param.rs
+++ b/crates/ide_assists/src/handlers/remove_unused_param.rs
@@ -4,7 +4,7 @@ use syntax::{
4 ast::{self, ArgListOwner}, 4 ast::{self, ArgListOwner},
5 AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T, 5 AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T,
6}; 6};
7use test_utils::mark; 7
8use SyntaxKind::WHITESPACE; 8use SyntaxKind::WHITESPACE;
9 9
10use crate::{ 10use crate::{
@@ -49,7 +49,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt
49 Definition::Local(local) 49 Definition::Local(local)
50 }; 50 };
51 if param_def.usages(&ctx.sema).at_least_one() { 51 if param_def.usages(&ctx.sema).at_least_one() {
52 mark::hit!(keep_used); 52 cov_mark::hit!(keep_used);
53 return None; 53 return None;
54 } 54 }
55 acc.add( 55 acc.add(
@@ -243,7 +243,7 @@ fn b2() { foo(9) }
243 243
244 #[test] 244 #[test]
245 fn keep_used() { 245 fn keep_used() {
246 mark::check!(keep_used); 246 cov_mark::check!(keep_used);
247 check_assist_not_applicable( 247 check_assist_not_applicable(
248 remove_unused_param, 248 remove_unused_param,
249 r#" 249 r#"
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs
index fba7d6ddb..794c89323 100644
--- a/crates/ide_assists/src/handlers/reorder_fields.rs
+++ b/crates/ide_assists/src/handlers/reorder_fields.rs
@@ -4,7 +4,6 @@ use rustc_hash::FxHashMap;
4use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; 4use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
5use ide_db::RootDatabase; 5use ide_db::RootDatabase;
6use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode}; 6use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode};
7use test_utils::mark;
8 7
9use crate::{AssistContext, AssistId, AssistKind, Assists}; 8use crate::{AssistContext, AssistId, AssistKind, Assists};
10 9
@@ -39,7 +38,7 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
39 }); 38 });
40 39
41 if sorted_fields == fields { 40 if sorted_fields == fields {
42 mark::hit!(reorder_sorted_fields); 41 cov_mark::hit!(reorder_sorted_fields);
43 return None; 42 return None;
44 } 43 }
45 44
@@ -109,15 +108,13 @@ fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashM
109 108
110#[cfg(test)] 109#[cfg(test)]
111mod tests { 110mod tests {
112 use test_utils::mark;
113
114 use crate::tests::{check_assist, check_assist_not_applicable}; 111 use crate::tests::{check_assist, check_assist_not_applicable};
115 112
116 use super::*; 113 use super::*;
117 114
118 #[test] 115 #[test]
119 fn reorder_sorted_fields() { 116 fn reorder_sorted_fields() {
120 mark::check!(reorder_sorted_fields); 117 cov_mark::check!(reorder_sorted_fields);
121 check_assist_not_applicable( 118 check_assist_not_applicable(
122 reorder_fields, 119 reorder_fields,
123 r#" 120 r#"
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs
index 309f291c8..edf4b0bfe 100644
--- a/crates/ide_assists/src/handlers/reorder_impl.rs
+++ b/crates/ide_assists/src/handlers/reorder_impl.rs
@@ -8,7 +8,6 @@ use syntax::{
8 ast::{self, NameOwner}, 8 ast::{self, NameOwner},
9 AstNode, 9 AstNode,
10}; 10};
11use test_utils::mark;
12 11
13use crate::{AssistContext, AssistId, AssistKind, Assists}; 12use crate::{AssistContext, AssistId, AssistKind, Assists};
14 13
@@ -71,7 +70,7 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
71 70
72 // Don't edit already sorted methods: 71 // Don't edit already sorted methods:
73 if methods == sorted { 72 if methods == sorted {
74 mark::hit!(not_applicable_if_sorted); 73 cov_mark::hit!(not_applicable_if_sorted);
75 return None; 74 return None;
76 } 75 }
77 76
@@ -121,15 +120,13 @@ fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {
121 120
122#[cfg(test)] 121#[cfg(test)]
123mod tests { 122mod tests {
124 use test_utils::mark;
125
126 use crate::tests::{check_assist, check_assist_not_applicable}; 123 use crate::tests::{check_assist, check_assist_not_applicable};
127 124
128 use super::*; 125 use super::*;
129 126
130 #[test] 127 #[test]
131 fn not_applicable_if_sorted() { 128 fn not_applicable_if_sorted() {
132 mark::check!(not_applicable_if_sorted); 129 cov_mark::check!(not_applicable_if_sorted);
133 check_assist_not_applicable( 130 check_assist_not_applicable(
134 reorder_impl, 131 reorder_impl,
135 r#" 132 r#"
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index c69bc5cac..88fe2fe90 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -1,5 +1,6 @@
1use hir::ModuleDef;
1use ide_db::helpers::mod_path_to_ast; 2use ide_db::helpers::mod_path_to_ast;
2use ide_db::imports_locator; 3use ide_db::items_locator;
3use itertools::Itertools; 4use itertools::Itertools;
4use syntax::{ 5use syntax::{
5 ast::{self, make, AstNode, NameOwner}, 6 ast::{self, make, AstNode, NameOwner},
@@ -64,22 +65,20 @@ pub(crate) fn replace_derive_with_manual_impl(
64 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; 65 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
65 let current_crate = current_module.krate(); 66 let current_crate = current_module.krate();
66 67
67 let found_traits = imports_locator::find_exact_imports( 68 let found_traits =
68 &ctx.sema, 69 items_locator::with_exact_name(&ctx.sema, current_crate, trait_token.text().to_string())
69 current_crate, 70 .into_iter()
70 trait_token.text().to_string(), 71 .filter_map(|item| match ModuleDef::from(item.as_module_def_id()?) {
71 ) 72 ModuleDef::Trait(trait_) => Some(trait_),
72 .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { 73 _ => None,
73 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), 74 })
74 _ => None, 75 .flat_map(|trait_| {
75 }) 76 current_module
76 .flat_map(|trait_| { 77 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
77 current_module 78 .as_ref()
78 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) 79 .map(mod_path_to_ast)
79 .as_ref() 80 .zip(Some(trait_))
80 .map(mod_path_to_ast) 81 });
81 .zip(Some(trait_))
82 });
83 82
84 let mut no_traits_found = true; 83 let mut no_traits_found = true;
85 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { 84 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
diff --git a/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
index 27da28bc0..50b05ab0b 100644
--- a/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
+++ b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
@@ -3,7 +3,6 @@ use hir::known;
3use ide_db::helpers::FamousDefs; 3use ide_db::helpers::FamousDefs;
4use stdx::format_to; 4use stdx::format_to;
5use syntax::{ast, AstNode}; 5use syntax::{ast, AstNode};
6use test_utils::mark;
7 6
8use crate::{AssistContext, AssistId, AssistKind, Assists}; 7use crate::{AssistContext, AssistId, AssistKind, Assists};
9 8
@@ -34,7 +33,7 @@ pub(crate) fn replace_for_loop_with_for_each(acc: &mut Assists, ctx: &AssistCont
34 let pat = for_loop.pat()?; 33 let pat = for_loop.pat()?;
35 let body = for_loop.loop_body()?; 34 let body = for_loop.loop_body()?;
36 if body.syntax().text_range().start() < ctx.offset() { 35 if body.syntax().text_range().start() < ctx.offset() {
37 mark::hit!(not_available_in_body); 36 cov_mark::hit!(not_available_in_body);
38 return None; 37 return None;
39 } 38 }
40 39
@@ -187,7 +186,7 @@ fn main() {
187 186
188 #[test] 187 #[test]
189 fn not_available_in_body() { 188 fn not_available_in_body() {
190 mark::check!(not_available_in_body); 189 cov_mark::check!(not_available_in_body);
191 check_assist_not_applicable( 190 check_assist_not_applicable(
192 replace_for_loop_with_for_each, 191 replace_for_loop_with_for_each,
193 r" 192 r"
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
index f3bc6cf39..36d2e0331 100644
--- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -1,6 +1,5 @@
1use ide_db::helpers::insert_use::{insert_use, ImportScope}; 1use ide_db::helpers::insert_use::{insert_use, ImportScope};
2use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; 2use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode};
3use test_utils::mark;
4 3
5use crate::{AssistContext, AssistId, AssistKind, Assists}; 4use crate::{AssistContext, AssistId, AssistKind, Assists};
6 5
@@ -27,7 +26,7 @@ pub(crate) fn replace_qualified_name_with_use(
27 return None; 26 return None;
28 } 27 }
29 if path.qualifier().is_none() { 28 if path.qualifier().is_none() {
30 mark::hit!(dont_import_trivial_paths); 29 cov_mark::hit!(dont_import_trivial_paths);
31 return None; 30 return None;
32 } 31 }
33 32
@@ -44,7 +43,7 @@ pub(crate) fn replace_qualified_name_with_use(
44 let mut rewriter = SyntaxRewriter::default(); 43 let mut rewriter = SyntaxRewriter::default();
45 shorten_paths(&mut rewriter, syntax.clone(), &path); 44 shorten_paths(&mut rewriter, syntax.clone(), &path);
46 if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { 45 if let Some(ref import_scope) = ImportScope::from(syntax.clone()) {
47 rewriter += insert_use(import_scope, path, ctx.config.insert_use.merge); 46 rewriter += insert_use(import_scope, path, ctx.config.insert_use);
48 builder.rewrite(rewriter); 47 builder.rewrite(rewriter);
49 } 48 }
50 }, 49 },
@@ -458,7 +457,7 @@ impl Debug for Foo {
458 457
459 #[test] 458 #[test]
460 fn dont_import_trivial_paths() { 459 fn dont_import_trivial_paths() {
461 mark::check!(dont_import_trivial_paths); 460 cov_mark::check!(dont_import_trivial_paths);
462 check_assist_not_applicable( 461 check_assist_not_applicable(
463 replace_qualified_name_with_use, 462 replace_qualified_name_with_use,
464 r" 463 r"
diff --git a/crates/ide_assists/src/handlers/replace_string_with_char.rs b/crates/ide_assists/src/handlers/replace_string_with_char.rs
index 317318c24..634b9c0b7 100644
--- a/crates/ide_assists/src/handlers/replace_string_with_char.rs
+++ b/crates/ide_assists/src/handlers/replace_string_with_char.rs
@@ -25,13 +25,16 @@ pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext) -
25 if value.chars().take(2).count() != 1 { 25 if value.chars().take(2).count() != 1 {
26 return None; 26 return None;
27 } 27 }
28 let quote_offets = token.quote_offsets()?;
28 29
29 acc.add( 30 acc.add(
30 AssistId("replace_string_with_char", AssistKind::RefactorRewrite), 31 AssistId("replace_string_with_char", AssistKind::RefactorRewrite),
31 "Replace string with char", 32 "Replace string with char",
32 target, 33 target,
33 |edit| { 34 |edit| {
34 edit.replace(token.syntax().text_range(), format!("'{}'", value)); 35 let (left, right) = quote_offets.quotes;
36 edit.replace(left, String::from('\''));
37 edit.replace(right, String::from('\''));
35 }, 38 },
36 ) 39 )
37} 40}
@@ -47,10 +50,10 @@ mod tests {
47 check_assist_target( 50 check_assist_target(
48 replace_string_with_char, 51 replace_string_with_char,
49 r#" 52 r#"
50 fn f() { 53fn f() {
51 let s = "$0c"; 54 let s = "$0c";
52 } 55}
53 "#, 56"#,
54 r#""c""#, 57 r#""c""#,
55 ); 58 );
56 } 59 }
@@ -60,15 +63,15 @@ mod tests {
60 check_assist( 63 check_assist(
61 replace_string_with_char, 64 replace_string_with_char,
62 r#" 65 r#"
63 fn f() { 66fn f() {
64 let s = "$0c"; 67 let s = "$0c";
65 } 68}
66 "#, 69"#,
67 r##" 70 r##"
68 fn f() { 71fn f() {
69 let s = 'c'; 72 let s = 'c';
70 } 73}
71 "##, 74"##,
72 ) 75 )
73 } 76 }
74 77
@@ -77,15 +80,15 @@ mod tests {
77 check_assist( 80 check_assist(
78 replace_string_with_char, 81 replace_string_with_char,
79 r#" 82 r#"
80 fn f() { 83fn f() {
81 let s = "$0😀"; 84 let s = "$0😀";
82 } 85}
83 "#, 86"#,
84 r##" 87 r##"
85 fn f() { 88fn f() {
86 let s = '😀'; 89 let s = '😀';
87 } 90}
88 "##, 91"##,
89 ) 92 )
90 } 93 }
91 94
@@ -94,10 +97,10 @@ mod tests {
94 check_assist_not_applicable( 97 check_assist_not_applicable(
95 replace_string_with_char, 98 replace_string_with_char,
96 r#" 99 r#"
97 fn f() { 100fn f() {
98 let s = "$0test"; 101 let s = "$0test";
99 } 102}
100 "#, 103"#,
101 ) 104 )
102 } 105 }
103 106
@@ -106,15 +109,15 @@ mod tests {
106 check_assist( 109 check_assist(
107 replace_string_with_char, 110 replace_string_with_char,
108 r#" 111 r#"
109 fn f() { 112fn f() {
110 format!($0"x", 92) 113 format!($0"x", 92)
111 } 114}
112 "#, 115"#,
113 r##" 116 r##"
114 fn f() { 117fn f() {
115 format!('x', 92) 118 format!('x', 92)
116 } 119}
117 "##, 120"##,
118 ) 121 )
119 } 122 }
120 123
@@ -123,15 +126,66 @@ mod tests {
123 check_assist( 126 check_assist(
124 replace_string_with_char, 127 replace_string_with_char,
125 r#" 128 r#"
126 fn f() { 129fn f() {
127 find($0"x"); 130 find($0"x");
128 } 131}
129 "#, 132"#,
130 r##" 133 r##"
131 fn f() { 134fn f() {
132 find('x'); 135 find('x');
133 } 136}
134 "##, 137"##,
138 )
139 }
140
141 #[test]
142 fn replace_string_with_char_newline() {
143 check_assist(
144 replace_string_with_char,
145 r#"
146fn f() {
147 find($0"\n");
148}
149"#,
150 r##"
151fn f() {
152 find('\n');
153}
154"##,
155 )
156 }
157
158 #[test]
159 fn replace_string_with_char_unicode_escape() {
160 check_assist(
161 replace_string_with_char,
162 r#"
163fn f() {
164 find($0"\u{7FFF}");
165}
166"#,
167 r##"
168fn f() {
169 find('\u{7FFF}');
170}
171"##,
172 )
173 }
174
175 #[test]
176 fn replace_raw_string_with_char() {
177 check_assist(
178 replace_string_with_char,
179 r##"
180fn f() {
181 $0r#"X"#
182}
183"##,
184 r##"
185fn f() {
186 'X'
187}
188"##,
135 ) 189 )
136 } 190 }
137} 191}
diff --git a/crates/ide_assists/src/handlers/unmerge_use.rs b/crates/ide_assists/src/handlers/unmerge_use.rs
index 3dbef8e51..616af7c2e 100644
--- a/crates/ide_assists/src/handlers/unmerge_use.rs
+++ b/crates/ide_assists/src/handlers/unmerge_use.rs
@@ -3,7 +3,6 @@ use syntax::{
3 ast::{self, edit::AstNodeEdit, VisibilityOwner}, 3 ast::{self, edit::AstNodeEdit, VisibilityOwner},
4 AstNode, SyntaxKind, 4 AstNode, SyntaxKind,
5}; 5};
6use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 assist_context::{AssistContext, Assists}, 8 assist_context::{AssistContext, Assists},
@@ -27,7 +26,7 @@ pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
27 26
28 let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?; 27 let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?;
29 if tree_list.use_trees().count() < 2 { 28 if tree_list.use_trees().count() < 2 {
30 mark::hit!(skip_single_use_item); 29 cov_mark::hit!(skip_single_use_item);
31 return None; 30 return None;
32 } 31 }
33 32
@@ -89,7 +88,7 @@ mod tests {
89 88
90 #[test] 89 #[test]
91 fn skip_single_use_item() { 90 fn skip_single_use_item() {
92 mark::check!(skip_single_use_item); 91 cov_mark::check!(skip_single_use_item);
93 check_assist_not_applicable( 92 check_assist_not_applicable(
94 unmerge_use, 93 unmerge_use,
95 r" 94 r"
diff --git a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
index fec16fc49..e838630ea 100644
--- a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
+++ b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
@@ -4,7 +4,6 @@ use syntax::{
4 ast::{self, make, BlockExpr, Expr, LoopBodyOwner}, 4 ast::{self, make, BlockExpr, Expr, LoopBodyOwner},
5 match_ast, AstNode, SyntaxNode, 5 match_ast, AstNode, SyntaxNode,
6}; 6};
7use test_utils::mark;
8 7
9use crate::{AssistContext, AssistId, AssistKind, Assists}; 8use crate::{AssistContext, AssistId, AssistKind, Assists};
10 9
@@ -39,7 +38,7 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext)
39 let first_part_ret_type = ret_type_str.splitn(2, '<').next(); 38 let first_part_ret_type = ret_type_str.splitn(2, '<').next();
40 if let Some(ret_type_first_part) = first_part_ret_type { 39 if let Some(ret_type_first_part) = first_part_ret_type {
41 if ret_type_first_part.ends_with("Result") { 40 if ret_type_first_part.ends_with("Result") {
42 mark::hit!(wrap_return_type_in_result_simple_return_type_already_result); 41 cov_mark::hit!(wrap_return_type_in_result_simple_return_type_already_result);
43 return None; 42 return None;
44 } 43 }
45 } 44 }
@@ -367,7 +366,7 @@ fn foo() -> std::result::Result<i32$0, String> {
367 366
368 #[test] 367 #[test]
369 fn wrap_return_type_in_result_simple_return_type_already_result() { 368 fn wrap_return_type_in_result_simple_return_type_already_result() {
370 mark::check!(wrap_return_type_in_result_simple_return_type_already_result); 369 cov_mark::check!(wrap_return_type_in_result_simple_return_type_already_result);
371 check_assist_not_applicable( 370 check_assist_not_applicable(
372 wrap_return_type_in_result, 371 wrap_return_type_in_result,
373 r#" 372 r#"
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 9c8148462..ea62d5f5d 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -127,6 +127,7 @@ mod handlers {
127 mod flip_comma; 127 mod flip_comma;
128 mod flip_trait_bound; 128 mod flip_trait_bound;
129 mod generate_default_from_enum_variant; 129 mod generate_default_from_enum_variant;
130 mod generate_default_from_new;
130 mod generate_derive; 131 mod generate_derive;
131 mod generate_enum_is_method; 132 mod generate_enum_is_method;
132 mod generate_enum_projection_method; 133 mod generate_enum_projection_method;
@@ -189,6 +190,7 @@ mod handlers {
189 flip_comma::flip_comma, 190 flip_comma::flip_comma,
190 flip_trait_bound::flip_trait_bound, 191 flip_trait_bound::flip_trait_bound,
191 generate_default_from_enum_variant::generate_default_from_enum_variant, 192 generate_default_from_enum_variant::generate_default_from_enum_variant,
193 generate_default_from_new::generate_default_from_new,
192 generate_derive::generate_derive, 194 generate_derive::generate_derive,
193 generate_enum_is_method::generate_enum_is_method, 195 generate_enum_is_method::generate_enum_is_method,
194 generate_enum_projection_method::generate_enum_as_method, 196 generate_enum_projection_method::generate_enum_as_method,
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index b7f616760..a7a923beb 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -23,6 +23,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
23 insert_use: InsertUseConfig { 23 insert_use: InsertUseConfig {
24 merge: Some(MergeBehavior::Full), 24 merge: Some(MergeBehavior::Full),
25 prefix_kind: hir::PrefixKind::Plain, 25 prefix_kind: hir::PrefixKind::Plain,
26 group: true,
26 }, 27 },
27}; 28};
28 29
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 4f007aa48..304b5798f 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -440,6 +440,37 @@ impl Default for Version {
440} 440}
441 441
442#[test] 442#[test]
443fn doctest_generate_default_from_new() {
444 check_doc_test(
445 "generate_default_from_new",
446 r#####"
447struct Example { _inner: () }
448
449impl Example {
450 pub fn n$0ew() -> Self {
451 Self { _inner: () }
452 }
453}
454"#####,
455 r#####"
456struct Example { _inner: () }
457
458impl Example {
459 pub fn new() -> Self {
460 Self { _inner: () }
461 }
462}
463
464impl Default for Example {
465 fn default() -> Self {
466 Self::new()
467 }
468}
469"#####,
470 )
471}
472
473#[test]
443fn doctest_generate_derive() { 474fn doctest_generate_derive() {
444 check_doc_test( 475 check_doc_test(
445 "generate_derive", 476 "generate_derive",
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml
index c09101ccb..585ecca50 100644
--- a/crates/ide_completion/Cargo.toml
+++ b/crates/ide_completion/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13itertools = "0.10.0" 14itertools = "0.10.0"
14log = "0.4.8" 15log = "0.4.8"
15rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
@@ -21,11 +22,11 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
21base_db = { path = "../base_db", version = "0.0.0" } 22base_db = { path = "../base_db", version = "0.0.0" }
22ide_db = { path = "../ide_db", version = "0.0.0" } 23ide_db = { path = "../ide_db", version = "0.0.0" }
23profile = { path = "../profile", version = "0.0.0" } 24profile = { path = "../profile", version = "0.0.0" }
24test_utils = { path = "../test_utils", version = "0.0.0" }
25 25
26# completions crate should depend only on the top-level `hir` package. if you need 26# completions crate should depend only on the top-level `hir` package. if you need
27# something from some `hir_xxx` subpackage, reexport the API via `hir`. 27# something from some `hir_xxx` subpackage, reexport the API via `hir`.
28hir = { path = "../hir", version = "0.0.0" } 28hir = { path = "../hir", version = "0.0.0" }
29 29
30[dev-dependencies] 30[dev-dependencies]
31test_utils = { path = "../test_utils" }
31expect-test = "1.1" 32expect-test = "1.1"
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index 084d7721d..5ee9a9f07 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -2,7 +2,6 @@
2 2
3use hir::{HasVisibility, Type}; 3use hir::{HasVisibility, Type};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use test_utils::mark;
6 5
7use crate::{context::CompletionContext, Completions}; 6use crate::{context::CompletionContext, Completions};
8 7
@@ -19,7 +18,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
19 }; 18 };
20 19
21 if ctx.is_call { 20 if ctx.is_call {
22 mark::hit!(test_no_struct_field_completion_for_method_call); 21 cov_mark::hit!(test_no_struct_field_completion_for_method_call);
23 } else { 22 } else {
24 complete_fields(acc, ctx, &receiver_ty); 23 complete_fields(acc, ctx, &receiver_ty);
25 } 24 }
@@ -62,7 +61,6 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
62#[cfg(test)] 61#[cfg(test)]
63mod tests { 62mod tests {
64 use expect_test::{expect, Expect}; 63 use expect_test::{expect, Expect};
65 use test_utils::mark;
66 64
67 use crate::{test_utils::completion_list, CompletionKind}; 65 use crate::{test_utils::completion_list, CompletionKind};
68 66
@@ -122,7 +120,7 @@ impl A {
122 120
123 #[test] 121 #[test]
124 fn test_no_struct_field_completion_for_method_call() { 122 fn test_no_struct_field_completion_for_method_call() {
125 mark::check!(test_no_struct_field_completion_for_method_call); 123 cov_mark::check!(test_no_struct_field_completion_for_method_call);
126 check( 124 check(
127 r#" 125 r#"
128struct A { the_field: u32 } 126struct A { the_field: u32 }
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index da8375af9..391a11c91 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -21,6 +21,46 @@
21//! ``` 21//! ```
22//! 22//!
23//! Also completes associated items, that require trait imports. 23//! Also completes associated items, that require trait imports.
24//! If any unresolved and/or partially-qualified path predeces the input, it will be taken into account.
25//! Currently, only the imports with their import path ending with the whole qialifier will be proposed
26//! (no fuzzy matching for qualifier).
27//!
28//! ```
29//! mod foo {
30//! pub mod bar {
31//! pub struct Item;
32//!
33//! impl Item {
34//! pub const TEST_ASSOC: usize = 3;
35//! }
36//! }
37//! }
38//!
39//! fn main() {
40//! bar::Item::TEST_A$0
41//! }
42//! ```
43//! ->
44//! ```
45//! use foo::bar;
46//!
47//! mod foo {
48//! pub mod bar {
49//! pub struct Item;
50//!
51//! impl Item {
52//! pub const TEST_ASSOC: usize = 3;
53//! }
54//! }
55//! }
56//!
57//! fn main() {
58//! bar::Item::TEST_ASSOC
59//! }
60//! ```
61//!
62//! NOTE: currently, if an assoc item comes from a trait that's not currently imported and it also has an unresolved and/or partially-qualified path,
63//! no imports will be proposed.
24//! 64//!
25//! .Fuzzy search details 65//! .Fuzzy search details
26//! 66//!
@@ -48,14 +88,13 @@
48//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 88//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
49//! capability enabled. 89//! capability enabled.
50 90
51use hir::{AsAssocItem, ModPath, ScopeDef}; 91use hir::ModPath;
52use ide_db::helpers::{ 92use ide_db::helpers::{
53 import_assets::{ImportAssets, ImportCandidate}, 93 import_assets::{ImportAssets, ImportCandidate},
54 insert_use::ImportScope, 94 insert_use::ImportScope,
55}; 95};
56use rustc_hash::FxHashSet; 96use itertools::Itertools;
57use syntax::{AstNode, SyntaxNode, T}; 97use syntax::{AstNode, SyntaxNode, T};
58use test_utils::mark;
59 98
60use crate::{ 99use crate::{
61 context::CompletionContext, 100 context::CompletionContext,
@@ -93,50 +132,26 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
93 &ctx.sema, 132 &ctx.sema,
94 )?; 133 )?;
95 134
96 let scope_definitions = scope_definitions(ctx); 135 acc.add_all(
97 let mut all_mod_paths = import_assets 136 import_assets
98 .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind) 137 .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
99 .into_iter() 138 .into_iter()
100 .map(|(mod_path, item_in_ns)| { 139 .sorted_by_key(|located_import| {
101 let scope_item = match item_in_ns { 140 compute_fuzzy_completion_order_key(
102 hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()), 141 &located_import.import_path,
103 hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()), 142 &user_input_lowercased,
104 hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()), 143 )
105 }; 144 })
106 (mod_path, scope_item) 145 .filter_map(|import| {
107 }) 146 render_resolution_with_import(
108 .filter(|(_, proposed_def)| !scope_definitions.contains(proposed_def)) 147 RenderContext::new(ctx),
109 .collect::<Vec<_>>(); 148 ImportEdit { import, scope: import_scope.clone() },
110 all_mod_paths.sort_by_cached_key(|(mod_path, _)| { 149 )
111 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) 150 }),
112 }); 151 );
113
114 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
115 let import_for_trait_assoc_item = match definition {
116 ScopeDef::ModuleDef(module_def) => module_def
117 .as_assoc_item(ctx.db)
118 .and_then(|assoc| assoc.containing_trait(ctx.db))
119 .is_some(),
120 _ => false,
121 };
122 let import_edit = ImportEdit {
123 import_path,
124 import_scope: import_scope.clone(),
125 import_for_trait_assoc_item,
126 };
127 render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition)
128 }));
129 Some(()) 152 Some(())
130} 153}
131 154
132fn scope_definitions(ctx: &CompletionContext) -> FxHashSet<ScopeDef> {
133 let mut scope_definitions = FxHashSet::default();
134 ctx.scope.process_all_names(&mut |_, scope_def| {
135 scope_definitions.insert(scope_def);
136 });
137 scope_definitions
138}
139
140pub(crate) fn position_for_import<'a>( 155pub(crate) fn position_for_import<'a>(
141 ctx: &'a CompletionContext, 156 ctx: &'a CompletionContext,
142 import_candidate: Option<&ImportCandidate>, 157 import_candidate: Option<&ImportCandidate>,
@@ -161,23 +176,30 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
161 current_module, 176 current_module,
162 ctx.sema.type_of_expr(dot_receiver)?, 177 ctx.sema.type_of_expr(dot_receiver)?,
163 fuzzy_name, 178 fuzzy_name,
179 dot_receiver.syntax().clone(),
164 ) 180 )
165 } else { 181 } else {
166 let fuzzy_name_length = fuzzy_name.len(); 182 let fuzzy_name_length = fuzzy_name.len();
183 let approximate_node = match current_module.definition_source(ctx.db).value {
184 hir::ModuleSource::SourceFile(s) => s.syntax().clone(),
185 hir::ModuleSource::Module(m) => m.syntax().clone(),
186 hir::ModuleSource::BlockExpr(b) => b.syntax().clone(),
187 };
167 let assets_for_path = ImportAssets::for_fuzzy_path( 188 let assets_for_path = ImportAssets::for_fuzzy_path(
168 current_module, 189 current_module,
169 ctx.path_qual.clone(), 190 ctx.path_qual.clone(),
170 fuzzy_name, 191 fuzzy_name,
171 &ctx.sema, 192 &ctx.sema,
172 ); 193 approximate_node,
194 )?;
173 195
174 if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_)) 196 if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
175 && fuzzy_name_length < 2 197 && fuzzy_name_length < 2
176 { 198 {
177 mark::hit!(ignore_short_input_for_path); 199 cov_mark::hit!(ignore_short_input_for_path);
178 None 200 None
179 } else { 201 } else {
180 assets_for_path 202 Some(assets_for_path)
181 } 203 }
182 } 204 }
183} 205}
@@ -186,12 +208,12 @@ fn compute_fuzzy_completion_order_key(
186 proposed_mod_path: &ModPath, 208 proposed_mod_path: &ModPath,
187 user_input_lowercased: &str, 209 user_input_lowercased: &str,
188) -> usize { 210) -> usize {
189 mark::hit!(certain_fuzzy_order_test); 211 cov_mark::hit!(certain_fuzzy_order_test);
190 let proposed_import_name = match proposed_mod_path.segments().last() { 212 let import_name = match proposed_mod_path.segments().last() {
191 Some(name) => name.to_string().to_lowercase(), 213 Some(name) => name.to_string().to_lowercase(),
192 None => return usize::MAX, 214 None => return usize::MAX,
193 }; 215 };
194 match proposed_import_name.match_indices(user_input_lowercased).next() { 216 match import_name.match_indices(user_input_lowercased).next() {
195 Some((first_matching_index, _)) => first_matching_index, 217 Some((first_matching_index, _)) => first_matching_index,
196 None => usize::MAX, 218 None => usize::MAX,
197 } 219 }
@@ -200,7 +222,6 @@ fn compute_fuzzy_completion_order_key(
200#[cfg(test)] 222#[cfg(test)]
201mod tests { 223mod tests {
202 use expect_test::{expect, Expect}; 224 use expect_test::{expect, Expect};
203 use test_utils::mark;
204 225
205 use crate::{ 226 use crate::{
206 item::CompletionKind, 227 item::CompletionKind,
@@ -295,7 +316,7 @@ fn main() {
295 316
296 #[test] 317 #[test]
297 fn short_paths_are_ignored() { 318 fn short_paths_are_ignored() {
298 mark::check!(ignore_short_input_for_path); 319 cov_mark::check!(ignore_short_input_for_path);
299 320
300 check( 321 check(
301 r#" 322 r#"
@@ -319,7 +340,7 @@ fn main() {
319 340
320 #[test] 341 #[test]
321 fn fuzzy_completions_come_in_specific_order() { 342 fn fuzzy_completions_come_in_specific_order() {
322 mark::check!(certain_fuzzy_order_test); 343 cov_mark::check!(certain_fuzzy_order_test);
323 check( 344 check(
324 r#" 345 r#"
325//- /lib.rs crate:dep 346//- /lib.rs crate:dep
@@ -775,4 +796,155 @@ fn main() {
775}"#, 796}"#,
776 ); 797 );
777 } 798 }
799
800 #[test]
801 fn unresolved_qualifier() {
802 let fixture = r#"
803mod foo {
804 pub mod bar {
805 pub mod baz {
806 pub struct Item;
807 }
808 }
809}
810
811fn main() {
812 bar::baz::Ite$0
813}"#;
814
815 check(
816 fixture,
817 expect![[r#"
818 st foo::bar::baz::Item
819 "#]],
820 );
821
822 check_edit(
823 "Item",
824 fixture,
825 r#"
826 use foo::bar;
827
828 mod foo {
829 pub mod bar {
830 pub mod baz {
831 pub struct Item;
832 }
833 }
834 }
835
836 fn main() {
837 bar::baz::Item
838 }"#,
839 );
840 }
841
842 #[test]
843 fn unresolved_assoc_item_container() {
844 let fixture = r#"
845mod foo {
846 pub struct Item;
847
848 impl Item {
849 pub const TEST_ASSOC: usize = 3;
850 }
851}
852
853fn main() {
854 Item::TEST_A$0
855}"#;
856
857 check(
858 fixture,
859 expect![[r#"
860 ct TEST_ASSOC (foo::Item)
861 "#]],
862 );
863
864 check_edit(
865 "TEST_ASSOC",
866 fixture,
867 r#"
868use foo::Item;
869
870mod foo {
871 pub struct Item;
872
873 impl Item {
874 pub const TEST_ASSOC: usize = 3;
875 }
876}
877
878fn main() {
879 Item::TEST_ASSOC
880}"#,
881 );
882 }
883
884 #[test]
885 fn unresolved_assoc_item_container_with_path() {
886 let fixture = r#"
887mod foo {
888 pub mod bar {
889 pub struct Item;
890
891 impl Item {
892 pub const TEST_ASSOC: usize = 3;
893 }
894 }
895}
896
897fn main() {
898 bar::Item::TEST_A$0
899}"#;
900
901 check(
902 fixture,
903 expect![[r#"
904 ct TEST_ASSOC (foo::bar::Item)
905 "#]],
906 );
907
908 check_edit(
909 "TEST_ASSOC",
910 fixture,
911 r#"
912use foo::bar;
913
914mod foo {
915 pub mod bar {
916 pub struct Item;
917
918 impl Item {
919 pub const TEST_ASSOC: usize = 3;
920 }
921 }
922}
923
924fn main() {
925 bar::Item::TEST_ASSOC
926}"#,
927 );
928 }
929
930 #[test]
931 fn fuzzy_unresolved_path() {
932 check(
933 r#"
934mod foo {
935 pub mod bar {
936 pub struct Item;
937
938 impl Item {
939 pub const TEST_ASSOC: usize = 3;
940 }
941 }
942}
943
944fn main() {
945 bar::Ass$0
946}"#,
947 expect![[]],
948 )
949 }
778} 950}
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 03c6dd454..80aa9fb06 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -3,7 +3,6 @@
3use std::iter; 3use std::iter;
4 4
5use syntax::SyntaxKind; 5use syntax::SyntaxKind;
6use test_utils::mark;
7 6
8use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; 7use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
9 8
@@ -47,11 +46,11 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
47 46
48pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 47pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
49 if ctx.token.kind() == SyntaxKind::COMMENT { 48 if ctx.token.kind() == SyntaxKind::COMMENT {
50 mark::hit!(no_keyword_completion_in_comments); 49 cov_mark::hit!(no_keyword_completion_in_comments);
51 return; 50 return;
52 } 51 }
53 if ctx.record_lit_syntax.is_some() { 52 if ctx.record_lit_syntax.is_some() {
54 mark::hit!(no_keyword_completion_in_record_lit); 53 cov_mark::hit!(no_keyword_completion_in_record_lit);
55 return; 54 return;
56 } 55 }
57 56
@@ -172,7 +171,7 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
172 Some(cap) => { 171 Some(cap) => {
173 let tmp; 172 let tmp;
174 let snippet = if snippet.ends_with('}') && ctx.incomplete_let { 173 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
175 mark::hit!(let_semi); 174 cov_mark::hit!(let_semi);
176 tmp = format!("{};", snippet); 175 tmp = format!("{};", snippet);
177 &tmp 176 &tmp
178 } else { 177 } else {
@@ -188,7 +187,6 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
188#[cfg(test)] 187#[cfg(test)]
189mod tests { 188mod tests {
190 use expect_test::{expect, Expect}; 189 use expect_test::{expect, Expect};
191 use test_utils::mark;
192 190
193 use crate::{ 191 use crate::{
194 test_utils::{check_edit, completion_list}, 192 test_utils::{check_edit, completion_list},
@@ -494,7 +492,7 @@ fn quux() -> i32 {
494 492
495 #[test] 493 #[test]
496 fn no_keyword_completion_in_comments() { 494 fn no_keyword_completion_in_comments() {
497 mark::check!(no_keyword_completion_in_comments); 495 cov_mark::check!(no_keyword_completion_in_comments);
498 check( 496 check(
499 r#" 497 r#"
500fn test() { 498fn test() {
@@ -599,7 +597,7 @@ struct Foo {
599 597
600 #[test] 598 #[test]
601 fn skip_struct_initializer() { 599 fn skip_struct_initializer() {
602 mark::check!(no_keyword_completion_in_record_lit); 600 cov_mark::check!(no_keyword_completion_in_record_lit);
603 check( 601 check(
604 r#" 602 r#"
605struct Foo { 603struct Foo {
@@ -643,7 +641,7 @@ fn foo() {
643 641
644 #[test] 642 #[test]
645 fn let_semi() { 643 fn let_semi() {
646 mark::check!(let_semi); 644 cov_mark::check!(let_semi);
647 check_edit( 645 check_edit(
648 "match", 646 "match",
649 r#" 647 r#"
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index 9c34ed0b6..d45ad7944 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -187,6 +187,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
187 ctx, 187 ctx,
188 cap, 188 cap,
189 &dot_receiver, 189 &dot_receiver,
190 "err",
191 "Err(expr)",
192 &format!("Err({})", receiver_text),
193 )
194 .add_to(acc);
195
196 postfix_snippet(
197 ctx,
198 cap,
199 &dot_receiver,
190 "some", 200 "some",
191 "Some(expr)", 201 "Some(expr)",
192 &format!("Some({})", receiver_text), 202 &format!("Some({})", receiver_text),
@@ -325,6 +335,7 @@ fn main() {
325 sn match match expr {} 335 sn match match expr {}
326 sn box Box::new(expr) 336 sn box Box::new(expr)
327 sn ok Ok(expr) 337 sn ok Ok(expr)
338 sn err Err(expr)
328 sn some Some(expr) 339 sn some Some(expr)
329 sn dbg dbg!(expr) 340 sn dbg dbg!(expr)
330 sn dbgr dbg!(&expr) 341 sn dbgr dbg!(&expr)
@@ -357,6 +368,7 @@ fn main() {
357 sn match match expr {} 368 sn match match expr {}
358 sn box Box::new(expr) 369 sn box Box::new(expr)
359 sn ok Ok(expr) 370 sn ok Ok(expr)
371 sn err Err(expr)
360 sn some Some(expr) 372 sn some Some(expr)
361 sn dbg dbg!(expr) 373 sn dbg dbg!(expr)
362 sn dbgr dbg!(&expr) 374 sn dbgr dbg!(&expr)
@@ -380,6 +392,7 @@ fn main() {
380 sn match match expr {} 392 sn match match expr {}
381 sn box Box::new(expr) 393 sn box Box::new(expr)
382 sn ok Ok(expr) 394 sn ok Ok(expr)
395 sn err Err(expr)
383 sn some Some(expr) 396 sn some Some(expr)
384 sn dbg dbg!(expr) 397 sn dbg dbg!(expr)
385 sn dbgr dbg!(&expr) 398 sn dbgr dbg!(&expr)
@@ -408,6 +421,7 @@ fn main() {
408 sn match match expr {} 421 sn match match expr {}
409 sn box Box::new(expr) 422 sn box Box::new(expr)
410 sn ok Ok(expr) 423 sn ok Ok(expr)
424 sn err Err(expr)
411 sn some Some(expr) 425 sn some Some(expr)
412 sn dbg dbg!(expr) 426 sn dbg dbg!(expr)
413 sn dbgr dbg!(&expr) 427 sn dbgr dbg!(&expr)
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 72fb757b1..df74b739e 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -3,7 +3,6 @@
3use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; 3use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use syntax::AstNode; 5use syntax::AstNode;
6use test_utils::mark;
7 6
8use crate::{CompletionContext, Completions}; 7use crate::{CompletionContext, Completions};
9 8
@@ -39,7 +38,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
39 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { 38 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() {
40 if name_ref.syntax().text() == name.to_string().as_str() { 39 if name_ref.syntax().text() == name.to_string().as_str() {
41 // for `use self::foo$0`, don't suggest `foo` as a completion 40 // for `use self::foo$0`, don't suggest `foo` as a completion
42 mark::hit!(dont_complete_current_use); 41 cov_mark::hit!(dont_complete_current_use);
43 continue; 42 continue;
44 } 43 }
45 } 44 }
@@ -155,7 +154,6 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
155#[cfg(test)] 154#[cfg(test)]
156mod tests { 155mod tests {
157 use expect_test::{expect, Expect}; 156 use expect_test::{expect, Expect};
158 use test_utils::mark;
159 157
160 use crate::{ 158 use crate::{
161 test_utils::{check_edit, completion_list}, 159 test_utils::{check_edit, completion_list},
@@ -174,7 +172,7 @@ mod tests {
174 172
175 #[test] 173 #[test]
176 fn dont_complete_current_use() { 174 fn dont_complete_current_use() {
177 mark::check!(dont_complete_current_use); 175 cov_mark::check!(dont_complete_current_use);
178 check(r#"use self::foo$0;"#, expect![[""]]); 176 check(r#"use self::foo$0;"#, expect![[""]]);
179 } 177 }
180 178
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index e9d0ff665..044dfd160 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -2,7 +2,6 @@
2 2
3use hir::ScopeDef; 3use hir::ScopeDef;
4use syntax::AstNode; 4use syntax::AstNode;
5use test_utils::mark;
6 5
7use crate::{CompletionContext, Completions}; 6use crate::{CompletionContext, Completions};
8 7
@@ -30,13 +29,13 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
30 29
31 ctx.scope.process_all_names(&mut |name, res| { 30 ctx.scope.process_all_names(&mut |name, res| {
32 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { 31 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
33 mark::hit!(skip_lifetime_completion); 32 cov_mark::hit!(skip_lifetime_completion);
34 return; 33 return;
35 } 34 }
36 if ctx.use_item_syntax.is_some() { 35 if ctx.use_item_syntax.is_some() {
37 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { 36 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
38 if name_ref.syntax().text() == name.to_string().as_str() { 37 if name_ref.syntax().text() == name.to_string().as_str() {
39 mark::hit!(self_fulfilling_completion); 38 cov_mark::hit!(self_fulfilling_completion);
40 return; 39 return;
41 } 40 }
42 } 41 }
@@ -48,7 +47,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
48#[cfg(test)] 47#[cfg(test)]
49mod tests { 48mod tests {
50 use expect_test::{expect, Expect}; 49 use expect_test::{expect, Expect};
51 use test_utils::mark;
52 50
53 use crate::{ 51 use crate::{
54 test_utils::{check_edit, completion_list_with_config, TEST_CONFIG}, 52 test_utils::{check_edit, completion_list_with_config, TEST_CONFIG},
@@ -66,7 +64,7 @@ mod tests {
66 64
67 #[test] 65 #[test]
68 fn self_fulfilling_completion() { 66 fn self_fulfilling_completion() {
69 mark::check!(self_fulfilling_completion); 67 cov_mark::check!(self_fulfilling_completion);
70 check( 68 check(
71 r#" 69 r#"
72use foo$0 70use foo$0
@@ -185,7 +183,7 @@ fn quux() {
185 183
186 #[test] 184 #[test]
187 fn completes_if_prefix_is_keyword() { 185 fn completes_if_prefix_is_keyword() {
188 mark::check!(completes_if_prefix_is_keyword); 186 cov_mark::check!(completes_if_prefix_is_keyword);
189 check_edit( 187 check_edit(
190 "wherewolf", 188 "wherewolf",
191 r#" 189 r#"
@@ -223,7 +221,7 @@ fn main() {
223 221
224 #[test] 222 #[test]
225 fn does_not_complete_lifetimes() { 223 fn does_not_complete_lifetimes() {
226 mark::check!(skip_lifetime_completion); 224 cov_mark::check!(skip_lifetime_completion);
227 check( 225 check(
228 r#"fn quux<'a>() { $0 }"#, 226 r#"fn quux<'a>() { $0 }"#,
229 expect![[r#" 227 expect![[r#"
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 3db357855..17d9a3adf 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -7,7 +7,7 @@ use syntax::{
7 algo::find_node_at_offset, ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode, 7 algo::find_node_at_offset, ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode,
8 SyntaxToken, TextRange, TextSize, 8 SyntaxToken, TextRange, TextSize,
9}; 9};
10use test_utils::mark; 10
11use text_edit::Indel; 11use text_edit::Indel;
12 12
13use crate::{ 13use crate::{
@@ -240,7 +240,7 @@ impl<'a> CompletionContext<'a> {
240 // check kind of macro-expanded token, but use range of original token 240 // check kind of macro-expanded token, but use range of original token
241 let kind = self.token.kind(); 241 let kind = self.token.kind();
242 if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() { 242 if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() {
243 mark::hit!(completes_if_prefix_is_keyword); 243 cov_mark::hit!(completes_if_prefix_is_keyword);
244 self.original_token.text_range() 244 self.original_token.text_range()
245 } else { 245 } else {
246 TextRange::empty(self.position.offset) 246 TextRange::empty(self.position.offset)
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index 884711f11..14afec603 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -2,15 +2,16 @@
2 2
3use std::fmt; 3use std::fmt;
4 4
5use hir::{Documentation, ModPath, Mutability}; 5use hir::{Documentation, Mutability};
6use ide_db::{ 6use ide_db::{
7 helpers::{ 7 helpers::{
8 insert_use::{self, ImportScope, MergeBehavior}, 8 import_assets::LocatedImport,
9 insert_use::{self, ImportScope, InsertUseConfig},
9 mod_path_to_ast, SnippetCap, 10 mod_path_to_ast, SnippetCap,
10 }, 11 },
11 SymbolKind, 12 SymbolKind,
12}; 13};
13use stdx::{impl_from, never}; 14use stdx::{format_to, impl_from, never};
14use syntax::{algo, TextRange}; 15use syntax::{algo, TextRange};
15use text_edit::TextEdit; 16use text_edit::TextEdit;
16 17
@@ -62,12 +63,18 @@ pub struct CompletionItem {
62 /// after completion. 63 /// after completion.
63 trigger_call_info: bool, 64 trigger_call_info: bool,
64 65
65 /// Score is useful to pre select or display in better order completion items 66 /// We use this to sort completion. Relevance records facts like "do the
66 score: Option<CompletionScore>, 67 /// types align precisely?". We can't sort by relevances directly, they are
68 /// only partially ordered.
69 ///
70 /// Note that Relevance ignores fuzzy match score. We compute Relevance for
71 /// all possible items, and then separately build an ordered completion list
72 /// based on relevance and fuzzy matching with the already typed identifier.
73 relevance: Relevance,
67 74
68 /// Indicates that a reference or mutable reference to this variable is a 75 /// Indicates that a reference or mutable reference to this variable is a
69 /// possible match. 76 /// possible match.
70 ref_match: Option<(Mutability, CompletionScore)>, 77 ref_match: Option<Mutability>,
71 78
72 /// The import data to add to completion's edits. 79 /// The import data to add to completion's edits.
73 import_to_add: Option<ImportEdit>, 80 import_to_add: Option<ImportEdit>,
@@ -100,8 +107,11 @@ impl fmt::Debug for CompletionItem {
100 if self.deprecated { 107 if self.deprecated {
101 s.field("deprecated", &true); 108 s.field("deprecated", &true);
102 } 109 }
103 if let Some(score) = &self.score { 110 if self.relevance.is_relevant() {
104 s.field("score", score); 111 s.field("relevance", &self.relevance);
112 }
113 if let Some(mutability) = &self.ref_match {
114 s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref()));
105 } 115 }
106 if self.trigger_call_info { 116 if self.trigger_call_info {
107 s.field("trigger_call_info", &true); 117 s.field("trigger_call_info", &true);
@@ -118,6 +128,36 @@ pub enum CompletionScore {
118 TypeAndNameMatch, 128 TypeAndNameMatch,
119} 129}
120 130
131#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
132pub struct Relevance {
133 /// This is set in cases like these:
134 ///
135 /// ```
136 /// fn f(spam: String) {}
137 /// fn main {
138 /// let spam = 92;
139 /// f($0) // name of local matches the name of param
140 /// }
141 /// ```
142 pub exact_name_match: bool,
143 /// This is set in cases like these:
144 ///
145 /// ```
146 /// fn f(spam: String) {}
147 /// fn main {
148 /// let foo = String::new();
149 /// f($0) // type of local matches the type of param
150 /// }
151 /// ```
152 pub exact_type_match: bool,
153}
154
155impl Relevance {
156 pub fn is_relevant(&self) -> bool {
157 self != &Relevance::default()
158 }
159}
160
121#[derive(Debug, Clone, Copy, PartialEq, Eq)] 161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum CompletionItemKind { 162pub enum CompletionItemKind {
123 SymbolKind(SymbolKind), 163 SymbolKind(SymbolKind),
@@ -207,9 +247,9 @@ impl CompletionItem {
207 lookup: None, 247 lookup: None,
208 kind: None, 248 kind: None,
209 text_edit: None, 249 text_edit: None,
210 deprecated: None, 250 deprecated: false,
211 trigger_call_info: None, 251 trigger_call_info: None,
212 score: None, 252 relevance: Relevance::default(),
213 ref_match: None, 253 ref_match: None,
214 import_to_add: None, 254 import_to_add: None,
215 } 255 }
@@ -252,15 +292,15 @@ impl CompletionItem {
252 self.deprecated 292 self.deprecated
253 } 293 }
254 294
255 pub fn score(&self) -> Option<CompletionScore> { 295 pub fn relevance(&self) -> Relevance {
256 self.score 296 self.relevance
257 } 297 }
258 298
259 pub fn trigger_call_info(&self) -> bool { 299 pub fn trigger_call_info(&self) -> bool {
260 self.trigger_call_info 300 self.trigger_call_info
261 } 301 }
262 302
263 pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> { 303 pub fn ref_match(&self) -> Option<Mutability> {
264 self.ref_match 304 self.ref_match
265 } 305 }
266 306
@@ -272,22 +312,18 @@ impl CompletionItem {
272/// An extra import to add after the completion is applied. 312/// An extra import to add after the completion is applied.
273#[derive(Debug, Clone)] 313#[derive(Debug, Clone)]
274pub struct ImportEdit { 314pub struct ImportEdit {
275 pub import_path: ModPath, 315 pub import: LocatedImport,
276 pub import_scope: ImportScope, 316 pub scope: ImportScope,
277 pub import_for_trait_assoc_item: bool,
278} 317}
279 318
280impl ImportEdit { 319impl ImportEdit {
281 /// Attempts to insert the import to the given scope, producing a text edit. 320 /// Attempts to insert the import to the given scope, producing a text edit.
282 /// May return no edit in edge cases, such as scope already containing the import. 321 /// May return no edit in edge cases, such as scope already containing the import.
283 pub fn to_text_edit(&self, merge_behavior: Option<MergeBehavior>) -> Option<TextEdit> { 322 pub fn to_text_edit(&self, cfg: InsertUseConfig) -> Option<TextEdit> {
284 let _p = profile::span("ImportEdit::to_text_edit"); 323 let _p = profile::span("ImportEdit::to_text_edit");
285 324
286 let rewriter = insert_use::insert_use( 325 let rewriter =
287 &self.import_scope, 326 insert_use::insert_use(&self.scope, mod_path_to_ast(&self.import.import_path), cfg);
288 mod_path_to_ast(&self.import_path),
289 merge_behavior,
290 );
291 let old_ast = rewriter.rewrite_root()?; 327 let old_ast = rewriter.rewrite_root()?;
292 let mut import_insert = TextEdit::builder(); 328 let mut import_insert = TextEdit::builder();
293 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert); 329 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
@@ -311,10 +347,10 @@ pub(crate) struct Builder {
311 lookup: Option<String>, 347 lookup: Option<String>,
312 kind: Option<CompletionItemKind>, 348 kind: Option<CompletionItemKind>,
313 text_edit: Option<TextEdit>, 349 text_edit: Option<TextEdit>,
314 deprecated: Option<bool>, 350 deprecated: bool,
315 trigger_call_info: Option<bool>, 351 trigger_call_info: Option<bool>,
316 score: Option<CompletionScore>, 352 relevance: Relevance,
317 ref_match: Option<(Mutability, CompletionScore)>, 353 ref_match: Option<Mutability>,
318} 354}
319 355
320impl Builder { 356impl Builder {
@@ -325,20 +361,19 @@ impl Builder {
325 let mut lookup = self.lookup; 361 let mut lookup = self.lookup;
326 let mut insert_text = self.insert_text; 362 let mut insert_text = self.insert_text;
327 363
328 if let Some(import_to_add) = self.import_to_add.as_ref() { 364 if let Some(original_path) = self
329 if import_to_add.import_for_trait_assoc_item { 365 .import_to_add
330 lookup = lookup.or_else(|| Some(label.clone())); 366 .as_ref()
331 insert_text = insert_text.or_else(|| Some(label.clone())); 367 .and_then(|import_edit| import_edit.import.original_path.as_ref())
332 label = format!("{} ({})", label, import_to_add.import_path); 368 {
369 lookup = lookup.or_else(|| Some(label.clone()));
370 insert_text = insert_text.or_else(|| Some(label.clone()));
371
372 let original_path_label = original_path.to_string();
373 if original_path_label.ends_with(&label) {
374 label = original_path_label;
333 } else { 375 } else {
334 let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); 376 format_to!(label, " ({})", original_path)
335 let _ = import_path_without_last_segment.pop_segment();
336
337 if !import_path_without_last_segment.segments().is_empty() {
338 lookup = lookup.or_else(|| Some(label.clone()));
339 insert_text = insert_text.or_else(|| Some(label.clone()));
340 label = format!("{}::{}", import_path_without_last_segment, label);
341 }
342 } 377 }
343 } 378 }
344 379
@@ -359,9 +394,9 @@ impl Builder {
359 lookup, 394 lookup,
360 kind: self.kind, 395 kind: self.kind,
361 completion_kind: self.completion_kind, 396 completion_kind: self.completion_kind,
362 deprecated: self.deprecated.unwrap_or(false), 397 deprecated: self.deprecated,
363 trigger_call_info: self.trigger_call_info.unwrap_or(false), 398 trigger_call_info: self.trigger_call_info.unwrap_or(false),
364 score: self.score, 399 relevance: self.relevance,
365 ref_match: self.ref_match, 400 ref_match: self.ref_match,
366 import_to_add: self.import_to_add, 401 import_to_add: self.import_to_add,
367 } 402 }
@@ -419,11 +454,11 @@ impl Builder {
419 self 454 self
420 } 455 }
421 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { 456 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder {
422 self.deprecated = Some(deprecated); 457 self.deprecated = deprecated;
423 self 458 self
424 } 459 }
425 pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder { 460 pub(crate) fn set_relevance(mut self, relevance: Relevance) -> Builder {
426 self.score = Some(score); 461 self.relevance = relevance;
427 self 462 self
428 } 463 }
429 pub(crate) fn trigger_call_info(mut self) -> Builder { 464 pub(crate) fn trigger_call_info(mut self) -> Builder {
@@ -434,17 +469,8 @@ impl Builder {
434 self.import_to_add = import_to_add; 469 self.import_to_add = import_to_add;
435 self 470 self
436 } 471 }
437 pub(crate) fn set_ref_match( 472 pub(crate) fn ref_match(mut self, mutability: Mutability) -> Builder {
438 mut self, 473 self.ref_match = Some(mutability);
439 ref_match: Option<(Mutability, CompletionScore)>,
440 ) -> Builder {
441 self.ref_match = ref_match;
442 self 474 self
443 } 475 }
444} 476}
445
446impl<'a> Into<CompletionItem> for Builder {
447 fn into(self) -> CompletionItem {
448 self.build()
449 }
450}
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 76f31de9e..d46f521a0 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -13,7 +13,9 @@ mod completions;
13 13
14use completions::flyimport::position_for_import; 14use completions::flyimport::position_for_import;
15use ide_db::{ 15use ide_db::{
16 base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase, 16 base_db::FilePosition,
17 helpers::{import_assets::LocatedImport, insert_use::ImportScope},
18 items_locator, RootDatabase,
17}; 19};
18use text_edit::TextEdit; 20use text_edit::TextEdit;
19 21
@@ -21,7 +23,10 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi
21 23
22pub use crate::{ 24pub use crate::{
23 config::CompletionConfig, 25 config::CompletionConfig,
24 item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat}, 26 item::{
27 CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat,
28 Relevance,
29 },
25}; 30};
26 31
27//FIXME: split the following feature into fine-grained features. 32//FIXME: split the following feature into fine-grained features.
@@ -139,25 +144,27 @@ pub fn resolve_completion_edits(
139 position: FilePosition, 144 position: FilePosition,
140 full_import_path: &str, 145 full_import_path: &str,
141 imported_name: String, 146 imported_name: String,
142 import_for_trait_assoc_item: bool,
143) -> Option<Vec<TextEdit>> { 147) -> Option<Vec<TextEdit>> {
144 let ctx = CompletionContext::new(db, position, config)?; 148 let ctx = CompletionContext::new(db, position, config)?;
145 let position_for_import = position_for_import(&ctx, None)?; 149 let position_for_import = position_for_import(&ctx, None)?;
146 let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?; 150 let scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
147 151
148 let current_module = ctx.sema.scope(position_for_import).module()?; 152 let current_module = ctx.sema.scope(position_for_import).module()?;
149 let current_crate = current_module.krate(); 153 let current_crate = current_module.krate();
150 154
151 let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name) 155 let (import_path, item_to_import) =
152 .filter_map(|candidate| { 156 items_locator::with_exact_name(&ctx.sema, current_crate, imported_name)
153 let item: hir::ItemInNs = candidate.either(Into::into, Into::into); 157 .into_iter()
154 current_module.find_use_path_prefixed(db, item, config.insert_use.prefix_kind) 158 .filter_map(|candidate| {
155 }) 159 current_module
156 .find(|mod_path| mod_path.to_string() == full_import_path)?; 160 .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind)
161 .zip(Some(candidate))
162 })
163 .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
164 let import =
165 LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path));
157 166
158 ImportEdit { import_path, import_scope, import_for_trait_assoc_item } 167 ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit])
159 .to_text_edit(config.insert_use.merge)
160 .map(|edit| vec![edit])
161} 168}
162 169
163#[cfg(test)] 170#[cfg(test)]
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index eddaaa6f3..8c8b149a1 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -13,13 +13,15 @@ mod builder_ext;
13use hir::{ 13use hir::{
14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, 14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type,
15}; 15};
16use ide_db::{helpers::SnippetCap, RootDatabase, SymbolKind}; 16use ide_db::{
17 helpers::{item_name, SnippetCap},
18 RootDatabase, SymbolKind,
19};
17use syntax::TextRange; 20use syntax::TextRange;
18use test_utils::mark;
19 21
20use crate::{ 22use crate::{
21 item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 23 item::{ImportEdit, Relevance},
22 CompletionScore, 24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
23}; 25};
24 26
25use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; 27use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
@@ -51,18 +53,20 @@ pub(crate) fn render_resolution<'a>(
51pub(crate) fn render_resolution_with_import<'a>( 53pub(crate) fn render_resolution_with_import<'a>(
52 ctx: RenderContext<'a>, 54 ctx: RenderContext<'a>,
53 import_edit: ImportEdit, 55 import_edit: ImportEdit,
54 resolution: &ScopeDef,
55) -> Option<CompletionItem> { 56) -> Option<CompletionItem> {
57 let resolution = ScopeDef::from(import_edit.import.original_item);
56 let local_name = match resolution { 58 let local_name = match resolution {
57 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), 59 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(),
58 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), 60 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(),
59 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), 61 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(),
60 _ => import_edit.import_path.segments().last()?.to_string(), 62 _ => item_name(ctx.db(), import_edit.import.original_item)?.to_string(),
61 }; 63 };
62 Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| { 64 Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map(
63 item.completion_kind = CompletionKind::Magic; 65 |mut item| {
64 item 66 item.completion_kind = CompletionKind::Magic;
65 }) 67 item
68 },
69 )
66} 70}
67 71
68/// Interface for data and methods required for items rendering. 72/// Interface for data and methods required for items rendering.
@@ -113,13 +117,13 @@ impl<'a> RenderContext<'a> {
113 node.docs(self.db()) 117 node.docs(self.db())
114 } 118 }
115 119
116 fn active_name_and_type(&self) -> Option<(String, Type)> { 120 fn expected_name_and_type(&self) -> Option<(String, Type)> {
117 if let Some(record_field) = &self.completion.record_field_syntax { 121 if let Some(record_field) = &self.completion.record_field_syntax {
118 mark::hit!(record_field_type_match); 122 cov_mark::hit!(record_field_type_match);
119 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; 123 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?;
120 Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) 124 Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db())))
121 } else if let Some(active_parameter) = &self.completion.active_parameter { 125 } else if let Some(active_parameter) = &self.completion.active_parameter {
122 mark::hit!(active_param_type_match); 126 cov_mark::hit!(active_param_type_match);
123 Some((active_parameter.name.clone(), active_parameter.ty.clone())) 127 Some((active_parameter.name.clone(), active_parameter.ty.clone()))
124 } else { 128 } else {
125 None 129 None
@@ -151,8 +155,8 @@ impl<'a> Render<'a> {
151 .set_documentation(field.docs(self.ctx.db())) 155 .set_documentation(field.docs(self.ctx.db()))
152 .set_deprecated(is_deprecated); 156 .set_deprecated(is_deprecated);
153 157
154 if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) { 158 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) {
155 item = item.set_score(score); 159 item = item.set_relevance(relevance);
156 } 160 }
157 161
158 item.build() 162 item.build()
@@ -242,16 +246,23 @@ impl<'a> Render<'a> {
242 } 246 }
243 }; 247 };
244 248
245 let mut ref_match = None;
246 if let ScopeDef::Local(local) = resolution { 249 if let ScopeDef::Local(local) = resolution {
247 if let Some((active_name, active_type)) = self.ctx.active_name_and_type() { 250 let ty = local.ty(self.ctx.db());
248 let ty = local.ty(self.ctx.db()); 251 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) {
249 if let Some(score) = 252 item = item.set_relevance(relevance)
250 compute_score_from_active(&active_type, &active_name, &ty, &local_name) 253 }
251 { 254 if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() {
252 item = item.set_score(score); 255 if let Some(ty_without_ref) = expected_type.remove_ref() {
256 if ty_without_ref == ty {
257 cov_mark::hit!(suggest_ref);
258 let mutability = if expected_type.is_mutable_reference() {
259 Mutability::Mut
260 } else {
261 Mutability::Shared
262 };
263 item = item.ref_match(mutability)
264 }
253 } 265 }
254 ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name);
255 } 266 }
256 } 267 }
257 268
@@ -269,7 +280,7 @@ impl<'a> Render<'a> {
269 _ => false, 280 _ => false,
270 }; 281 };
271 if has_non_default_type_params { 282 if has_non_default_type_params {
272 mark::hit!(inserts_angle_brackets_for_generics); 283 cov_mark::hit!(inserts_angle_brackets_for_generics);
273 item = item 284 item = item
274 .lookup_by(local_name.clone()) 285 .lookup_by(local_name.clone())
275 .label(format!("{}<…>", local_name)) 286 .label(format!("{}<…>", local_name))
@@ -281,7 +292,6 @@ impl<'a> Render<'a> {
281 Some( 292 Some(
282 item.kind(kind) 293 item.kind(kind)
283 .add_import(import_to_add) 294 .add_import(import_to_add)
284 .set_ref_match(ref_match)
285 .set_documentation(self.docs(resolution)) 295 .set_documentation(self.docs(resolution))
286 .set_deprecated(self.is_deprecated(resolution)) 296 .set_deprecated(self.is_deprecated(resolution))
287 .build(), 297 .build(),
@@ -313,56 +323,23 @@ impl<'a> Render<'a> {
313 } 323 }
314} 324}
315 325
316fn compute_score_from_active( 326fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<Relevance> {
317 active_type: &Type, 327 let (expected_name, expected_type) = ctx.expected_name_and_type()?;
318 active_name: &str, 328 let mut res = Relevance::default();
319 ty: &Type, 329 res.exact_type_match = ty == &expected_type;
320 name: &str, 330 res.exact_name_match = name == &expected_name;
321) -> Option<CompletionScore> {
322 // Compute score
323 // For the same type
324 if active_type != ty {
325 return None;
326 }
327
328 let mut res = CompletionScore::TypeMatch;
329
330 // If same type + same name then go top position
331 if active_name == name {
332 res = CompletionScore::TypeAndNameMatch
333 }
334
335 Some(res) 331 Some(res)
336} 332}
337fn refed_type_matches(
338 active_type: &Type,
339 active_name: &str,
340 ty: &Type,
341 name: &str,
342) -> Option<(Mutability, CompletionScore)> {
343 let derefed_active = active_type.remove_ref()?;
344 let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?;
345 Some((
346 if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared },
347 score,
348 ))
349}
350
351fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> {
352 let (active_name, active_type) = ctx.active_name_and_type()?;
353 compute_score_from_active(&active_type, &active_name, ty, name)
354}
355 333
356#[cfg(test)] 334#[cfg(test)]
357mod tests { 335mod tests {
358 use std::cmp::Reverse; 336 use std::cmp::Reverse;
359 337
360 use expect_test::{expect, Expect}; 338 use expect_test::{expect, Expect};
361 use test_utils::mark;
362 339
363 use crate::{ 340 use crate::{
364 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, 341 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
365 CompletionKind, CompletionScore, 342 CompletionKind, Relevance,
366 }; 343 };
367 344
368 fn check(ra_fixture: &str, expect: Expect) { 345 fn check(ra_fixture: &str, expect: Expect) {
@@ -370,24 +347,25 @@ mod tests {
370 expect.assert_debug_eq(&actual); 347 expect.assert_debug_eq(&actual);
371 } 348 }
372 349
373 fn check_scores(ra_fixture: &str, expect: Expect) { 350 fn check_relevance(ra_fixture: &str, expect: Expect) {
374 fn display_score(score: Option<CompletionScore>) -> &'static str { 351 fn display_relevance(relevance: Relevance) -> &'static str {
375 match score { 352 match relevance {
376 Some(CompletionScore::TypeMatch) => "[type]", 353 Relevance { exact_type_match: true, exact_name_match: true } => "[type+name]",
377 Some(CompletionScore::TypeAndNameMatch) => "[type+name]", 354 Relevance { exact_type_match: true, exact_name_match: false } => "[type]",
378 None => "[]".into(), 355 Relevance { exact_type_match: false, exact_name_match: true } => "[name]",
356 Relevance { exact_type_match: false, exact_name_match: false } => "[]",
379 } 357 }
380 } 358 }
381 359
382 let mut completions = get_all_items(TEST_CONFIG, ra_fixture); 360 let mut completions = get_all_items(TEST_CONFIG, ra_fixture);
383 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); 361 completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string()));
384 let actual = completions 362 let actual = completions
385 .into_iter() 363 .into_iter()
386 .filter(|it| it.completion_kind == CompletionKind::Reference) 364 .filter(|it| it.completion_kind == CompletionKind::Reference)
387 .map(|it| { 365 .map(|it| {
388 let tag = it.kind().unwrap().tag(); 366 let tag = it.kind().unwrap().tag();
389 let score = display_score(it.score()); 367 let relevance = display_relevance(it.relevance());
390 format!("{} {} {}\n", tag, it.label(), score) 368 format!("{} {} {}\n", tag, it.label(), relevance)
391 }) 369 })
392 .collect::<String>(); 370 .collect::<String>();
393 expect.assert_eq(&actual); 371 expect.assert_eq(&actual);
@@ -734,7 +712,7 @@ fn foo(s: S) { s.$0 }
734 712
735 #[test] 713 #[test]
736 fn no_call_parens_if_fn_ptr_needed() { 714 fn no_call_parens_if_fn_ptr_needed() {
737 mark::check!(no_call_parens_if_fn_ptr_needed); 715 cov_mark::check!(no_call_parens_if_fn_ptr_needed);
738 check_edit( 716 check_edit(
739 "foo", 717 "foo",
740 r#" 718 r#"
@@ -758,7 +736,7 @@ fn main() -> ManualVtable {
758 736
759 #[test] 737 #[test]
760 fn no_parens_in_use_item() { 738 fn no_parens_in_use_item() {
761 mark::check!(no_parens_in_use_item); 739 cov_mark::check!(no_parens_in_use_item);
762 check_edit( 740 check_edit(
763 "foo", 741 "foo",
764 r#" 742 r#"
@@ -802,7 +780,7 @@ fn f(foo: &Foo) { foo.foo(); }
802 780
803 #[test] 781 #[test]
804 fn inserts_angle_brackets_for_generics() { 782 fn inserts_angle_brackets_for_generics() {
805 mark::check!(inserts_angle_brackets_for_generics); 783 cov_mark::check!(inserts_angle_brackets_for_generics);
806 check_edit( 784 check_edit(
807 "Vec", 785 "Vec",
808 r#" 786 r#"
@@ -850,9 +828,9 @@ fn foo(xs: Vec<i128>)
850 } 828 }
851 829
852 #[test] 830 #[test]
853 fn active_param_score() { 831 fn active_param_relevance() {
854 mark::check!(active_param_type_match); 832 cov_mark::check!(active_param_type_match);
855 check_scores( 833 check_relevance(
856 r#" 834 r#"
857struct S { foo: i64, bar: u32, baz: u32 } 835struct S { foo: i64, bar: u32, baz: u32 }
858fn test(bar: u32) { } 836fn test(bar: u32) { }
@@ -867,9 +845,9 @@ fn foo(s: S) { test(s.$0) }
867 } 845 }
868 846
869 #[test] 847 #[test]
870 fn record_field_scores() { 848 fn record_field_relevances() {
871 mark::check!(record_field_type_match); 849 cov_mark::check!(record_field_type_match);
872 check_scores( 850 check_relevance(
873 r#" 851 r#"
874struct A { foo: i64, bar: u32, baz: u32 } 852struct A { foo: i64, bar: u32, baz: u32 }
875struct B { x: (), y: f32, bar: u32 } 853struct B { x: (), y: f32, bar: u32 }
@@ -884,8 +862,8 @@ fn foo(a: A) { B { bar: a.$0 }; }
884 } 862 }
885 863
886 #[test] 864 #[test]
887 fn record_field_and_call_scores() { 865 fn record_field_and_call_relevances() {
888 check_scores( 866 check_relevance(
889 r#" 867 r#"
890struct A { foo: i64, bar: u32, baz: u32 } 868struct A { foo: i64, bar: u32, baz: u32 }
891struct B { x: (), y: f32, bar: u32 } 869struct B { x: (), y: f32, bar: u32 }
@@ -898,7 +876,7 @@ fn foo(a: A) { B { bar: f(a.$0) }; }
898 fd baz [] 876 fd baz []
899 "#]], 877 "#]],
900 ); 878 );
901 check_scores( 879 check_relevance(
902 r#" 880 r#"
903struct A { foo: i64, bar: u32, baz: u32 } 881struct A { foo: i64, bar: u32, baz: u32 }
904struct B { x: (), y: f32, bar: u32 } 882struct B { x: (), y: f32, bar: u32 }
@@ -915,7 +893,7 @@ fn foo(a: A) { f(B { bar: a.$0 }); }
915 893
916 #[test] 894 #[test]
917 fn prioritize_exact_ref_match() { 895 fn prioritize_exact_ref_match() {
918 check_scores( 896 check_relevance(
919 r#" 897 r#"
920struct WorldSnapshot { _f: () }; 898struct WorldSnapshot { _f: () };
921fn go(world: &WorldSnapshot) { go(w$0) } 899fn go(world: &WorldSnapshot) { go(w$0) }
@@ -930,7 +908,7 @@ fn go(world: &WorldSnapshot) { go(w$0) }
930 908
931 #[test] 909 #[test]
932 fn too_many_arguments() { 910 fn too_many_arguments() {
933 check_scores( 911 check_relevance(
934 r#" 912 r#"
935struct Foo; 913struct Foo;
936fn f(foo: &Foo) { f(foo, w$0) } 914fn f(foo: &Foo) { f(foo, w$0) }
@@ -942,4 +920,70 @@ fn f(foo: &Foo) { f(foo, w$0) }
942 "#]], 920 "#]],
943 ); 921 );
944 } 922 }
923
924 #[test]
925 fn suggest_ref_mut() {
926 cov_mark::check!(suggest_ref);
927 check(
928 r#"
929struct S;
930fn foo(s: &mut S) {}
931fn main() {
932 let mut s = S;
933 foo($0);
934}
935 "#,
936 expect![[r#"
937 [
938 CompletionItem {
939 label: "S",
940 source_range: 70..70,
941 delete: 70..70,
942 insert: "S",
943 kind: SymbolKind(
944 Struct,
945 ),
946 },
947 CompletionItem {
948 label: "foo(…)",
949 source_range: 70..70,
950 delete: 70..70,
951 insert: "foo(${1:&mut s})$0",
952 kind: SymbolKind(
953 Function,
954 ),
955 lookup: "foo",
956 detail: "-> ()",
957 trigger_call_info: true,
958 },
959 CompletionItem {
960 label: "main()",
961 source_range: 70..70,
962 delete: 70..70,
963 insert: "main()$0",
964 kind: SymbolKind(
965 Function,
966 ),
967 lookup: "main",
968 detail: "-> ()",
969 },
970 CompletionItem {
971 label: "s",
972 source_range: 70..70,
973 delete: 70..70,
974 insert: "s",
975 kind: SymbolKind(
976 Local,
977 ),
978 detail: "S",
979 relevance: Relevance {
980 exact_name_match: true,
981 exact_type_match: false,
982 },
983 ref_match: "&mut ",
984 },
985 ]
986 "#]],
987 )
988 }
945} 989}
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs
index d053a988b..95a7596c1 100644
--- a/crates/ide_completion/src/render/builder_ext.rs
+++ b/crates/ide_completion/src/render/builder_ext.rs
@@ -1,7 +1,6 @@
1//! Extensions for `Builder` structure required for item rendering. 1//! Extensions for `Builder` structure required for item rendering.
2 2
3use itertools::Itertools; 3use itertools::Itertools;
4use test_utils::mark;
5 4
6use crate::{item::Builder, CompletionContext}; 5use crate::{item::Builder, CompletionContext};
7 6
@@ -30,7 +29,7 @@ impl Builder {
30 return false; 29 return false;
31 } 30 }
32 if ctx.use_item_syntax.is_some() { 31 if ctx.use_item_syntax.is_some() {
33 mark::hit!(no_parens_in_use_item); 32 cov_mark::hit!(no_parens_in_use_item);
34 return false; 33 return false;
35 } 34 }
36 if ctx.is_pattern_call { 35 if ctx.is_pattern_call {
@@ -43,7 +42,7 @@ impl Builder {
43 // Don't add parentheses if the expected type is some function reference. 42 // Don't add parentheses if the expected type is some function reference.
44 if let Some(ty) = &ctx.expected_type { 43 if let Some(ty) = &ctx.expected_type {
45 if ty.is_fn() { 44 if ty.is_fn() {
46 mark::hit!(no_call_parens_if_fn_ptr_needed); 45 cov_mark::hit!(no_call_parens_if_fn_ptr_needed);
47 return false; 46 return false;
48 } 47 }
49 } 48 }
@@ -67,7 +66,7 @@ impl Builder {
67 None => return self, 66 None => return self,
68 }; 67 };
69 // If not an import, add parenthesis automatically. 68 // If not an import, add parenthesis automatically.
70 mark::hit!(inserts_parens_for_function_calls); 69 cov_mark::hit!(inserts_parens_for_function_calls);
71 70
72 let (snippet, label) = if params.is_empty() { 71 let (snippet, label) = if params.is_empty() {
73 (format!("{}()$0", name), format!("{}()", name)) 72 (format!("{}()$0", name), format!("{}()", name))
@@ -82,7 +81,7 @@ impl Builder {
82 format!("{}({})$0", name, function_params_snippet) 81 format!("{}({})$0", name, function_params_snippet)
83 } 82 }
84 _ => { 83 _ => {
85 mark::hit!(suppress_arg_snippets); 84 cov_mark::hit!(suppress_arg_snippets);
86 format!("{}($0)", name) 85 format!("{}($0)", name)
87 } 86 }
88 }; 87 };
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
index 9214193b4..ed055c1fb 100644
--- a/crates/ide_completion/src/render/enum_variant.rs
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -3,7 +3,6 @@
3use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; 3use hir::{HasAttrs, HirDisplay, ModPath, StructKind};
4use ide_db::SymbolKind; 4use ide_db::SymbolKind;
5use itertools::Itertools; 5use itertools::Itertools;
6use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 item::{CompletionItem, CompletionKind, ImportEdit}, 8 item::{CompletionItem, CompletionKind, ImportEdit},
@@ -68,7 +67,7 @@ impl<'a> EnumRender<'a> {
68 .detail(self.detail()); 67 .detail(self.detail());
69 68
70 if self.variant_kind == StructKind::Tuple { 69 if self.variant_kind == StructKind::Tuple {
71 mark::hit!(inserts_parens_for_tuple_enums); 70 cov_mark::hit!(inserts_parens_for_tuple_enums);
72 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len()); 71 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len());
73 builder = 72 builder =
74 builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params); 73 builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
@@ -103,13 +102,11 @@ impl<'a> EnumRender<'a> {
103 102
104#[cfg(test)] 103#[cfg(test)]
105mod tests { 104mod tests {
106 use test_utils::mark;
107
108 use crate::test_utils::check_edit; 105 use crate::test_utils::check_edit;
109 106
110 #[test] 107 #[test]
111 fn inserts_parens_for_tuple_enums() { 108 fn inserts_parens_for_tuple_enums() {
112 mark::check!(inserts_parens_for_tuple_enums); 109 cov_mark::check!(inserts_parens_for_tuple_enums);
113 check_edit( 110 check_edit(
114 "Some", 111 "Some",
115 r#" 112 r#"
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index e46e21d24..5931945a8 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -3,7 +3,6 @@
3use hir::{HasSource, HirDisplay, Type}; 3use hir::{HasSource, HirDisplay, Type};
4use ide_db::SymbolKind; 4use ide_db::SymbolKind;
5use syntax::ast::Fn; 5use syntax::ast::Fn;
6use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit},
@@ -82,7 +81,7 @@ impl<'a> FunctionRender<'a> {
82 self.func.method_params(self.ctx.db()).unwrap_or_default() 81 self.func.method_params(self.ctx.db()).unwrap_or_default()
83 } else { 82 } else {
84 if let Some(s) = ast_params.self_param() { 83 if let Some(s) = ast_params.self_param() {
85 mark::hit!(parens_for_method_call_as_assoc_fn); 84 cov_mark::hit!(parens_for_method_call_as_assoc_fn);
86 params_pats.push(Some(s.to_string())); 85 params_pats.push(Some(s.to_string()));
87 } 86 }
88 self.func.assoc_fn_params(self.ctx.db()) 87 self.func.assoc_fn_params(self.ctx.db())
@@ -114,8 +113,6 @@ impl<'a> FunctionRender<'a> {
114 113
115#[cfg(test)] 114#[cfg(test)]
116mod tests { 115mod tests {
117 use test_utils::mark;
118
119 use crate::{ 116 use crate::{
120 test_utils::{check_edit, check_edit_with_config, TEST_CONFIG}, 117 test_utils::{check_edit, check_edit_with_config, TEST_CONFIG},
121 CompletionConfig, 118 CompletionConfig,
@@ -123,7 +120,7 @@ mod tests {
123 120
124 #[test] 121 #[test]
125 fn inserts_parens_for_function_calls() { 122 fn inserts_parens_for_function_calls() {
126 mark::check!(inserts_parens_for_function_calls); 123 cov_mark::check!(inserts_parens_for_function_calls);
127 check_edit( 124 check_edit(
128 "no_args", 125 "no_args",
129 r#" 126 r#"
@@ -191,7 +188,7 @@ fn bar(s: &S) {
191 188
192 #[test] 189 #[test]
193 fn parens_for_method_call_as_assoc_fn() { 190 fn parens_for_method_call_as_assoc_fn() {
194 mark::check!(parens_for_method_call_as_assoc_fn); 191 cov_mark::check!(parens_for_method_call_as_assoc_fn);
195 check_edit( 192 check_edit(
196 "foo", 193 "foo",
197 r#" 194 r#"
@@ -213,7 +210,7 @@ fn main() { S::foo(${1:&self})$0 }
213 210
214 #[test] 211 #[test]
215 fn suppress_arg_snippets() { 212 fn suppress_arg_snippets() {
216 mark::check!(suppress_arg_snippets); 213 cov_mark::check!(suppress_arg_snippets);
217 check_edit_with_config( 214 check_edit_with_config(
218 CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG }, 215 CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG },
219 "with_args", 216 "with_args",
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index a4535786f..a6cf3e479 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -3,7 +3,6 @@
3use hir::{Documentation, HasSource}; 3use hir::{Documentation, HasSource};
4use ide_db::SymbolKind; 4use ide_db::SymbolKind;
5use syntax::display::macro_label; 5use syntax::display::macro_label;
6use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 item::{CompletionItem, CompletionKind, ImportEdit}, 8 item::{CompletionItem, CompletionKind, ImportEdit},
@@ -57,7 +56,7 @@ impl<'a> MacroRender<'a> {
57 } 56 }
58 None if needs_bang => builder.insert_text(self.banged_name()), 57 None if needs_bang => builder.insert_text(self.banged_name()),
59 _ => { 58 _ => {
60 mark::hit!(dont_insert_macro_call_parens_unncessary); 59 cov_mark::hit!(dont_insert_macro_call_parens_unncessary);
61 builder.insert_text(&self.name) 60 builder.insert_text(&self.name)
62 } 61 }
63 }; 62 };
@@ -125,13 +124,11 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
125 124
126#[cfg(test)] 125#[cfg(test)]
127mod tests { 126mod tests {
128 use test_utils::mark;
129
130 use crate::test_utils::check_edit; 127 use crate::test_utils::check_edit;
131 128
132 #[test] 129 #[test]
133 fn dont_insert_macro_call_parens_unncessary() { 130 fn dont_insert_macro_call_parens_unncessary() {
134 mark::check!(dont_insert_macro_call_parens_unncessary); 131 cov_mark::check!(dont_insert_macro_call_parens_unncessary);
135 check_edit( 132 check_edit(
136 "frobnicate!", 133 "frobnicate!",
137 r#" 134 r#"
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
index baff83305..9da844031 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/test_utils.rs
@@ -25,6 +25,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
25 insert_use: InsertUseConfig { 25 insert_use: InsertUseConfig {
26 merge: Some(MergeBehavior::Full), 26 merge: Some(MergeBehavior::Full),
27 prefix_kind: PrefixKind::Plain, 27 prefix_kind: PrefixKind::Plain,
28 group: true,
28 }, 29 },
29}; 30};
30 31
@@ -119,7 +120,7 @@ pub(crate) fn check_edit_with_config(
119 120
120 let mut combined_edit = completion.text_edit().to_owned(); 121 let mut combined_edit = completion.text_edit().to_owned();
121 if let Some(import_text_edit) = 122 if let Some(import_text_edit) =
122 completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use.merge)) 123 completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use))
123 { 124 {
124 combined_edit.union(import_text_edit).expect( 125 combined_edit.union(import_text_edit).expect(
125 "Failed to apply completion resolve changes: change ranges overlap, but should not", 126 "Failed to apply completion resolve changes: change ranges overlap, but should not",
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml
index d4612a75e..1f7a90d20 100644
--- a/crates/ide_db/Cargo.toml
+++ b/crates/ide_db/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13log = "0.4.8" 14log = "0.4.8"
14rayon = "1.5.0" 15rayon = "1.5.0"
15fst = { version = "0.4", default-features = false } 16fst = { version = "0.4", default-features = false }
@@ -23,10 +24,10 @@ syntax = { path = "../syntax", version = "0.0.0" }
23text_edit = { path = "../text_edit", version = "0.0.0" } 24text_edit = { path = "../text_edit", version = "0.0.0" }
24base_db = { path = "../base_db", version = "0.0.0" } 25base_db = { path = "../base_db", version = "0.0.0" }
25profile = { path = "../profile", version = "0.0.0" } 26profile = { path = "../profile", version = "0.0.0" }
26test_utils = { path = "../test_utils", version = "0.0.0" }
27# ide should depend only on the top-level `hir` package. if you need 27# ide should depend only on the top-level `hir` package. if you need
28# something from some `hir_xxx` subpackage, reexport the API via `hir`. 28# something from some `hir_xxx` subpackage, reexport the API via `hir`.
29hir = { path = "../hir", version = "0.0.0" } 29hir = { path = "../hir", version = "0.0.0" }
30 30
31[dev-dependencies] 31[dev-dependencies]
32test_utils = { path = "../test_utils" }
32expect-test = "1.1" 33expect-test = "1.1"
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs
index 615fa7b0e..d8878aa91 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -7,7 +7,6 @@ use syntax::{
7 ast::{self, ArgListOwner}, 7 ast::{self, ArgListOwner},
8 match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, 8 match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
9}; 9};
10use test_utils::mark;
11 10
12use crate::RootDatabase; 11use crate::RootDatabase;
13 12
@@ -122,7 +121,7 @@ fn call_info_impl(
122 121
123 let arg_list_range = arg_list.syntax().text_range(); 122 let arg_list_range = arg_list.syntax().text_range();
124 if !arg_list_range.contains_inclusive(token.text_range().start()) { 123 if !arg_list_range.contains_inclusive(token.text_range().start()) {
125 mark::hit!(call_info_bad_offset); 124 cov_mark::hit!(call_info_bad_offset);
126 return None; 125 return None;
127 } 126 }
128 let param = std::cmp::min( 127 let param = std::cmp::min(
@@ -162,7 +161,7 @@ impl ActiveParameter {
162 let idx = active_parameter?; 161 let idx = active_parameter?;
163 let mut params = signature.params(sema.db); 162 let mut params = signature.params(sema.db);
164 if !(idx < params.len()) { 163 if !(idx < params.len()) {
165 mark::hit!(too_many_arguments); 164 cov_mark::hit!(too_many_arguments);
166 return None; 165 return None;
167 } 166 }
168 let (pat, ty) = params.swap_remove(idx); 167 let (pat, ty) = params.swap_remove(idx);
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
index c714cf280..9f84c253c 100644
--- a/crates/ide_db/src/call_info/tests.rs
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -1,7 +1,7 @@
1use crate::RootDatabase; 1use crate::RootDatabase;
2use base_db::{fixture::ChangeFixture, FilePosition}; 2use base_db::{fixture::ChangeFixture, FilePosition};
3use expect_test::{expect, Expect}; 3use expect_test::{expect, Expect};
4use test_utils::{mark, RangeOrOffset}; 4use test_utils::RangeOrOffset;
5 5
6/// Creates analysis from a multi-file fixture, returns positions marked with $0. 6/// Creates analysis from a multi-file fixture, returns positions marked with $0.
7pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { 7pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
@@ -347,7 +347,7 @@ pub fn foo(mut r: WriteHandler<()>) {
347 347
348#[test] 348#[test]
349fn call_info_bad_offset() { 349fn call_info_bad_offset() {
350 mark::check!(call_info_bad_offset); 350 cov_mark::check!(call_info_bad_offset);
351 check( 351 check(
352 r#" 352 r#"
353fn foo(x: u32, y: u32) -> u32 {x + y} 353fn foo(x: u32, y: u32) -> u32 {x + y}
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 3ff77400b..3c95d3cff 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -2,11 +2,19 @@
2pub mod insert_use; 2pub mod insert_use;
3pub mod import_assets; 3pub mod import_assets;
4 4
5use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; 5use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait};
6use syntax::ast::{self, make}; 6use syntax::ast::{self, make};
7 7
8use crate::RootDatabase; 8use crate::RootDatabase;
9 9
10pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
11 match item {
12 ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).name(db),
13 ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).name(db),
14 ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).name(db),
15 }
16}
17
10/// Converts the mod path struct into its ast representation. 18/// Converts the mod path struct into its ast representation.
11pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { 19pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
12 let _p = profile::span("mod_path_to_ast"); 20 let _p = profile::span("mod_path_to_ast");
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 517abbb4b..e03ccd351 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -1,19 +1,29 @@
1//! Look up accessible paths for items. 1//! Look up accessible paths for items.
2use either::Either; 2use hir::{
3use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics}; 3 AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module,
4 ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, Type,
5};
6use itertools::Itertools;
4use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
5use syntax::{ast, AstNode}; 8use syntax::{ast, utils::path_to_string_stripping_turbo_fish, AstNode, SyntaxNode};
6 9
7use crate::{ 10use crate::{
8 imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, 11 items_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
9 RootDatabase, 12 RootDatabase,
10}; 13};
11 14
15use super::item_name;
16
17/// A candidate for import, derived during various IDE activities:
18/// * completion with imports on the fly proposals
19/// * completion edit resolve requests
20/// * assists
21/// * etc.
12#[derive(Debug)] 22#[derive(Debug)]
13pub enum ImportCandidate { 23pub enum ImportCandidate {
14 // A path, qualified (`std::collections::HashMap`) or not (`HashMap`). 24 /// A path, qualified (`std::collections::HashMap`) or not (`HashMap`).
15 Path(PathImportCandidate), 25 Path(PathImportCandidate),
16 /// A trait associated function (with no self parameter) or associated constant. 26 /// A trait associated function (with no self parameter) or an associated constant.
17 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type 27 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type
18 /// and `name` is the `test_function` 28 /// and `name` is the `test_function`
19 TraitAssocItem(TraitImportCandidate), 29 TraitAssocItem(TraitImportCandidate),
@@ -23,21 +33,40 @@ pub enum ImportCandidate {
23 TraitMethod(TraitImportCandidate), 33 TraitMethod(TraitImportCandidate),
24} 34}
25 35
36/// A trait import needed for a given associated item access.
37/// For `some::path::SomeStruct::ASSOC_`, contains the
38/// type of `some::path::SomeStruct` and `ASSOC_` as the item name.
26#[derive(Debug)] 39#[derive(Debug)]
27pub struct TraitImportCandidate { 40pub struct TraitImportCandidate {
28 pub receiver_ty: hir::Type, 41 /// A type of the item that has the associated item accessed at.
29 pub name: NameToImport, 42 pub receiver_ty: Type,
43 /// The associated item name that the trait to import should contain.
44 pub assoc_item_name: NameToImport,
30} 45}
31 46
47/// Path import for a given name, qualified or not.
32#[derive(Debug)] 48#[derive(Debug)]
33pub struct PathImportCandidate { 49pub struct PathImportCandidate {
34 pub qualifier: Option<ast::Path>, 50 /// Optional qualifier before name.
51 pub qualifier: Option<FirstSegmentUnresolved>,
52 /// The name the item (struct, trait, enum, etc.) should have.
35 pub name: NameToImport, 53 pub name: NameToImport,
36} 54}
37 55
56/// A qualifier that has a first segment and it's unresolved.
57#[derive(Debug)]
58pub struct FirstSegmentUnresolved {
59 fist_segment: ast::NameRef,
60 full_qualifier: ast::Path,
61}
62
63/// A name that will be used during item lookups.
38#[derive(Debug)] 64#[derive(Debug)]
39pub enum NameToImport { 65pub enum NameToImport {
66 /// Requires items with names that exactly match the given string, case-sensitive.
40 Exact(String), 67 Exact(String),
68 /// Requires items with names that case-insensitively contain all letters from the string,
69 /// in the same order, but not necessary adjacent.
41 Fuzzy(String), 70 Fuzzy(String),
42} 71}
43 72
@@ -50,10 +79,12 @@ impl NameToImport {
50 } 79 }
51} 80}
52 81
82/// A struct to find imports in the project, given a certain name (or its part) and the context.
53#[derive(Debug)] 83#[derive(Debug)]
54pub struct ImportAssets { 84pub struct ImportAssets {
55 import_candidate: ImportCandidate, 85 import_candidate: ImportCandidate,
56 module_with_candidate: hir::Module, 86 candidate_node: SyntaxNode,
87 module_with_candidate: Module,
57} 88}
58 89
59impl ImportAssets { 90impl ImportAssets {
@@ -61,9 +92,11 @@ impl ImportAssets {
61 method_call: &ast::MethodCallExpr, 92 method_call: &ast::MethodCallExpr,
62 sema: &Semantics<RootDatabase>, 93 sema: &Semantics<RootDatabase>,
63 ) -> Option<Self> { 94 ) -> Option<Self> {
95 let candidate_node = method_call.syntax().clone();
64 Some(Self { 96 Some(Self {
65 import_candidate: ImportCandidate::for_method_call(sema, method_call)?, 97 import_candidate: ImportCandidate::for_method_call(sema, method_call)?,
66 module_with_candidate: sema.scope(method_call.syntax()).module()?, 98 module_with_candidate: sema.scope(&candidate_node).module()?,
99 candidate_node,
67 }) 100 })
68 } 101 }
69 102
@@ -71,94 +104,94 @@ impl ImportAssets {
71 fully_qualified_path: &ast::Path, 104 fully_qualified_path: &ast::Path,
72 sema: &Semantics<RootDatabase>, 105 sema: &Semantics<RootDatabase>,
73 ) -> Option<Self> { 106 ) -> Option<Self> {
74 let syntax_under_caret = fully_qualified_path.syntax(); 107 let candidate_node = fully_qualified_path.syntax().clone();
75 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { 108 if candidate_node.ancestors().find_map(ast::Use::cast).is_some() {
76 return None; 109 return None;
77 } 110 }
78 Some(Self { 111 Some(Self {
79 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?, 112 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?,
80 module_with_candidate: sema.scope(syntax_under_caret).module()?, 113 module_with_candidate: sema.scope(&candidate_node).module()?,
114 candidate_node,
81 }) 115 })
82 } 116 }
83 117
84 pub fn for_fuzzy_path( 118 pub fn for_fuzzy_path(
85 module_with_path: Module, 119 module_with_candidate: Module,
86 qualifier: Option<ast::Path>, 120 qualifier: Option<ast::Path>,
87 fuzzy_name: String, 121 fuzzy_name: String,
88 sema: &Semantics<RootDatabase>, 122 sema: &Semantics<RootDatabase>,
123 candidate_node: SyntaxNode,
89 ) -> Option<Self> { 124 ) -> Option<Self> {
90 Some(match qualifier { 125 Some(Self {
91 Some(qualifier) => { 126 import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?,
92 let qualifier_resolution = sema.resolve_path(&qualifier)?; 127 module_with_candidate,
93 match qualifier_resolution { 128 candidate_node,
94 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => Self {
95 import_candidate: ImportCandidate::TraitAssocItem(TraitImportCandidate {
96 receiver_ty: assoc_item_path.ty(sema.db),
97 name: NameToImport::Fuzzy(fuzzy_name),
98 }),
99 module_with_candidate: module_with_path,
100 },
101 _ => Self {
102 import_candidate: ImportCandidate::Path(PathImportCandidate {
103 qualifier: Some(qualifier),
104 name: NameToImport::Fuzzy(fuzzy_name),
105 }),
106 module_with_candidate: module_with_path,
107 },
108 }
109 }
110 None => Self {
111 import_candidate: ImportCandidate::Path(PathImportCandidate {
112 qualifier: None,
113 name: NameToImport::Fuzzy(fuzzy_name),
114 }),
115 module_with_candidate: module_with_path,
116 },
117 }) 129 })
118 } 130 }
119 131
120 pub fn for_fuzzy_method_call( 132 pub fn for_fuzzy_method_call(
121 module_with_method_call: Module, 133 module_with_method_call: Module,
122 receiver_ty: hir::Type, 134 receiver_ty: Type,
123 fuzzy_method_name: String, 135 fuzzy_method_name: String,
136 candidate_node: SyntaxNode,
124 ) -> Option<Self> { 137 ) -> Option<Self> {
125 Some(Self { 138 Some(Self {
126 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { 139 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
127 receiver_ty, 140 receiver_ty,
128 name: NameToImport::Fuzzy(fuzzy_method_name), 141 assoc_item_name: NameToImport::Fuzzy(fuzzy_method_name),
129 }), 142 }),
130 module_with_candidate: module_with_method_call, 143 module_with_candidate: module_with_method_call,
144 candidate_node,
131 }) 145 })
132 } 146 }
133} 147}
134 148
149/// An import (not necessary the only one) that corresponds a certain given [`PathImportCandidate`].
150/// (the structure is not entirely correct, since there can be situations requiring two imports, see FIXME below for the details)
151#[derive(Debug, Clone, PartialEq, Eq, Hash)]
152pub struct LocatedImport {
153 /// The path to use in the `use` statement for a given candidate to be imported.
154 pub import_path: ModPath,
155 /// An item that will be imported with the import path given.
156 pub item_to_import: ItemInNs,
157 /// The path import candidate, resolved.
158 ///
159 /// Not necessary matches the import:
160 /// For any associated constant from the trait, we try to access as `some::path::SomeStruct::ASSOC_`
161 /// the original item is the associated constant, but the import has to be a trait that
162 /// defines this constant.
163 pub original_item: ItemInNs,
164 /// A path of the original item.
165 pub original_path: Option<ModPath>,
166}
167
168impl LocatedImport {
169 pub fn new(
170 import_path: ModPath,
171 item_to_import: ItemInNs,
172 original_item: ItemInNs,
173 original_path: Option<ModPath>,
174 ) -> Self {
175 Self { import_path, item_to_import, original_item, original_path }
176 }
177}
178
135impl ImportAssets { 179impl ImportAssets {
136 pub fn import_candidate(&self) -> &ImportCandidate { 180 pub fn import_candidate(&self) -> &ImportCandidate {
137 &self.import_candidate 181 &self.import_candidate
138 } 182 }
139 183
140 fn name_to_import(&self) -> &NameToImport {
141 match &self.import_candidate {
142 ImportCandidate::Path(candidate) => &candidate.name,
143 ImportCandidate::TraitAssocItem(candidate)
144 | ImportCandidate::TraitMethod(candidate) => &candidate.name,
145 }
146 }
147
148 pub fn search_for_imports( 184 pub fn search_for_imports(
149 &self, 185 &self,
150 sema: &Semantics<RootDatabase>, 186 sema: &Semantics<RootDatabase>,
151 prefix_kind: PrefixKind, 187 prefix_kind: PrefixKind,
152 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 188 ) -> Vec<LocatedImport> {
153 let _p = profile::span("import_assets::search_for_imports"); 189 let _p = profile::span("import_assets::search_for_imports");
154 self.search_for(sema, Some(prefix_kind)) 190 self.search_for(sema, Some(prefix_kind))
155 } 191 }
156 192
157 /// This may return non-absolute paths if a part of the returned path is already imported into scope. 193 /// This may return non-absolute paths if a part of the returned path is already imported into scope.
158 pub fn search_for_relative_paths( 194 pub fn search_for_relative_paths(&self, sema: &Semantics<RootDatabase>) -> Vec<LocatedImport> {
159 &self,
160 sema: &Semantics<RootDatabase>,
161 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
162 let _p = profile::span("import_assets::search_for_relative_paths"); 195 let _p = profile::span("import_assets::search_for_relative_paths");
163 self.search_for(sema, None) 196 self.search_for(sema, None)
164 } 197 }
@@ -166,29 +199,29 @@ impl ImportAssets {
166 fn search_for( 199 fn search_for(
167 &self, 200 &self,
168 sema: &Semantics<RootDatabase>, 201 sema: &Semantics<RootDatabase>,
169 prefixed: Option<hir::PrefixKind>, 202 prefixed: Option<PrefixKind>,
170 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 203 ) -> Vec<LocatedImport> {
171 let current_crate = self.module_with_candidate.krate(); 204 let items_with_candidate_name = match self.name_to_import() {
172 205 NameToImport::Exact(exact_name) => items_locator::with_exact_name(
173 let unfiltered_imports = match self.name_to_import() { 206 sema,
174 NameToImport::Exact(exact_name) => { 207 self.module_with_candidate.krate(),
175 imports_locator::find_exact_imports(sema, current_crate, exact_name.clone()) 208 exact_name.clone(),
176 } 209 ),
177 // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items: 210 // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items:
178 // instead, we need to look up all trait impls for a certain struct and search through them only 211 // instead, we need to look up all trait impls for a certain struct and search through them only
179 // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032 212 // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
180 // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup 213 // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
181 // for the details 214 // for the details
182 NameToImport::Fuzzy(fuzzy_name) => { 215 NameToImport::Fuzzy(fuzzy_name) => {
183 let (assoc_item_search, limit) = match self.import_candidate { 216 let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() {
184 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { 217 (AssocItemSearch::AssocItemsOnly, None)
185 (AssocItemSearch::AssocItemsOnly, None) 218 } else {
186 } 219 (AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT))
187 _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)),
188 }; 220 };
189 imports_locator::find_similar_imports( 221
222 items_locator::with_similar_name(
190 sema, 223 sema,
191 current_crate, 224 self.module_with_candidate.krate(),
192 fuzzy_name.clone(), 225 fuzzy_name.clone(),
193 assoc_item_search, 226 assoc_item_search,
194 limit, 227 limit,
@@ -196,63 +229,224 @@ impl ImportAssets {
196 } 229 }
197 }; 230 };
198 231
199 let db = sema.db; 232 let scope_definitions = self.scope_definitions(sema);
200 let mut res = 233 self.applicable_defs(sema.db, prefixed, items_with_candidate_name)
201 applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports) 234 .into_iter()
202 .filter_map(|candidate| { 235 .filter(|import| import.import_path.len() > 1)
203 let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into); 236 .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
204 237 .sorted_by_key(|import| import.import_path.clone())
205 let item_to_search = match self.import_candidate { 238 .collect()
206 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { 239 }
207 let canidate_trait = match candidate { 240
208 Either::Left(module_def) => { 241 fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> {
209 module_def.as_assoc_item(db)?.containing_trait(db) 242 let mut scope_definitions = FxHashSet::default();
210 } 243 sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| {
211 _ => None, 244 scope_definitions.insert(scope_def);
212 }?; 245 });
213 ModuleDef::from(canidate_trait).into() 246 scope_definitions
214 } 247 }
215 _ => item, 248
216 }; 249 fn name_to_import(&self) -> &NameToImport {
217 250 match &self.import_candidate {
218 if let Some(prefix_kind) = prefixed { 251 ImportCandidate::Path(candidate) => &candidate.name,
219 self.module_with_candidate.find_use_path_prefixed( 252 ImportCandidate::TraitAssocItem(candidate)
220 db, 253 | ImportCandidate::TraitMethod(candidate) => &candidate.assoc_item_name,
221 item_to_search, 254 }
222 prefix_kind, 255 }
223 ) 256
224 } else { 257 fn applicable_defs(
225 self.module_with_candidate.find_use_path(db, item_to_search) 258 &self,
226 } 259 db: &RootDatabase,
227 .map(|path| (path, item)) 260 prefixed: Option<PrefixKind>,
228 }) 261 items_with_candidate_name: FxHashSet<ItemInNs>,
229 .filter(|(use_path, _)| use_path.len() > 1) 262 ) -> FxHashSet<LocatedImport> {
230 .collect::<Vec<_>>(); 263 let _p = profile::span("import_assets::applicable_defs");
231 res.sort_by_cached_key(|(path, _)| path.clone()); 264 let current_crate = self.module_with_candidate.krate();
232 res 265
266 let mod_path = |item| {
267 get_mod_path(db, item_for_path_search(db, item)?, &self.module_with_candidate, prefixed)
268 };
269
270 match &self.import_candidate {
271 ImportCandidate::Path(path_candidate) => {
272 path_applicable_imports(db, path_candidate, mod_path, items_with_candidate_name)
273 }
274 ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items(
275 db,
276 current_crate,
277 trait_candidate,
278 true,
279 mod_path,
280 items_with_candidate_name,
281 ),
282 ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
283 db,
284 current_crate,
285 trait_candidate,
286 false,
287 mod_path,
288 items_with_candidate_name,
289 ),
290 }
233 } 291 }
234} 292}
235 293
236fn applicable_defs<'a>( 294fn path_applicable_imports(
237 import_candidate: &ImportCandidate,
238 current_crate: Crate,
239 db: &RootDatabase, 295 db: &RootDatabase,
240 unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>, 296 path_candidate: &PathImportCandidate,
241) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { 297 mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
242 let receiver_ty = match import_candidate { 298 items_with_candidate_name: FxHashSet<ItemInNs>,
243 ImportCandidate::Path(_) => return unfiltered_imports, 299) -> FxHashSet<LocatedImport> {
244 ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => { 300 let _p = profile::span("import_assets::path_applicable_imports");
245 &candidate.receiver_ty 301
302 let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier {
303 None => {
304 return items_with_candidate_name
305 .into_iter()
306 .filter_map(|item| {
307 Some(LocatedImport::new(mod_path(item)?, item, item, mod_path(item)))
308 })
309 .collect();
246 } 310 }
311 Some(first_segment_unresolved) => (
312 first_segment_unresolved.fist_segment.to_string(),
313 path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier),
314 ),
247 }; 315 };
248 316
317 items_with_candidate_name
318 .into_iter()
319 .filter_map(|item| {
320 import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item)
321 })
322 .collect()
323}
324
325fn import_for_item(
326 db: &RootDatabase,
327 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
328 unresolved_first_segment: &str,
329 unresolved_qualifier: &str,
330 original_item: ItemInNs,
331) -> Option<LocatedImport> {
332 let _p = profile::span("import_assets::import_for_item");
333
334 let original_item_candidate = item_for_path_search(db, original_item)?;
335 let import_path_candidate = mod_path(original_item_candidate)?;
336 let import_path_string = import_path_candidate.to_string();
337
338 let expected_import_end = if item_as_assoc(db, original_item).is_some() {
339 unresolved_qualifier.to_string()
340 } else {
341 format!("{}::{}", unresolved_qualifier, item_name(db, original_item)?)
342 };
343 if !import_path_string.contains(unresolved_first_segment)
344 || !import_path_string.ends_with(&expected_import_end)
345 {
346 return None;
347 }
348
349 let segment_import =
350 find_import_for_segment(db, original_item_candidate, &unresolved_first_segment)?;
351 let trait_item_to_import = item_as_assoc(db, original_item)
352 .and_then(|assoc| assoc.containing_trait(db))
353 .map(|trait_| ItemInNs::from(ModuleDef::from(trait_)));
354 Some(match (segment_import == original_item_candidate, trait_item_to_import) {
355 (true, Some(_)) => {
356 // FIXME we should be able to import both the trait and the segment,
357 // but it's unclear what to do with overlapping edits (merge imports?)
358 // especially in case of lazy completion edit resolutions.
359 return None;
360 }
361 (false, Some(trait_to_import)) => LocatedImport::new(
362 mod_path(trait_to_import)?,
363 trait_to_import,
364 original_item,
365 mod_path(original_item),
366 ),
367 (true, None) => LocatedImport::new(
368 import_path_candidate,
369 original_item_candidate,
370 original_item,
371 mod_path(original_item),
372 ),
373 (false, None) => LocatedImport::new(
374 mod_path(segment_import)?,
375 segment_import,
376 original_item,
377 mod_path(original_item),
378 ),
379 })
380}
381
382fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
383 Some(match item {
384 ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
385 Some(assoc_item) => match assoc_item.container(db) {
386 AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
387 AssocItemContainer::Impl(impl_) => {
388 ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?))
389 }
390 },
391 None => item,
392 },
393 ItemInNs::Macros(_) => item,
394 })
395}
396
397fn find_import_for_segment(
398 db: &RootDatabase,
399 original_item: ItemInNs,
400 unresolved_first_segment: &str,
401) -> Option<ItemInNs> {
402 let segment_is_name = item_name(db, original_item)
403 .map(|name| name.to_string() == unresolved_first_segment)
404 .unwrap_or(false);
405
406 Some(if segment_is_name {
407 original_item
408 } else {
409 let matching_module =
410 module_with_segment_name(db, &unresolved_first_segment, original_item)?;
411 ItemInNs::from(ModuleDef::from(matching_module))
412 })
413}
414
415fn module_with_segment_name(
416 db: &RootDatabase,
417 segment_name: &str,
418 candidate: ItemInNs,
419) -> Option<Module> {
420 let mut current_module = match candidate {
421 ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).module(db),
422 ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).module(db),
423 ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).module(db),
424 };
425 while let Some(module) = current_module {
426 if let Some(module_name) = module.name(db) {
427 if module_name.to_string() == segment_name {
428 return Some(module);
429 }
430 }
431 current_module = module.parent(db);
432 }
433 None
434}
435
436fn trait_applicable_items(
437 db: &RootDatabase,
438 current_crate: Crate,
439 trait_candidate: &TraitImportCandidate,
440 trait_assoc_item: bool,
441 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
442 items_with_candidate_name: FxHashSet<ItemInNs>,
443) -> FxHashSet<LocatedImport> {
444 let _p = profile::span("import_assets::trait_applicable_items");
249 let mut required_assoc_items = FxHashSet::default(); 445 let mut required_assoc_items = FxHashSet::default();
250 446
251 let trait_candidates = unfiltered_imports 447 let trait_candidates = items_with_candidate_name
252 .filter_map(|input| match input { 448 .into_iter()
253 Either::Left(module_def) => module_def.as_assoc_item(db), 449 .filter_map(|input| item_as_assoc(db, input))
254 _ => None,
255 })
256 .filter_map(|assoc| { 450 .filter_map(|assoc| {
257 let assoc_item_trait = assoc.containing_trait(db)?; 451 let assoc_item_trait = assoc.containing_trait(db)?;
258 required_assoc_items.insert(assoc); 452 required_assoc_items.insert(assoc);
@@ -260,11 +454,10 @@ fn applicable_defs<'a>(
260 }) 454 })
261 .collect(); 455 .collect();
262 456
263 let mut applicable_defs = FxHashSet::default(); 457 let mut located_imports = FxHashSet::default();
264 458
265 match import_candidate { 459 if trait_assoc_item {
266 ImportCandidate::Path(_) => unreachable!(), 460 trait_candidate.receiver_ty.iterate_path_candidates(
267 ImportCandidate::TraitAssocItem(_) => receiver_ty.iterate_path_candidates(
268 db, 461 db,
269 current_crate, 462 current_crate,
270 &trait_candidates, 463 &trait_candidates,
@@ -276,12 +469,21 @@ fn applicable_defs<'a>(
276 return None; 469 return None;
277 } 470 }
278 } 471 }
279 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); 472
473 let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
474 let original_item = assoc_to_item(assoc);
475 located_imports.insert(LocatedImport::new(
476 mod_path(item)?,
477 item,
478 original_item,
479 mod_path(original_item),
480 ));
280 } 481 }
281 None::<()> 482 None::<()>
282 }, 483 },
283 ), 484 )
284 ImportCandidate::TraitMethod(_) => receiver_ty.iterate_method_candidates( 485 } else {
486 trait_candidate.receiver_ty.iterate_method_candidates(
285 db, 487 db,
286 current_crate, 488 current_crate,
287 &trait_candidates, 489 &trait_candidates,
@@ -289,21 +491,41 @@ fn applicable_defs<'a>(
289 |_, function| { 491 |_, function| {
290 let assoc = function.as_assoc_item(db)?; 492 let assoc = function.as_assoc_item(db)?;
291 if required_assoc_items.contains(&assoc) { 493 if required_assoc_items.contains(&assoc) {
292 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); 494 let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
495 let original_item = assoc_to_item(assoc);
496 located_imports.insert(LocatedImport::new(
497 mod_path(item)?,
498 item,
499 original_item,
500 mod_path(original_item),
501 ));
293 } 502 }
294 None::<()> 503 None::<()>
295 }, 504 },
296 ), 505 )
297 }; 506 };
298 507
299 Box::new(applicable_defs.into_iter()) 508 located_imports
300} 509}
301 510
302fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef { 511fn assoc_to_item(assoc: AssocItem) -> ItemInNs {
303 match assoc { 512 match assoc {
304 AssocItem::Function(f) => f.into(), 513 AssocItem::Function(f) => ItemInNs::from(ModuleDef::from(f)),
305 AssocItem::Const(c) => c.into(), 514 AssocItem::Const(c) => ItemInNs::from(ModuleDef::from(c)),
306 AssocItem::TypeAlias(t) => t.into(), 515 AssocItem::TypeAlias(t) => ItemInNs::from(ModuleDef::from(t)),
516 }
517}
518
519fn get_mod_path(
520 db: &RootDatabase,
521 item_to_search: ItemInNs,
522 module_with_candidate: &Module,
523 prefixed: Option<PrefixKind>,
524) -> Option<ModPath> {
525 if let Some(prefix_kind) = prefixed {
526 module_with_candidate.find_use_path_prefixed(db, item_to_search, prefix_kind)
527 } else {
528 module_with_candidate.find_use_path(db, item_to_search)
307 } 529 }
308} 530}
309 531
@@ -316,7 +538,7 @@ impl ImportCandidate {
316 Some(_) => None, 538 Some(_) => None,
317 None => Some(Self::TraitMethod(TraitImportCandidate { 539 None => Some(Self::TraitMethod(TraitImportCandidate {
318 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, 540 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?,
319 name: NameToImport::Exact(method_call.name_ref()?.to_string()), 541 assoc_item_name: NameToImport::Exact(method_call.name_ref()?.to_string()),
320 })), 542 })),
321 } 543 }
322 } 544 }
@@ -325,41 +547,63 @@ impl ImportCandidate {
325 if sema.resolve_path(path).is_some() { 547 if sema.resolve_path(path).is_some() {
326 return None; 548 return None;
327 } 549 }
550 path_import_candidate(
551 sema,
552 path.qualifier(),
553 NameToImport::Exact(path.segment()?.name_ref()?.to_string()),
554 )
555 }
328 556
329 let segment = path.segment()?; 557 fn for_fuzzy_path(
330 let candidate = if let Some(qualifier) = path.qualifier() { 558 qualifier: Option<ast::Path>,
331 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; 559 fuzzy_name: String,
332 let qualifier_start_path = 560 sema: &Semantics<RootDatabase>,
333 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; 561 ) -> Option<Self> {
334 if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) { 562 path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name))
335 let qualifier_resolution = if qualifier_start_path == qualifier { 563 }
336 qualifier_start_resolution 564
565 fn is_trait_candidate(&self) -> bool {
566 matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_))
567 }
568}
569
570fn path_import_candidate(
571 sema: &Semantics<RootDatabase>,
572 qualifier: Option<ast::Path>,
573 name: NameToImport,
574) -> Option<ImportCandidate> {
575 Some(match qualifier {
576 Some(qualifier) => match sema.resolve_path(&qualifier) {
577 None => {
578 let qualifier_start =
579 qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
580 let qualifier_start_path =
581 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
582 if sema.resolve_path(&qualifier_start_path).is_none() {
583 ImportCandidate::Path(PathImportCandidate {
584 qualifier: Some(FirstSegmentUnresolved {
585 fist_segment: qualifier_start,
586 full_qualifier: qualifier,
587 }),
588 name,
589 })
337 } else { 590 } else {
338 sema.resolve_path(&qualifier)? 591 return None;
339 };
340 match qualifier_resolution {
341 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => {
342 ImportCandidate::TraitAssocItem(TraitImportCandidate {
343 receiver_ty: assoc_item_path.ty(sema.db),
344 name: NameToImport::Exact(segment.name_ref()?.to_string()),
345 })
346 }
347 _ => return None,
348 } 592 }
349 } else { 593 }
350 ImportCandidate::Path(PathImportCandidate { 594 Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => {
351 qualifier: Some(qualifier), 595 ImportCandidate::TraitAssocItem(TraitImportCandidate {
352 name: NameToImport::Exact(qualifier_start.to_string()), 596 receiver_ty: assoc_item_path.ty(sema.db),
597 assoc_item_name: name,
353 }) 598 })
354 } 599 }
355 } else { 600 Some(_) => return None,
356 ImportCandidate::Path(PathImportCandidate { 601 },
357 qualifier: None, 602 None => ImportCandidate::Path(PathImportCandidate { qualifier: None, name }),
358 name: NameToImport::Exact( 603 })
359 segment.syntax().descendants().find_map(ast::NameRef::cast)?.to_string(), 604}
360 ), 605
361 }) 606fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> {
362 }; 607 item.as_module_def_id()
363 Some(candidate) 608 .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
364 }
365} 609}
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index fd4035198..df66d8ea0 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -13,12 +13,12 @@ use syntax::{
13 }, 13 },
14 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, 14 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
15}; 15};
16use test_utils::mark;
17 16
18#[derive(Clone, Copy, Debug, PartialEq, Eq)] 17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct InsertUseConfig { 18pub struct InsertUseConfig {
20 pub merge: Option<MergeBehavior>, 19 pub merge: Option<MergeBehavior>,
21 pub prefix_kind: hir::PrefixKind, 20 pub prefix_kind: hir::PrefixKind,
21 pub group: bool,
22} 22}
23 23
24#[derive(Debug, Clone)] 24#[derive(Debug, Clone)]
@@ -99,13 +99,13 @@ fn is_inner_comment(token: SyntaxToken) -> bool {
99pub fn insert_use<'a>( 99pub fn insert_use<'a>(
100 scope: &ImportScope, 100 scope: &ImportScope,
101 path: ast::Path, 101 path: ast::Path,
102 merge: Option<MergeBehavior>, 102 cfg: InsertUseConfig,
103) -> SyntaxRewriter<'a> { 103) -> SyntaxRewriter<'a> {
104 let _p = profile::span("insert_use"); 104 let _p = profile::span("insert_use");
105 let mut rewriter = SyntaxRewriter::default(); 105 let mut rewriter = SyntaxRewriter::default();
106 let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false)); 106 let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false));
107 // merge into existing imports if possible 107 // merge into existing imports if possible
108 if let Some(mb) = merge { 108 if let Some(mb) = cfg.merge {
109 for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { 109 for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) {
110 if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { 110 if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) {
111 rewriter.replace(existing_use.syntax(), merged.syntax()); 111 rewriter.replace(existing_use.syntax(), merged.syntax());
@@ -116,7 +116,7 @@ pub fn insert_use<'a>(
116 116
117 // either we weren't allowed to merge or there is no import that fits the merge conditions 117 // either we weren't allowed to merge or there is no import that fits the merge conditions
118 // so look for the place we have to insert to 118 // so look for the place we have to insert to
119 let (insert_position, add_blank) = find_insert_position(scope, path); 119 let (insert_position, add_blank) = find_insert_position(scope, path, cfg.group);
120 120
121 let indent = if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { 121 let indent = if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize {
122 Some(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()) 122 Some(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into())
@@ -137,7 +137,7 @@ pub fn insert_use<'a>(
137 137
138 if add_blank.has_before() { 138 if add_blank.has_before() {
139 if let Some(indent) = indent.clone() { 139 if let Some(indent) = indent.clone() {
140 mark::hit!(insert_use_indent_before); 140 cov_mark::hit!(insert_use_indent_before);
141 buf.push(indent); 141 buf.push(indent);
142 } 142 }
143 } 143 }
@@ -155,11 +155,11 @@ pub fn insert_use<'a>(
155 // only add indentation *after* our stuff if there's another node directly after it 155 // only add indentation *after* our stuff if there's another node directly after it
156 if add_blank.has_after() && matches!(insert_position, InsertPosition::Before(_)) { 156 if add_blank.has_after() && matches!(insert_position, InsertPosition::Before(_)) {
157 if let Some(indent) = indent { 157 if let Some(indent) = indent {
158 mark::hit!(insert_use_indent_after); 158 cov_mark::hit!(insert_use_indent_after);
159 buf.push(indent); 159 buf.push(indent);
160 } 160 }
161 } else if add_blank.has_after() && matches!(insert_position, InsertPosition::After(_)) { 161 } else if add_blank.has_after() && matches!(insert_position, InsertPosition::After(_)) {
162 mark::hit!(insert_use_no_indent_after); 162 cov_mark::hit!(insert_use_no_indent_after);
163 } 163 }
164 164
165 buf 165 buf
@@ -538,6 +538,7 @@ impl AddBlankLine {
538fn find_insert_position( 538fn find_insert_position(
539 scope: &ImportScope, 539 scope: &ImportScope,
540 insert_path: ast::Path, 540 insert_path: ast::Path,
541 group_imports: bool,
541) -> (InsertPosition<SyntaxElement>, AddBlankLine) { 542) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
542 let group = ImportGroup::new(&insert_path); 543 let group = ImportGroup::new(&insert_path);
543 let path_node_iter = scope 544 let path_node_iter = scope
@@ -550,6 +551,14 @@ fn find_insert_position(
550 let has_tl = tree.use_tree_list().is_some(); 551 let has_tl = tree.use_tree_list().is_some();
551 Some((path, has_tl, node)) 552 Some((path, has_tl, node))
552 }); 553 });
554
555 if !group_imports {
556 if let Some((_, _, node)) = path_node_iter.last() {
557 return (InsertPosition::After(node.into()), AddBlankLine::Before);
558 }
559 return (InsertPosition::First, AddBlankLine::AfterTwice);
560 }
561
553 // Iterator that discards anything thats not in the required grouping 562 // Iterator that discards anything thats not in the required grouping
554 // This implementation allows the user to rearrange their import groups as this only takes the first group that fits 563 // This implementation allows the user to rearrange their import groups as this only takes the first group that fits
555 let group_iter = path_node_iter 564 let group_iter = path_node_iter
@@ -565,6 +574,7 @@ fn find_insert_position(
565 use_tree_path_cmp(&insert_path, false, path, has_tl) != Ordering::Greater 574 use_tree_path_cmp(&insert_path, false, path, has_tl) != Ordering::Greater
566 }, 575 },
567 ); 576 );
577
568 match post_insert { 578 match post_insert {
569 // insert our import before that element 579 // insert our import before that element
570 Some((.., node)) => (InsertPosition::Before(node.into()), AddBlankLine::After), 580 Some((.., node)) => (InsertPosition::Before(node.into()), AddBlankLine::After),
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
index 4bbe66f1f..3d151e629 100644
--- a/crates/ide_db/src/helpers/insert_use/tests.rs
+++ b/crates/ide_db/src/helpers/insert_use/tests.rs
@@ -1,8 +1,32 @@
1use super::*; 1use super::*;
2 2
3use hir::PrefixKind;
3use test_utils::assert_eq_text; 4use test_utils::assert_eq_text;
4 5
5#[test] 6#[test]
7fn insert_not_group() {
8 check(
9 "use external_crate2::bar::A",
10 r"
11use std::bar::B;
12use external_crate::bar::A;
13use crate::bar::A;
14use self::bar::A;
15use super::bar::A;",
16 r"
17use std::bar::B;
18use external_crate::bar::A;
19use crate::bar::A;
20use self::bar::A;
21use super::bar::A;
22use external_crate2::bar::A;",
23 None,
24 false,
25 false,
26 );
27}
28
29#[test]
6fn insert_existing() { 30fn insert_existing() {
7 check_full("std::fs", "use std::fs;", "use std::fs;") 31 check_full("std::fs", "use std::fs;", "use std::fs;")
8} 32}
@@ -27,7 +51,7 @@ use std::bar::G;",
27 51
28#[test] 52#[test]
29fn insert_start_indent() { 53fn insert_start_indent() {
30 mark::check!(insert_use_indent_after); 54 cov_mark::check!(insert_use_indent_after);
31 check_none( 55 check_none(
32 "std::bar::AA", 56 "std::bar::AA",
33 r" 57 r"
@@ -96,7 +120,7 @@ use std::bar::ZZ;",
96 120
97#[test] 121#[test]
98fn insert_end_indent() { 122fn insert_end_indent() {
99 mark::check!(insert_use_indent_before); 123 cov_mark::check!(insert_use_indent_before);
100 check_none( 124 check_none(
101 "std::bar::ZZ", 125 "std::bar::ZZ",
102 r" 126 r"
@@ -231,7 +255,7 @@ fn insert_empty_file() {
231 255
232#[test] 256#[test]
233fn insert_empty_module() { 257fn insert_empty_module() {
234 mark::check!(insert_use_no_indent_after); 258 cov_mark::check!(insert_use_no_indent_after);
235 check( 259 check(
236 "foo::bar", 260 "foo::bar",
237 "mod x {}", 261 "mod x {}",
@@ -240,6 +264,7 @@ fn insert_empty_module() {
240}", 264}",
241 None, 265 None,
242 true, 266 true,
267 true,
243 ) 268 )
244} 269}
245 270
@@ -584,6 +609,7 @@ fn check(
584 ra_fixture_after: &str, 609 ra_fixture_after: &str,
585 mb: Option<MergeBehavior>, 610 mb: Option<MergeBehavior>,
586 module: bool, 611 module: bool,
612 group: bool,
587) { 613) {
588 let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(); 614 let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone();
589 if module { 615 if module {
@@ -597,21 +623,25 @@ fn check(
597 .find_map(ast::Path::cast) 623 .find_map(ast::Path::cast)
598 .unwrap(); 624 .unwrap();
599 625
600 let rewriter = insert_use(&file, path, mb); 626 let rewriter = insert_use(
627 &file,
628 path,
629 InsertUseConfig { merge: mb, prefix_kind: PrefixKind::Plain, group },
630 );
601 let result = rewriter.rewrite(file.as_syntax_node()).to_string(); 631 let result = rewriter.rewrite(file.as_syntax_node()).to_string();
602 assert_eq_text!(ra_fixture_after, &result); 632 assert_eq_text!(ra_fixture_after, &result);
603} 633}
604 634
605fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 635fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
606 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Full), false) 636 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Full), false, true)
607} 637}
608 638
609fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 639fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
610 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Last), false) 640 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Last), false, true)
611} 641}
612 642
613fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 643fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
614 check(path, ra_fixture_before, ra_fixture_after, None, false) 644 check(path, ra_fixture_before, ra_fixture_after, None, false, true)
615} 645}
616 646
617fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) { 647fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) {
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/items_locator.rs
index 502e8281a..8a7f02935 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/items_locator.rs
@@ -1,9 +1,10 @@
1//! This module contains an import search functionality that is provided to the assists module. 1//! This module contains an import search functionality that is provided to the assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the assists module.
3 3
4use either::Either;
4use hir::{ 5use hir::{
5 import_map::{self, ImportKind}, 6 import_map::{self, ImportKind},
6 AsAssocItem, Crate, MacroDef, ModuleDef, Semantics, 7 AsAssocItem, Crate, ItemInNs, ModuleDef, Semantics,
7}; 8};
8use syntax::{ast, AstNode, SyntaxKind::NAME}; 9use syntax::{ast, AstNode, SyntaxKind::NAME};
9 10
@@ -12,47 +13,47 @@ use crate::{
12 symbol_index::{self, FileSymbol}, 13 symbol_index::{self, FileSymbol},
13 RootDatabase, 14 RootDatabase,
14}; 15};
15use either::Either;
16use rustc_hash::FxHashSet; 16use rustc_hash::FxHashSet;
17 17
18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40; 18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
19 19
20pub fn find_exact_imports<'a>( 20pub fn with_exact_name(
21 sema: &Semantics<'a, RootDatabase>, 21 sema: &Semantics<'_, RootDatabase>,
22 krate: Crate, 22 krate: Crate,
23 name_to_import: String, 23 exact_name: String,
24) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> { 24) -> FxHashSet<ItemInNs> {
25 let _p = profile::span("find_exact_imports"); 25 let _p = profile::span("find_exact_imports");
26 Box::new(find_imports( 26 find_items(
27 sema, 27 sema,
28 krate, 28 krate,
29 { 29 {
30 let mut local_query = symbol_index::Query::new(name_to_import.clone()); 30 let mut local_query = symbol_index::Query::new(exact_name.clone());
31 local_query.exact(); 31 local_query.exact();
32 local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT); 32 local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
33 local_query 33 local_query
34 }, 34 },
35 import_map::Query::new(name_to_import) 35 import_map::Query::new(exact_name)
36 .limit(DEFAULT_QUERY_SEARCH_LIMIT) 36 .limit(DEFAULT_QUERY_SEARCH_LIMIT)
37 .name_only() 37 .name_only()
38 .search_mode(import_map::SearchMode::Equals) 38 .search_mode(import_map::SearchMode::Equals)
39 .case_sensitive(), 39 .case_sensitive(),
40 )) 40 )
41} 41}
42 42
43#[derive(Debug)]
43pub enum AssocItemSearch { 44pub enum AssocItemSearch {
44 Include, 45 Include,
45 Exclude, 46 Exclude,
46 AssocItemsOnly, 47 AssocItemsOnly,
47} 48}
48 49
49pub fn find_similar_imports<'a>( 50pub fn with_similar_name(
50 sema: &Semantics<'a, RootDatabase>, 51 sema: &Semantics<'_, RootDatabase>,
51 krate: Crate, 52 krate: Crate,
52 fuzzy_search_string: String, 53 fuzzy_search_string: String,
53 assoc_item_search: AssocItemSearch, 54 assoc_item_search: AssocItemSearch,
54 limit: Option<usize>, 55 limit: Option<usize>,
55) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { 56) -> FxHashSet<ItemInNs> {
56 let _p = profile::span("find_similar_imports"); 57 let _p = profile::span("find_similar_imports");
57 58
58 let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) 59 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
@@ -76,37 +77,39 @@ pub fn find_similar_imports<'a>(
76 local_query.limit(limit); 77 local_query.limit(limit);
77 } 78 }
78 79
79 let db = sema.db; 80 find_items(sema, krate, local_query, external_query)
80 Box::new(find_imports(sema, krate, local_query, external_query).filter( 81 .into_iter()
81 move |import_candidate| match assoc_item_search { 82 .filter(move |&item| match assoc_item_search {
82 AssocItemSearch::Include => true, 83 AssocItemSearch::Include => true,
83 AssocItemSearch::Exclude => !is_assoc_item(import_candidate, db), 84 AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
84 AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, db), 85 AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
85 }, 86 })
86 )) 87 .collect()
87} 88}
88 89
89fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool { 90fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
90 match import_candidate { 91 item.as_module_def_id()
91 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(), 92 .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
92 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(), 93 .is_some()
93 Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(),
94 _ => false,
95 }
96} 94}
97 95
98fn find_imports<'a>( 96fn find_items(
99 sema: &Semantics<'a, RootDatabase>, 97 sema: &Semantics<'_, RootDatabase>,
100 krate: Crate, 98 krate: Crate,
101 local_query: symbol_index::Query, 99 local_query: symbol_index::Query,
102 external_query: import_map::Query, 100 external_query: import_map::Query,
103) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { 101) -> FxHashSet<ItemInNs> {
104 let _p = profile::span("find_similar_imports"); 102 let _p = profile::span("find_similar_imports");
105 let db = sema.db; 103 let db = sema.db;
106 104
107 // Query dependencies first. 105 // Query dependencies first.
108 let mut candidates: FxHashSet<_> = 106 let mut candidates = krate
109 krate.query_external_importables(db, external_query).collect(); 107 .query_external_importables(db, external_query)
108 .map(|external_importable| match external_importable {
109 Either::Left(module_def) => ItemInNs::from(module_def),
110 Either::Right(macro_def) => ItemInNs::from(macro_def),
111 })
112 .collect::<FxHashSet<_>>();
110 113
111 // Query the local crate using the symbol index. 114 // Query the local crate using the symbol index.
112 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); 115 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query);
@@ -114,19 +117,19 @@ fn find_imports<'a>(
114 candidates.extend( 117 candidates.extend(
115 local_results 118 local_results
116 .into_iter() 119 .into_iter()
117 .filter_map(|import_candidate| get_name_definition(sema, &import_candidate)) 120 .filter_map(|local_candidate| get_name_definition(sema, &local_candidate))
118 .filter_map(|name_definition_to_import| match name_definition_to_import { 121 .filter_map(|name_definition_to_import| match name_definition_to_import {
119 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), 122 Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)),
120 Definition::Macro(macro_def) => Some(Either::Right(macro_def)), 123 Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)),
121 _ => None, 124 _ => None,
122 }), 125 }),
123 ); 126 );
124 127
125 candidates.into_iter() 128 candidates
126} 129}
127 130
128fn get_name_definition<'a>( 131fn get_name_definition(
129 sema: &Semantics<'a, RootDatabase>, 132 sema: &Semantics<'_, RootDatabase>,
130 import_candidate: &FileSymbol, 133 import_candidate: &FileSymbol,
131) -> Option<Definition> { 134) -> Option<Definition> {
132 let _p = profile::span("get_name_definition"); 135 let _p = profile::span("get_name_definition");
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 6eb34b06b..88ee4a87d 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -8,7 +8,7 @@ pub mod line_index;
8pub mod symbol_index; 8pub mod symbol_index;
9pub mod defs; 9pub mod defs;
10pub mod search; 10pub mod search;
11pub mod imports_locator; 11pub mod items_locator;
12pub mod source_change; 12pub mod source_change;
13pub mod ty_filter; 13pub mod ty_filter;
14pub mod traits; 14pub mod traits;
diff --git a/crates/ide_ssr/Cargo.toml b/crates/ide_ssr/Cargo.toml
index edbc1846b..8c31df13a 100644
--- a/crates/ide_ssr/Cargo.toml
+++ b/crates/ide_ssr/Cargo.toml
@@ -11,6 +11,7 @@ edition = "2018"
11doctest = false 11doctest = false
12 12
13[dependencies] 13[dependencies]
14cov-mark = "1.1"
14rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
15itertools = "0.10.0" 16itertools = "0.10.0"
16 17
@@ -18,7 +19,7 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
18syntax = { path = "../syntax", version = "0.0.0" } 19syntax = { path = "../syntax", version = "0.0.0" }
19ide_db = { path = "../ide_db", version = "0.0.0" } 20ide_db = { path = "../ide_db", version = "0.0.0" }
20hir = { path = "../hir", version = "0.0.0" } 21hir = { path = "../hir", version = "0.0.0" }
21test_utils = { path = "../test_utils", version = "0.0.0" }
22 22
23[dev-dependencies] 23[dev-dependencies]
24test_utils = { path = "../test_utils" }
24expect-test = "1.1" 25expect-test = "1.1"
diff --git a/crates/ide_ssr/src/matching.rs b/crates/ide_ssr/src/matching.rs
index df013bae9..e1adb381e 100644
--- a/crates/ide_ssr/src/matching.rs
+++ b/crates/ide_ssr/src/matching.rs
@@ -15,7 +15,6 @@ use syntax::{
15 ast::{AstNode, AstToken}, 15 ast::{AstNode, AstToken},
16 SmolStr, 16 SmolStr,
17}; 17};
18use test_utils::mark;
19 18
20// Creates a match error. If we're currently attempting to match some code that we thought we were 19// Creates a match error. If we're currently attempting to match some code that we thought we were
21// going to match, as indicated by the --debug-snippet flag, then populate the reason field. 20// going to match, as indicated by the --debug-snippet flag, then populate the reason field.
@@ -731,7 +730,7 @@ impl NodeKind {
731 fn matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed> { 730 fn matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed> {
732 let ok = match self { 731 let ok = match self {
733 Self::Literal => { 732 Self::Literal => {
734 mark::hit!(literal_constraint); 733 cov_mark::hit!(literal_constraint);
735 ast::Literal::can_cast(node.kind()) 734 ast::Literal::can_cast(node.kind())
736 } 735 }
737 }; 736 };
diff --git a/crates/ide_ssr/src/parsing.rs b/crates/ide_ssr/src/parsing.rs
index 3d5e4feb7..5ff25cb6d 100644
--- a/crates/ide_ssr/src/parsing.rs
+++ b/crates/ide_ssr/src/parsing.rs
@@ -10,7 +10,6 @@ use crate::{SsrError, SsrPattern, SsrRule};
10use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
11use std::{fmt::Display, str::FromStr}; 11use std::{fmt::Display, str::FromStr};
12use syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T}; 12use syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T};
13use test_utils::mark;
14 13
15#[derive(Debug)] 14#[derive(Debug)]
16pub(crate) struct ParsedRule { 15pub(crate) struct ParsedRule {
@@ -131,7 +130,7 @@ impl RuleBuilder {
131 let old_len = self.rules.len(); 130 let old_len = self.rules.len();
132 self.rules.retain(|rule| contains_path(&rule.pattern)); 131 self.rules.retain(|rule| contains_path(&rule.pattern));
133 if self.rules.len() < old_len { 132 if self.rules.len() < old_len {
134 mark::hit!(pattern_is_a_single_segment_path); 133 cov_mark::hit!(pattern_is_a_single_segment_path);
135 } 134 }
136 } 135 }
137 Ok(self.rules) 136 Ok(self.rules)
diff --git a/crates/ide_ssr/src/replacing.rs b/crates/ide_ssr/src/replacing.rs
index 06a94a46c..c9ccc1961 100644
--- a/crates/ide_ssr/src/replacing.rs
+++ b/crates/ide_ssr/src/replacing.rs
@@ -5,7 +5,7 @@ use itertools::Itertools;
5use rustc_hash::{FxHashMap, FxHashSet}; 5use rustc_hash::{FxHashMap, FxHashSet};
6use syntax::ast::{self, AstNode, AstToken}; 6use syntax::ast::{self, AstNode, AstToken};
7use syntax::{SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize}; 7use syntax::{SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize};
8use test_utils::mark; 8
9use text_edit::TextEdit; 9use text_edit::TextEdit;
10 10
11/// Returns a text edit that will replace each match in `matches` with its corresponding replacement 11/// Returns a text edit that will replace each match in `matches` with its corresponding replacement
@@ -128,7 +128,7 @@ impl ReplacementRenderer<'_> {
128 && (placeholder_value.autoderef_count > 0 128 && (placeholder_value.autoderef_count > 0
129 || placeholder_value.autoref_kind != ast::SelfParamKind::Owned) 129 || placeholder_value.autoref_kind != ast::SelfParamKind::Owned)
130 { 130 {
131 mark::hit!(replace_autoref_autoderef_capture); 131 cov_mark::hit!(replace_autoref_autoderef_capture);
132 let ref_kind = match placeholder_value.autoref_kind { 132 let ref_kind = match placeholder_value.autoref_kind {
133 ast::SelfParamKind::Owned => "", 133 ast::SelfParamKind::Owned => "",
134 ast::SelfParamKind::Ref => "&", 134 ast::SelfParamKind::Ref => "&",
diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs
index 14e5a3b69..af94c7bb1 100644
--- a/crates/ide_ssr/src/resolving.rs
+++ b/crates/ide_ssr/src/resolving.rs
@@ -6,7 +6,6 @@ use ide_db::base_db::FilePosition;
6use parsing::Placeholder; 6use parsing::Placeholder;
7use rustc_hash::FxHashMap; 7use rustc_hash::FxHashMap;
8use syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken}; 8use syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken};
9use test_utils::mark;
10 9
11pub(crate) struct ResolutionScope<'db> { 10pub(crate) struct ResolutionScope<'db> {
12 scope: hir::SemanticsScope<'db>, 11 scope: hir::SemanticsScope<'db>,
@@ -170,13 +169,13 @@ impl Resolver<'_, '_> {
170 // calls. e.g. `Foo::bar($s)` should match `x.bar()`. 169 // calls. e.g. `Foo::bar($s)` should match `x.bar()`.
171 true 170 true
172 } else { 171 } else {
173 mark::hit!(replace_associated_trait_default_function_call); 172 cov_mark::hit!(replace_associated_trait_default_function_call);
174 false 173 false
175 } 174 }
176 } 175 }
177 hir::PathResolution::AssocItem(_) => { 176 hir::PathResolution::AssocItem(_) => {
178 // Not a function. Could be a constant or an associated type. 177 // Not a function. Could be a constant or an associated type.
179 mark::hit!(replace_associated_trait_constant); 178 cov_mark::hit!(replace_associated_trait_constant);
180 false 179 false
181 } 180 }
182 _ => true, 181 _ => true,
@@ -267,7 +266,7 @@ fn pick_node_for_resolution(node: SyntaxNode) -> SyntaxNode {
267 match node.kind() { 266 match node.kind() {
268 SyntaxKind::EXPR_STMT => { 267 SyntaxKind::EXPR_STMT => {
269 if let Some(n) = node.first_child() { 268 if let Some(n) = node.first_child() {
270 mark::hit!(cursor_after_semicolon); 269 cov_mark::hit!(cursor_after_semicolon);
271 return n; 270 return n;
272 } 271 }
273 } 272 }
@@ -291,7 +290,7 @@ fn path_contains_type_arguments(path: Option<ast::Path>) -> bool {
291 if let Some(path) = path { 290 if let Some(path) = path {
292 if let Some(segment) = path.segment() { 291 if let Some(segment) = path.segment() {
293 if segment.generic_arg_list().is_some() { 292 if segment.generic_arg_list().is_some() {
294 mark::hit!(type_arguments_within_path); 293 cov_mark::hit!(type_arguments_within_path);
295 return true; 294 return true;
296 } 295 }
297 } 296 }
diff --git a/crates/ide_ssr/src/search.rs b/crates/ide_ssr/src/search.rs
index 836eb94b2..28cef742c 100644
--- a/crates/ide_ssr/src/search.rs
+++ b/crates/ide_ssr/src/search.rs
@@ -12,7 +12,6 @@ use ide_db::{
12}; 12};
13use rustc_hash::FxHashSet; 13use rustc_hash::FxHashSet;
14use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; 14use syntax::{ast, AstNode, SyntaxKind, SyntaxNode};
15use test_utils::mark;
16 15
17/// A cache for the results of find_usages. This is for when we have multiple patterns that have the 16/// A cache for the results of find_usages. This is for when we have multiple patterns that have the
18/// same path. e.g. if the pattern was `foo::Bar` that can parse as a path, an expression, a type 17/// same path. e.g. if the pattern was `foo::Bar` that can parse as a path, an expression, a type
@@ -61,7 +60,7 @@ impl<'db> MatchFinder<'db> {
61 for file_range in self.find_usages(usage_cache, definition).file_ranges() { 60 for file_range in self.find_usages(usage_cache, definition).file_ranges() {
62 if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) { 61 if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) {
63 if !is_search_permitted_ancestors(&node_to_match) { 62 if !is_search_permitted_ancestors(&node_to_match) {
64 mark::hit!(use_declaration_with_braces); 63 cov_mark::hit!(use_declaration_with_braces);
65 continue; 64 continue;
66 } 65 }
67 self.try_add_match(rule, &node_to_match, &None, matches_out); 66 self.try_add_match(rule, &node_to_match, &None, matches_out);
@@ -205,7 +204,7 @@ impl<'db> MatchFinder<'db> {
205 matches_out: &mut Vec<Match>, 204 matches_out: &mut Vec<Match>,
206 ) { 205 ) {
207 if !self.within_range_restrictions(code) { 206 if !self.within_range_restrictions(code) {
208 mark::hit!(replace_nonpath_within_selection); 207 cov_mark::hit!(replace_nonpath_within_selection);
209 return; 208 return;
210 } 209 }
211 if let Ok(m) = matching::get_match(false, rule, code, restrict_range, &self.sema) { 210 if let Ok(m) = matching::get_match(false, rule, code, restrict_range, &self.sema) {
diff --git a/crates/ide_ssr/src/tests.rs b/crates/ide_ssr/src/tests.rs
index a3ea44f23..1d8565dc0 100644
--- a/crates/ide_ssr/src/tests.rs
+++ b/crates/ide_ssr/src/tests.rs
@@ -3,7 +3,7 @@ use expect_test::{expect, Expect};
3use ide_db::base_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt}; 3use ide_db::base_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use std::sync::Arc; 5use std::sync::Arc;
6use test_utils::{mark, RangeOrOffset}; 6use test_utils::RangeOrOffset;
7 7
8fn parse_error_text(query: &str) -> String { 8fn parse_error_text(query: &str) -> String {
9 format!("{}", query.parse::<SsrRule>().unwrap_err()) 9 format!("{}", query.parse::<SsrRule>().unwrap_err())
@@ -492,7 +492,7 @@ fn match_resolved_type_name() {
492 492
493#[test] 493#[test]
494fn type_arguments_within_path() { 494fn type_arguments_within_path() {
495 mark::check!(type_arguments_within_path); 495 cov_mark::check!(type_arguments_within_path);
496 let code = r#" 496 let code = r#"
497 mod foo { 497 mod foo {
498 pub struct Bar<T> {t: T} 498 pub struct Bar<T> {t: T}
@@ -508,7 +508,7 @@ fn type_arguments_within_path() {
508 508
509#[test] 509#[test]
510fn literal_constraint() { 510fn literal_constraint() {
511 mark::check!(literal_constraint); 511 cov_mark::check!(literal_constraint);
512 let code = r#" 512 let code = r#"
513 enum Option<T> { Some(T), None } 513 enum Option<T> { Some(T), None }
514 use Option::Some; 514 use Option::Some;
@@ -641,7 +641,7 @@ fn replace_associated_function_call() {
641 641
642#[test] 642#[test]
643fn replace_associated_trait_default_function_call() { 643fn replace_associated_trait_default_function_call() {
644 mark::check!(replace_associated_trait_default_function_call); 644 cov_mark::check!(replace_associated_trait_default_function_call);
645 assert_ssr_transform( 645 assert_ssr_transform(
646 "Bar2::foo() ==>> Bar2::foo2()", 646 "Bar2::foo() ==>> Bar2::foo2()",
647 r#" 647 r#"
@@ -673,7 +673,7 @@ fn replace_associated_trait_default_function_call() {
673 673
674#[test] 674#[test]
675fn replace_associated_trait_constant() { 675fn replace_associated_trait_constant() {
676 mark::check!(replace_associated_trait_constant); 676 cov_mark::check!(replace_associated_trait_constant);
677 assert_ssr_transform( 677 assert_ssr_transform(
678 "Bar2::VALUE ==>> Bar2::VALUE_2222", 678 "Bar2::VALUE ==>> Bar2::VALUE_2222",
679 r#" 679 r#"
@@ -998,7 +998,7 @@ fn use_declaration_with_braces() {
998 // It would be OK for a path rule to match and alter a use declaration. We shouldn't mess it up 998 // It would be OK for a path rule to match and alter a use declaration. We shouldn't mess it up
999 // though. In particular, we must not change `use foo::{baz, bar}` to `use foo::{baz, 999 // though. In particular, we must not change `use foo::{baz, bar}` to `use foo::{baz,
1000 // foo2::bar2}`. 1000 // foo2::bar2}`.
1001 mark::check!(use_declaration_with_braces); 1001 cov_mark::check!(use_declaration_with_braces);
1002 assert_ssr_transform( 1002 assert_ssr_transform(
1003 "foo::bar ==>> foo2::bar2", 1003 "foo::bar ==>> foo2::bar2",
1004 r#" 1004 r#"
@@ -1076,7 +1076,7 @@ fn ufcs_matches_method_call() {
1076 1076
1077#[test] 1077#[test]
1078fn pattern_is_a_single_segment_path() { 1078fn pattern_is_a_single_segment_path() {
1079 mark::check!(pattern_is_a_single_segment_path); 1079 cov_mark::check!(pattern_is_a_single_segment_path);
1080 // The first function should not be altered because the `foo` in scope at the cursor position is 1080 // The first function should not be altered because the `foo` in scope at the cursor position is
1081 // a different `foo`. This case is special because "foo" can be parsed as a pattern (IDENT_PAT -> 1081 // a different `foo`. This case is special because "foo" can be parsed as a pattern (IDENT_PAT ->
1082 // NAME -> IDENT), which contains no path. If we're not careful we'll end up matching the `foo` 1082 // NAME -> IDENT), which contains no path. If we're not careful we'll end up matching the `foo`
@@ -1118,7 +1118,7 @@ fn replace_local_variable_reference() {
1118 // The pattern references a local variable `foo` in the block containing the cursor. We should 1118 // The pattern references a local variable `foo` in the block containing the cursor. We should
1119 // only replace references to this variable `foo`, not other variables that just happen to have 1119 // only replace references to this variable `foo`, not other variables that just happen to have
1120 // the same name. 1120 // the same name.
1121 mark::check!(cursor_after_semicolon); 1121 cov_mark::check!(cursor_after_semicolon);
1122 assert_ssr_transform( 1122 assert_ssr_transform(
1123 "foo + $a ==>> $a - foo", 1123 "foo + $a ==>> $a - foo",
1124 r#" 1124 r#"
@@ -1179,7 +1179,7 @@ fn replace_path_within_selection() {
1179 1179
1180#[test] 1180#[test]
1181fn replace_nonpath_within_selection() { 1181fn replace_nonpath_within_selection() {
1182 mark::check!(replace_nonpath_within_selection); 1182 cov_mark::check!(replace_nonpath_within_selection);
1183 assert_ssr_transform( 1183 assert_ssr_transform(
1184 "$a + $b ==>> $b * $a", 1184 "$a + $b ==>> $b * $a",
1185 r#" 1185 r#"
@@ -1269,7 +1269,7 @@ fn replace_autoref_autoderef_capture() {
1269 // second, we already have a reference, so it isn't. When $a is used in a context where autoref 1269 // second, we already have a reference, so it isn't. When $a is used in a context where autoref
1270 // doesn't apply, we need to prefix it with `&`. Finally, we have some cases where autoderef 1270 // doesn't apply, we need to prefix it with `&`. Finally, we have some cases where autoderef
1271 // needs to be applied. 1271 // needs to be applied.
1272 mark::check!(replace_autoref_autoderef_capture); 1272 cov_mark::check!(replace_autoref_autoderef_capture);
1273 let code = r#" 1273 let code = r#"
1274 struct Foo {} 1274 struct Foo {}
1275 impl Foo { 1275 impl Foo {
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml
index bb2656a80..139214207 100644
--- a/crates/mbe/Cargo.toml
+++ b/crates/mbe/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1"
13rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
14smallvec = "1.2.0" 15smallvec = "1.2.0"
15log = "0.4.8" 16log = "0.4.8"
@@ -17,8 +18,8 @@ log = "0.4.8"
17syntax = { path = "../syntax", version = "0.0.0" } 18syntax = { path = "../syntax", version = "0.0.0" }
18parser = { path = "../parser", version = "0.0.0" } 19parser = { path = "../parser", version = "0.0.0" }
19tt = { path = "../tt", version = "0.0.0" } 20tt = { path = "../tt", version = "0.0.0" }
20test_utils = { path = "../test_utils", version = "0.0.0" }
21stdx = { path = "../stdx", version = "0.0.0" } 21stdx = { path = "../stdx", version = "0.0.0" }
22 22
23[dev-dependencies] 23[dev-dependencies]
24profile = { path = "../profile" } 24profile = { path = "../profile" }
25test_utils = { path = "../test_utils" }
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index f3d2da55a..33b85e23d 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -17,7 +17,6 @@ mod benchmark;
17 17
18use std::fmt; 18use std::fmt;
19 19
20use test_utils::mark;
21pub use tt::{Delimiter, DelimiterKind, Punct}; 20pub use tt::{Delimiter, DelimiterKind, Punct};
22 21
23use crate::{ 22use crate::{
@@ -217,7 +216,7 @@ impl MacroDef {
217 let mut rules = Vec::new(); 216 let mut rules = Vec::new();
218 217
219 if Some(tt::DelimiterKind::Brace) == tt.delimiter_kind() { 218 if Some(tt::DelimiterKind::Brace) == tt.delimiter_kind() {
220 mark::hit!(parse_macro_def_rules); 219 cov_mark::hit!(parse_macro_def_rules);
221 while src.len() > 0 { 220 while src.len() > 0 {
222 let rule = Rule::parse(&mut src, true)?; 221 let rule = Rule::parse(&mut src, true)?;
223 rules.push(rule); 222 rules.push(rule);
@@ -229,7 +228,7 @@ impl MacroDef {
229 } 228 }
230 } 229 }
231 } else { 230 } else {
232 mark::hit!(parse_macro_def_simple); 231 cov_mark::hit!(parse_macro_def_simple);
233 let rule = Rule::parse(&mut src, false)?; 232 let rule = Rule::parse(&mut src, false)?;
234 if src.len() != 0 { 233 if src.len() != 0 {
235 return Err(ParseError::Expected("remain tokens in macro def".to_string())); 234 return Err(ParseError::Expected("remain tokens in macro def".to_string()));
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 5c641ebf2..3a168bb4b 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -6,7 +6,7 @@ use syntax::{
6 SyntaxKind::{ERROR, IDENT}, 6 SyntaxKind::{ERROR, IDENT},
7 SyntaxNode, WalkEvent, T, 7 SyntaxNode, WalkEvent, T,
8}; 8};
9use test_utils::{assert_eq_text, mark}; 9use test_utils::assert_eq_text;
10 10
11use super::*; 11use super::*;
12 12
@@ -687,7 +687,7 @@ fn test_match_literal() {
687 687
688#[test] 688#[test]
689fn test_parse_macro_def_simple() { 689fn test_parse_macro_def_simple() {
690 mark::check!(parse_macro_def_simple); 690 cov_mark::check!(parse_macro_def_simple);
691 691
692 parse_macro2( 692 parse_macro2(
693 r#" 693 r#"
@@ -701,7 +701,7 @@ macro foo($id:ident) {
701 701
702#[test] 702#[test]
703fn test_parse_macro_def_rules() { 703fn test_parse_macro_def_rules() {
704 mark::check!(parse_macro_def_rules); 704 cov_mark::check!(parse_macro_def_rules);
705 705
706 parse_macro2( 706 parse_macro2(
707 r#" 707 r#"
@@ -954,7 +954,8 @@ fn test_meta() {
954 .assert_expand_items( 954 .assert_expand_items(
955 r#"foo! { cfg(target_os = "windows") }"#, 955 r#"foo! { cfg(target_os = "windows") }"#,
956 r#"# [cfg (target_os = "windows")] fn bar () {}"#, 956 r#"# [cfg (target_os = "windows")] fn bar () {}"#,
957 ); 957 )
958 .assert_expand_items(r#"foo! { hello::world }"#, r#"# [hello :: world] fn bar () {}"#);
958} 959}
959 960
960#[test] 961#[test]
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 6913e9ec2..6159d064c 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -95,7 +95,7 @@ pub(crate) mod fragments {
95 // https://doc.rust-lang.org/reference/paths.html#simple-paths 95 // https://doc.rust-lang.org/reference/paths.html#simple-paths
96 // The start of an meta must be a simple path 96 // The start of an meta must be a simple path
97 match p.current() { 97 match p.current() {
98 IDENT | T![::] | T![super] | T![self] | T![crate] => p.bump_any(), 98 IDENT | T![super] | T![self] | T![crate] => p.bump_any(),
99 T![=] => { 99 T![=] => {
100 p.bump_any(); 100 p.bump_any();
101 match p.current() { 101 match p.current() {
@@ -105,6 +105,7 @@ pub(crate) mod fragments {
105 } 105 }
106 break; 106 break;
107 } 107 }
108 _ if p.at(T![::]) => p.bump(T![::]),
108 _ => break, 109 _ => break,
109 } 110 }
110 } 111 }
diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml
index 4c1b3036a..63b3f1532 100644
--- a/crates/proc_macro_srv/Cargo.toml
+++ b/crates/proc_macro_srv/Cargo.toml
@@ -17,9 +17,9 @@ memmap2 = "0.2.0"
17tt = { path = "../tt", version = "0.0.0" } 17tt = { path = "../tt", version = "0.0.0" }
18mbe = { path = "../mbe", version = "0.0.0" } 18mbe = { path = "../mbe", version = "0.0.0" }
19proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } 19proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" }
20test_utils = { path = "../test_utils", version = "0.0.0" }
21 20
22[dev-dependencies] 21[dev-dependencies]
22test_utils = { path = "../test_utils" }
23cargo_metadata = "0.13" 23cargo_metadata = "0.13"
24 24
25# used as proc macro test targets 25# used as proc macro test targets
diff --git a/crates/proc_macro_srv/src/dylib.rs b/crates/proc_macro_srv/src/dylib.rs
index 28a6ee547..baf10fea9 100644
--- a/crates/proc_macro_srv/src/dylib.rs
+++ b/crates/proc_macro_srv/src/dylib.rs
@@ -138,7 +138,7 @@ impl Expander {
138 parsed_body, 138 parsed_body,
139 false, 139 false,
140 ); 140 );
141 return res.map(|it| it.subtree); 141 return res.map(|it| it.into_subtree());
142 } 142 }
143 bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => { 143 bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
144 let res = client.run( 144 let res = client.run(
@@ -147,7 +147,7 @@ impl Expander {
147 parsed_body, 147 parsed_body,
148 false, 148 false,
149 ); 149 );
150 return res.map(|it| it.subtree); 150 return res.map(|it| it.into_subtree());
151 } 151 }
152 bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => { 152 bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
153 let res = client.run( 153 let res = client.run(
@@ -157,7 +157,7 @@ impl Expander {
157 parsed_body, 157 parsed_body,
158 false, 158 false,
159 ); 159 );
160 return res.map(|it| it.subtree); 160 return res.map(|it| it.into_subtree());
161 } 161 }
162 _ => continue, 162 _ => continue,
163 } 163 }
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
index ca6749b9b..b036d4e20 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
@@ -238,7 +238,7 @@ macro_rules! define_client_side {
238 $(impl $name { 238 $(impl $name {
239 #[allow(unused)] 239 #[allow(unused)]
240 $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* { 240 $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* {
241 panic!("hello"); 241 panic!("crates should be linked against the sysroot version of proc_macro, not this one from rust-analyzer");
242 // Bridge::with(|bridge| { 242 // Bridge::with(|bridge| {
243 // let mut b = bridge.cached_buffer.take(); 243 // let mut b = bridge.cached_buffer.take();
244 244
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
index e4006a5ab..e67902682 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs
@@ -268,7 +268,7 @@ trait Mark {
268 fn mark(unmarked: Self::Unmarked) -> Self; 268 fn mark(unmarked: Self::Unmarked) -> Self;
269} 269}
270 270
271/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details). 271/// Unwrap types wrapped by `cov_mark::mark` (see `Mark` for details).
272trait Unmark { 272trait Unmark {
273 type Unmarked; 273 type Unmarked;
274 fn unmark(self) -> Self::Unmarked; 274 fn unmark(self) -> Self::Unmarked;
diff --git a/crates/proc_macro_srv/src/rustc_server.rs b/crates/proc_macro_srv/src/rustc_server.rs
index 952b4a97f..ceefd187d 100644
--- a/crates/proc_macro_srv/src/rustc_server.rs
+++ b/crates/proc_macro_srv/src/rustc_server.rs
@@ -25,27 +25,35 @@ type Span = tt::TokenId;
25 25
26#[derive(Debug, Clone)] 26#[derive(Debug, Clone)]
27pub struct TokenStream { 27pub struct TokenStream {
28 pub subtree: tt::Subtree, 28 pub token_trees: Vec<TokenTree>,
29} 29}
30 30
31impl TokenStream { 31impl TokenStream {
32 pub fn new() -> Self { 32 pub fn new() -> Self {
33 TokenStream { subtree: Default::default() } 33 TokenStream { token_trees: Default::default() }
34 } 34 }
35 35
36 pub fn with_subtree(subtree: tt::Subtree) -> Self { 36 pub fn with_subtree(subtree: tt::Subtree) -> Self {
37 TokenStream { subtree } 37 if subtree.delimiter.is_some() {
38 TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] }
39 } else {
40 TokenStream { token_trees: subtree.token_trees }
41 }
42 }
43
44 pub fn into_subtree(self) -> tt::Subtree {
45 tt::Subtree { delimiter: None, token_trees: self.token_trees }
38 } 46 }
39 47
40 pub fn is_empty(&self) -> bool { 48 pub fn is_empty(&self) -> bool {
41 self.subtree.token_trees.is_empty() 49 self.token_trees.is_empty()
42 } 50 }
43} 51}
44 52
45/// Creates a token stream containing a single token tree. 53/// Creates a token stream containing a single token tree.
46impl From<TokenTree> for TokenStream { 54impl From<TokenTree> for TokenStream {
47 fn from(tree: TokenTree) -> TokenStream { 55 fn from(tree: TokenTree) -> TokenStream {
48 TokenStream { subtree: tt::Subtree { delimiter: None, token_trees: vec![tree] } } 56 TokenStream { token_trees: vec![tree] }
49 } 57 }
50} 58}
51 59
@@ -78,10 +86,10 @@ impl Extend<TokenStream> for TokenStream {
78 for tkn in item { 86 for tkn in item {
79 match tkn { 87 match tkn {
80 tt::TokenTree::Subtree(subtree) if subtree.delimiter.is_none() => { 88 tt::TokenTree::Subtree(subtree) if subtree.delimiter.is_none() => {
81 self.subtree.token_trees.extend(subtree.token_trees); 89 self.token_trees.extend(subtree.token_trees);
82 } 90 }
83 _ => { 91 _ => {
84 self.subtree.token_trees.push(tkn); 92 self.token_trees.push(tkn);
85 } 93 }
86 } 94 }
87 } 95 }
@@ -164,7 +172,7 @@ pub mod token_stream {
164 type IntoIter = super::IntoIter<TokenTree>; 172 type IntoIter = super::IntoIter<TokenTree>;
165 173
166 fn into_iter(self) -> Self::IntoIter { 174 fn into_iter(self) -> Self::IntoIter {
167 self.subtree.token_trees.into_iter() 175 self.token_trees.into_iter()
168 } 176 }
169 } 177 }
170 178
@@ -184,28 +192,23 @@ pub mod token_stream {
184 let (subtree, _token_map) = 192 let (subtree, _token_map) =
185 mbe::parse_to_token_tree(src).ok_or("Failed to parse from mbe")?; 193 mbe::parse_to_token_tree(src).ok_or("Failed to parse from mbe")?;
186 194
187 Ok(TokenStream { subtree }) 195 let subtree = subtree_replace_token_ids_with_unspecified(subtree);
196 Ok(TokenStream::with_subtree(subtree))
188 } 197 }
189 } 198 }
190 199
191 impl ToString for TokenStream { 200 impl ToString for TokenStream {
192 fn to_string(&self) -> String { 201 fn to_string(&self) -> String {
193 let tt = self.subtree.clone().into(); 202 return tokentrees_to_text(&self.token_trees[..]);
194 to_text(&tt)
195 }
196 }
197 203
198 fn to_text(tkn: &tt::TokenTree) -> String { 204 fn tokentrees_to_text(tkns: &[tt::TokenTree]) -> String {
199 match tkn { 205 tkns.iter()
200 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.text.clone().into(),
201 tt::TokenTree::Leaf(tt::Leaf::Literal(literal)) => literal.text.clone().into(),
202 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => format!("{}", punct.char),
203 tt::TokenTree::Subtree(subtree) => {
204 let content = subtree
205 .token_trees
206 .iter()
207 .fold((String::new(), true), |(last, last_to_joint), tkn| { 206 .fold((String::new(), true), |(last, last_to_joint), tkn| {
208 let s = [last, to_text(tkn)].join(if last_to_joint { "" } else { " " }); 207 let s = [last, tokentree_to_text(tkn)].join(if last_to_joint {
208 ""
209 } else {
210 " "
211 });
209 let mut is_joint = false; 212 let mut is_joint = false;
210 if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn { 213 if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn {
211 if punct.spacing == tt::Spacing::Joint { 214 if punct.spacing == tt::Spacing::Joint {
@@ -214,15 +217,63 @@ pub mod token_stream {
214 } 217 }
215 (s, is_joint) 218 (s, is_joint)
216 }) 219 })
217 .0; 220 .0
218 221 }
219 let (open, close) = match subtree.delimiter.map(|it| it.kind) { 222
220 None => ("", ""), 223 fn tokentree_to_text(tkn: &tt::TokenTree) -> String {
221 Some(tt::DelimiterKind::Brace) => ("{", "}"), 224 match tkn {
222 Some(tt::DelimiterKind::Parenthesis) => ("(", ")"), 225 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.text.clone().into(),
223 Some(tt::DelimiterKind::Bracket) => ("[", "]"), 226 tt::TokenTree::Leaf(tt::Leaf::Literal(literal)) => literal.text.clone().into(),
224 }; 227 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => format!("{}", punct.char),
225 format!("{}{}{}", open, content, close) 228 tt::TokenTree::Subtree(subtree) => {
229 let content = tokentrees_to_text(&subtree.token_trees);
230 let (open, close) = match subtree.delimiter.map(|it| it.kind) {
231 None => ("", ""),
232 Some(tt::DelimiterKind::Brace) => ("{", "}"),
233 Some(tt::DelimiterKind::Parenthesis) => ("(", ")"),
234 Some(tt::DelimiterKind::Bracket) => ("[", "]"),
235 };
236 format!("{}{}{}", open, content, close)
237 }
238 }
239 }
240 }
241 }
242
243 fn subtree_replace_token_ids_with_unspecified(subtree: tt::Subtree) -> tt::Subtree {
244 tt::Subtree {
245 delimiter: subtree
246 .delimiter
247 .map(|d| tt::Delimiter { id: tt::TokenId::unspecified(), ..d }),
248 token_trees: subtree
249 .token_trees
250 .into_iter()
251 .map(|t| token_tree_replace_token_ids_with_unspecified(t))
252 .collect(),
253 }
254 }
255
256 fn token_tree_replace_token_ids_with_unspecified(tt: tt::TokenTree) -> tt::TokenTree {
257 match tt {
258 tt::TokenTree::Leaf(leaf) => {
259 tt::TokenTree::Leaf(leaf_replace_token_ids_with_unspecified(leaf))
260 }
261 tt::TokenTree::Subtree(subtree) => {
262 tt::TokenTree::Subtree(subtree_replace_token_ids_with_unspecified(subtree))
263 }
264 }
265 }
266
267 fn leaf_replace_token_ids_with_unspecified(leaf: tt::Leaf) -> tt::Leaf {
268 match leaf {
269 tt::Leaf::Literal(lit) => {
270 tt::Leaf::Literal(tt::Literal { id: tt::TokenId::unspecified(), ..lit })
271 }
272 tt::Leaf::Punct(punct) => {
273 tt::Leaf::Punct(tt::Punct { id: tt::TokenId::unspecified(), ..punct })
274 }
275 tt::Leaf::Ident(ident) => {
276 tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), ..ident })
226 } 277 }
227 } 278 }
228 } 279 }
@@ -277,42 +328,6 @@ impl server::FreeFunctions for Rustc {
277 } 328 }
278} 329}
279 330
280fn subtree_replace_token_ids_with_unspecified(subtree: tt::Subtree) -> tt::Subtree {
281 tt::Subtree {
282 delimiter: subtree.delimiter.map(|d| tt::Delimiter { id: tt::TokenId::unspecified(), ..d }),
283 token_trees: subtree
284 .token_trees
285 .into_iter()
286 .map(|t| token_tree_replace_token_ids_with_unspecified(t))
287 .collect(),
288 }
289}
290
291fn token_tree_replace_token_ids_with_unspecified(tt: tt::TokenTree) -> tt::TokenTree {
292 match tt {
293 tt::TokenTree::Leaf(leaf) => {
294 tt::TokenTree::Leaf(leaf_replace_token_ids_with_unspecified(leaf))
295 }
296 tt::TokenTree::Subtree(subtree) => {
297 tt::TokenTree::Subtree(subtree_replace_token_ids_with_unspecified(subtree))
298 }
299 }
300}
301
302fn leaf_replace_token_ids_with_unspecified(leaf: tt::Leaf) -> tt::Leaf {
303 match leaf {
304 tt::Leaf::Literal(lit) => {
305 tt::Leaf::Literal(tt::Literal { id: tt::TokenId::unspecified(), ..lit })
306 }
307 tt::Leaf::Punct(punct) => {
308 tt::Leaf::Punct(tt::Punct { id: tt::TokenId::unspecified(), ..punct })
309 }
310 tt::Leaf::Ident(ident) => {
311 tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), ..ident })
312 }
313 }
314}
315
316impl server::TokenStream for Rustc { 331impl server::TokenStream for Rustc {
317 fn new(&mut self) -> Self::TokenStream { 332 fn new(&mut self) -> Self::TokenStream {
318 Self::TokenStream::new() 333 Self::TokenStream::new()
@@ -322,8 +337,9 @@ impl server::TokenStream for Rustc {
322 stream.is_empty() 337 stream.is_empty()
323 } 338 }
324 fn from_str(&mut self, src: &str) -> Self::TokenStream { 339 fn from_str(&mut self, src: &str) -> Self::TokenStream {
325 let (subtree, _) = mbe::parse_to_token_tree(src).expect("cannot parse string"); 340 use std::str::FromStr;
326 TokenStream::with_subtree(subtree_replace_token_ids_with_unspecified(subtree)) 341
342 Self::TokenStream::from_str(src).expect("cannot parse string")
327 } 343 }
328 fn to_string(&mut self, stream: &Self::TokenStream) -> String { 344 fn to_string(&mut self, stream: &Self::TokenStream) -> String {
329 stream.to_string() 345 stream.to_string()
@@ -429,10 +445,7 @@ fn spacing_to_external(spacing: Spacing) -> bridge::Spacing {
429 445
430impl server::Group for Rustc { 446impl server::Group for Rustc {
431 fn new(&mut self, delimiter: bridge::Delimiter, stream: Self::TokenStream) -> Self::Group { 447 fn new(&mut self, delimiter: bridge::Delimiter, stream: Self::TokenStream) -> Self::Group {
432 Self::Group { 448 Self::Group { delimiter: delim_to_internal(delimiter), token_trees: stream.token_trees }
433 delimiter: delim_to_internal(delimiter),
434 token_trees: stream.subtree.token_trees,
435 }
436 } 449 }
437 fn delimiter(&mut self, group: &Self::Group) -> bridge::Delimiter { 450 fn delimiter(&mut self, group: &Self::Group) -> bridge::Delimiter {
438 delim_to_external(group.delimiter) 451 delim_to_external(group.delimiter)
@@ -440,9 +453,7 @@ impl server::Group for Rustc {
440 453
441 // NOTE: Return value of do not include delimiter 454 // NOTE: Return value of do not include delimiter
442 fn stream(&mut self, group: &Self::Group) -> Self::TokenStream { 455 fn stream(&mut self, group: &Self::Group) -> Self::TokenStream {
443 TokenStream { 456 TokenStream { token_trees: group.token_trees.clone() }
444 subtree: tt::Subtree { delimiter: None, token_trees: group.token_trees.clone() },
445 }
446 } 457 }
447 458
448 fn span(&mut self, group: &Self::Group) -> Self::Span { 459 fn span(&mut self, group: &Self::Group) -> Self::Span {
@@ -751,28 +762,48 @@ mod tests {
751 #[test] 762 #[test]
752 fn test_rustc_server_to_string() { 763 fn test_rustc_server_to_string() {
753 let s = TokenStream { 764 let s = TokenStream {
754 subtree: tt::Subtree { 765 token_trees: vec![
755 delimiter: None, 766 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
756 token_trees: vec![ 767 text: "struct".into(),
757 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { 768 id: tt::TokenId::unspecified(),
758 text: "struct".into(), 769 })),
759 id: tt::TokenId::unspecified(), 770 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
760 })), 771 text: "T".into(),
761 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { 772 id: tt::TokenId::unspecified(),
762 text: "T".into(), 773 })),
774 tt::TokenTree::Subtree(tt::Subtree {
775 delimiter: Some(tt::Delimiter {
763 id: tt::TokenId::unspecified(), 776 id: tt::TokenId::unspecified(),
764 })), 777 kind: tt::DelimiterKind::Brace,
765 tt::TokenTree::Subtree(tt::Subtree {
766 delimiter: Some(tt::Delimiter {
767 id: tt::TokenId::unspecified(),
768 kind: tt::DelimiterKind::Brace,
769 }),
770 token_trees: vec![],
771 }), 778 }),
772 ], 779 token_trees: vec![],
773 }, 780 }),
781 ],
774 }; 782 };
775 783
776 assert_eq!(s.to_string(), "struct T {}"); 784 assert_eq!(s.to_string(), "struct T {}");
777 } 785 }
786
787 #[test]
788 fn test_rustc_server_from_str() {
789 use std::str::FromStr;
790 let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree {
791 delimiter: Some(tt::Delimiter {
792 id: tt::TokenId::unspecified(),
793 kind: tt::DelimiterKind::Parenthesis,
794 }),
795 token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
796 text: "a".into(),
797 id: tt::TokenId::unspecified(),
798 }))],
799 });
800
801 let t1 = TokenStream::from_str("(a)").unwrap();
802 assert_eq!(t1.token_trees.len(), 1);
803 assert_eq!(t1.token_trees[0], subtree_paren_a);
804
805 let t2 = TokenStream::from_str("(a);").unwrap();
806 assert_eq!(t2.token_trees.len(), 2);
807 assert_eq!(t2.token_trees[0], subtree_paren_a);
808 }
778} 809}
diff --git a/crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt b/crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt
index ea34e688f..fa581f110 100644
--- a/crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt
+++ b/crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt
@@ -101,7 +101,7 @@ SUBTREE $
101 PUNCH : [alone] 4294967295 101 PUNCH : [alone] 4294967295
102 IDENT Serialize 4294967295 102 IDENT Serialize 4294967295
103 IDENT for 4294967295 103 IDENT for 4294967295
104 IDENT Foo 1 104 IDENT Foo 4294967295
105 SUBTREE {} 4294967295 105 SUBTREE {} 4294967295
106 IDENT fn 4294967295 106 IDENT fn 4294967295
107 IDENT serialize 4294967295 107 IDENT serialize 4294967295
diff --git a/crates/proc_macro_srv/src/tests/utils.rs b/crates/proc_macro_srv/src/tests/utils.rs
index 22813052d..0484c3af4 100644
--- a/crates/proc_macro_srv/src/tests/utils.rs
+++ b/crates/proc_macro_srv/src/tests/utils.rs
@@ -52,7 +52,7 @@ pub fn assert_expand(
52 let expander = dylib::Expander::new(&path).unwrap(); 52 let expander = dylib::Expander::new(&path).unwrap();
53 let fixture = parse_string(ra_fixture).unwrap(); 53 let fixture = parse_string(ra_fixture).unwrap();
54 54
55 let res = expander.expand(macro_name, &fixture.subtree, None).unwrap(); 55 let res = expander.expand(macro_name, &fixture.into_subtree(), None).unwrap();
56 assert_eq_text!(&expect.trim(), &format!("{:?}", res)); 56 assert_eq_text!(&expect.trim(), &format!("{:?}", res));
57} 57}
58 58
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index f7241b711..bc6e20341 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -9,6 +9,8 @@ use cargo_metadata::{CargoOpt, MetadataCommand};
9use la_arena::{Arena, Idx}; 9use la_arena::{Arena, Idx};
10use paths::{AbsPath, AbsPathBuf}; 10use paths::{AbsPath, AbsPathBuf};
11use rustc_hash::FxHashMap; 11use rustc_hash::FxHashMap;
12use serde::Deserialize;
13use serde_json::from_value;
12 14
13use crate::build_data::BuildDataConfig; 15use crate::build_data::BuildDataConfig;
14use crate::utf8_stdout; 16use crate::utf8_stdout;
@@ -104,6 +106,13 @@ pub struct PackageData {
104 pub active_features: Vec<String>, 106 pub active_features: Vec<String>,
105 // String representation of package id 107 // String representation of package id
106 pub id: String, 108 pub id: String,
109 // The contents of [package.metadata.rust-analyzer]
110 pub metadata: RustAnalyzerPackageMetaData,
111}
112
113#[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)]
114pub struct RustAnalyzerPackageMetaData {
115 pub rustc_private: bool,
107} 116}
108 117
109#[derive(Debug, Clone, Eq, PartialEq)] 118#[derive(Debug, Clone, Eq, PartialEq)]
@@ -161,6 +170,13 @@ impl PackageData {
161 } 170 }
162} 171}
163 172
173#[derive(Deserialize, Default)]
174// Deserialise helper for the cargo metadata
175struct PackageMetadata {
176 #[serde(rename = "rust-analyzer")]
177 rust_analyzer: Option<RustAnalyzerPackageMetaData>,
178}
179
164impl CargoWorkspace { 180impl CargoWorkspace {
165 pub fn from_cargo_metadata( 181 pub fn from_cargo_metadata(
166 cargo_toml: &AbsPath, 182 cargo_toml: &AbsPath,
@@ -244,8 +260,10 @@ impl CargoWorkspace {
244 260
245 meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); 261 meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
246 for meta_pkg in &meta.packages { 262 for meta_pkg in &meta.packages {
247 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = 263 let cargo_metadata::Package {
248 meta_pkg; 264 id, edition, name, manifest_path, version, metadata, ..
265 } = meta_pkg;
266 let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default();
249 let is_member = ws_members.contains(&id); 267 let is_member = ws_members.contains(&id);
250 let edition = edition 268 let edition = edition
251 .parse::<Edition>() 269 .parse::<Edition>()
@@ -262,6 +280,7 @@ impl CargoWorkspace {
262 dependencies: Vec::new(), 280 dependencies: Vec::new(),
263 features: meta_pkg.features.clone().into_iter().collect(), 281 features: meta_pkg.features.clone().into_iter().collect(),
264 active_features: Vec::new(), 282 active_features: Vec::new(),
283 metadata: meta.rust_analyzer.unwrap_or_default(),
265 }); 284 });
266 let pkg_data = &mut packages[pkg]; 285 let pkg_data = &mut packages[pkg];
267 pkg_by_id.insert(id, pkg); 286 pkg_by_id.insert(id, pkg);
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 0220efdb4..1b53fcc30 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -2,11 +2,7 @@
2//! metadata` or `rust-project.json`) into representation stored in the salsa 2//! metadata` or `rust-project.json`) into representation stored in the salsa
3//! database -- `CrateGraph`. 3//! database -- `CrateGraph`.
4 4
5use std::{ 5use std::{collections::VecDeque, fmt, fs, path::Path, process::Command};
6 fmt, fs,
7 path::{Component, Path},
8 process::Command,
9};
10 6
11use anyhow::{Context, Result}; 7use anyhow::{Context, Result};
12use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; 8use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
@@ -60,6 +56,7 @@ impl fmt::Debug for ProjectWorkspace {
60 match self { 56 match self {
61 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f 57 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f
62 .debug_struct("Cargo") 58 .debug_struct("Cargo")
59 .field("root", &cargo.workspace_root().file_name())
63 .field("n_packages", &cargo.packages().len()) 60 .field("n_packages", &cargo.packages().len())
64 .field("n_sysroot_crates", &sysroot.crates().len()) 61 .field("n_sysroot_crates", &sysroot.crates().len())
65 .field( 62 .field(
@@ -279,11 +276,8 @@ impl ProjectWorkspace {
279 276
280 pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) { 277 pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) {
281 match self { 278 match self {
282 ProjectWorkspace::Cargo { cargo, rustc, .. } => { 279 ProjectWorkspace::Cargo { cargo, .. } => {
283 collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone()); 280 collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone());
284 if let Some(rustc) = rustc {
285 collector.add_config(rustc.workspace_root(), rustc.build_data_config().clone());
286 }
287 } 281 }
288 _ => {} 282 _ => {}
289 } 283 }
@@ -380,9 +374,11 @@ fn cargo_to_crate_graph(
380 cfg_options.insert_atom("debug_assertions".into()); 374 cfg_options.insert_atom("debug_assertions".into());
381 375
382 let mut pkg_crates = FxHashMap::default(); 376 let mut pkg_crates = FxHashMap::default();
383 377 // Does any crate signal to rust-analyzer that they need the rustc_private crates?
378 let mut has_private = false;
384 // Next, create crates for each package, target pair 379 // Next, create crates for each package, target pair
385 for pkg in cargo.packages() { 380 for pkg in cargo.packages() {
381 has_private |= cargo[pkg].metadata.rustc_private;
386 let mut lib_tgt = None; 382 let mut lib_tgt = None;
387 for &tgt in cargo[pkg].targets.iter() { 383 for &tgt in cargo[pkg].targets.iter() {
388 if let Some(file_id) = load(&cargo[tgt].root) { 384 if let Some(file_id) = load(&cargo[tgt].root) {
@@ -443,28 +439,66 @@ fn cargo_to_crate_graph(
443 } 439 }
444 } 440 }
445 441
446 let mut rustc_pkg_crates = FxHashMap::default(); 442 if has_private {
443 // If the user provided a path to rustc sources, we add all the rustc_private crates
444 // and create dependencies on them for the crates which opt-in to that
445 if let Some(rustc_workspace) = rustc {
446 handle_rustc_crates(
447 rustc_workspace,
448 load,
449 &mut crate_graph,
450 rustc_build_data_map,
451 &cfg_options,
452 proc_macro_loader,
453 &mut pkg_to_lib_crate,
454 &public_deps,
455 cargo,
456 &pkg_crates,
457 );
458 }
459 }
460 crate_graph
461}
447 462
448 // If the user provided a path to rustc sources, we add all the rustc_private crates 463fn handle_rustc_crates(
449 // and create dependencies on them for the crates in the current workspace 464 rustc_workspace: &CargoWorkspace,
450 if let Some(rustc_workspace) = rustc { 465 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
451 for pkg in rustc_workspace.packages() { 466 crate_graph: &mut CrateGraph,
467 rustc_build_data_map: Option<&FxHashMap<String, BuildData>>,
468 cfg_options: &CfgOptions,
469 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
470 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
471 public_deps: &[(CrateName, CrateId)],
472 cargo: &CargoWorkspace,
473 pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<CrateId>>,
474) {
475 let mut rustc_pkg_crates = FxHashMap::default();
476 // The root package of the rustc-dev component is rustc_driver, so we match that
477 let root_pkg =
478 rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver");
479 // The rustc workspace might be incomplete (such as if rustc-dev is not
480 // installed for the current toolchain) and `rustcSource` is set to discover.
481 if let Some(root_pkg) = root_pkg {
482 // Iterate through every crate in the dependency subtree of rustc_driver using BFS
483 let mut queue = VecDeque::new();
484 queue.push_back(root_pkg);
485 while let Some(pkg) = queue.pop_front() {
486 // Don't duplicate packages if they are dependended on a diamond pattern
487 // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates
488 // which is not ideal
489 if rustc_pkg_crates.contains_key(&pkg) {
490 continue;
491 }
492 for dep in &rustc_workspace[pkg].dependencies {
493 queue.push_back(dep.pkg);
494 }
452 for &tgt in rustc_workspace[pkg].targets.iter() { 495 for &tgt in rustc_workspace[pkg].targets.iter() {
453 if rustc_workspace[tgt].kind != TargetKind::Lib { 496 if rustc_workspace[tgt].kind != TargetKind::Lib {
454 continue; 497 continue;
455 } 498 }
456 // Exclude alloc / core / std
457 if rustc_workspace[tgt]
458 .root
459 .components()
460 .any(|c| c == Component::Normal("library".as_ref()))
461 {
462 continue;
463 }
464
465 if let Some(file_id) = load(&rustc_workspace[tgt].root) { 499 if let Some(file_id) = load(&rustc_workspace[tgt].root) {
466 let crate_id = add_target_crate_root( 500 let crate_id = add_target_crate_root(
467 &mut crate_graph, 501 crate_graph,
468 &rustc_workspace[pkg], 502 &rustc_workspace[pkg],
469 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)), 503 rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)),
470 &cfg_options, 504 &cfg_options,
@@ -472,44 +506,50 @@ fn cargo_to_crate_graph(
472 file_id, 506 file_id,
473 ); 507 );
474 pkg_to_lib_crate.insert(pkg, crate_id); 508 pkg_to_lib_crate.insert(pkg, crate_id);
475 // Add dependencies on the core / std / alloc for rustc 509 // Add dependencies on core / std / alloc for this crate
476 for (name, krate) in public_deps.iter() { 510 for (name, krate) in public_deps.iter() {
477 add_dep(&mut crate_graph, crate_id, name.clone(), *krate); 511 add_dep(crate_graph, crate_id, name.clone(), *krate);
478 } 512 }
479 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); 513 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
480 } 514 }
481 } 515 }
482 } 516 }
483 // Now add a dep edge from all targets of upstream to the lib 517 }
484 // target of downstream. 518 // Now add a dep edge from all targets of upstream to the lib
485 for pkg in rustc_workspace.packages() { 519 // target of downstream.
486 for dep in rustc_workspace[pkg].dependencies.iter() { 520 for pkg in rustc_pkg_crates.keys().copied() {
487 let name = CrateName::new(&dep.name).unwrap(); 521 for dep in rustc_workspace[pkg].dependencies.iter() {
488 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { 522 let name = CrateName::new(&dep.name).unwrap();
489 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { 523 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
490 add_dep(&mut crate_graph, from, name.clone(), to); 524 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
491 } 525 add_dep(crate_graph, from, name.clone(), to);
492 } 526 }
493 } 527 }
494 } 528 }
495 529 }
496 // Add dependencies for all the crates of the current workspace to rustc_private libraries 530 // Add a dependency on the rustc_private crates for all targets of each package
497 for dep in rustc_workspace.packages() { 531 // which opts in
498 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); 532 for dep in rustc_workspace.packages() {
499 533 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
500 if let Some(&to) = pkg_to_lib_crate.get(&dep) { 534
501 for pkg in cargo.packages() { 535 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
502 if !cargo[pkg].is_member { 536 for pkg in cargo.packages() {
503 continue; 537 let package = &cargo[pkg];
504 } 538 if !package.metadata.rustc_private {
505 for &from in pkg_crates.get(&pkg).into_iter().flatten() { 539 continue;
506 add_dep(&mut crate_graph, from, name.clone(), to); 540 }
541 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
542 // Avoid creating duplicate dependencies
543 // This avoids the situation where `from` depends on e.g. `arrayvec`, but
544 // `rust_analyzer` thinks that it should use the one from the `rustcSource`
545 // instead of the one from `crates.io`
546 if !crate_graph[from].dependencies.iter().any(|d| d.name == name) {
547 add_dep(crate_graph, from, name.clone(), to);
507 } 548 }
508 } 549 }
509 } 550 }
510 } 551 }
511 } 552 }
512 crate_graph
513} 553}
514 554
515fn add_target_crate_root( 555fn add_target_crate_root(
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 8789f0852..3130785cc 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -24,7 +24,7 @@ jod-thread = "0.1.0"
24log = "0.4.8" 24log = "0.4.8"
25lsp-types = { version = "0.88.0", features = ["proposed"] } 25lsp-types = { version = "0.88.0", features = ["proposed"] }
26parking_lot = "0.11.0" 26parking_lot = "0.11.0"
27xflags = "0.1.2" 27xflags = "0.2.1"
28oorandom = "11.1.2" 28oorandom = "11.1.2"
29rustc-hash = "1.1.0" 29rustc-hash = "1.1.0"
30serde = { version = "1.0.106", features = ["derive"] } 30serde = { version = "1.0.106", features = ["derive"] }
diff --git a/crates/rust-analyzer/src/bin/flags.rs b/crates/rust-analyzer/src/bin/flags.rs
index 244912d26..3a7caaf3f 100644
--- a/crates/rust-analyzer/src/bin/flags.rs
+++ b/crates/rust-analyzer/src/bin/flags.rs
@@ -6,7 +6,9 @@ use ide_ssr::{SsrPattern, SsrRule};
6use rust_analyzer::cli::{BenchWhat, Position, Verbosity}; 6use rust_analyzer::cli::{BenchWhat, Position, Verbosity};
7use vfs::AbsPathBuf; 7use vfs::AbsPathBuf;
8 8
9xflags::args_parser! { 9xflags::xflags! {
10 src "./src/bin/flags.rs"
11
10 /// LSP server for the Rust programming language. 12 /// LSP server for the Rust programming language.
11 cmd rust-analyzer { 13 cmd rust-analyzer {
12 /// Verbosity level, can be repeated multiple times. 14 /// Verbosity level, can be repeated multiple times.
@@ -120,7 +122,7 @@ xflags::args_parser! {
120 122
121// generated start 123// generated start
122// The following code is generated by `xflags` macro. 124// The following code is generated by `xflags` macro.
123// Run `env XFLAGS_DUMP= cargo build` to regenerate. 125// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate.
124#[derive(Debug)] 126#[derive(Debug)]
125pub struct RustAnalyzer { 127pub struct RustAnalyzer {
126 pub verbose: u32, 128 pub verbose: u32,
@@ -158,7 +160,7 @@ pub struct Parse {
158} 160}
159 161
160#[derive(Debug)] 162#[derive(Debug)]
161pub struct Symbols {} 163pub struct Symbols;
162 164
163#[derive(Debug)] 165#[derive(Debug)]
164pub struct Highlight { 166pub struct Highlight {
@@ -211,14 +213,13 @@ pub struct Search {
211} 213}
212 214
213#[derive(Debug)] 215#[derive(Debug)]
214pub struct ProcMacro {} 216pub struct ProcMacro;
215 217
216impl RustAnalyzer { 218impl RustAnalyzer {
217 pub const HELP: &'static str = Self::_HELP; 219 pub const HELP: &'static str = Self::HELP_;
218 220
219 pub fn from_env() -> xflags::Result<Self> { 221 pub fn from_env() -> xflags::Result<Self> {
220 let mut p = xflags::rt::Parser::new_from_env(); 222 Self::from_env_()
221 Self::_parse(&mut p)
222 } 223 }
223} 224}
224// generated end 225// generated end
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 3bd7e678d..49994824f 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -108,7 +108,11 @@ impl BenchCmd {
108 add_call_parenthesis: true, 108 add_call_parenthesis: true,
109 add_call_argument_snippets: true, 109 add_call_argument_snippets: true,
110 snippet_cap: SnippetCap::new(true), 110 snippet_cap: SnippetCap::new(true),
111 insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain }, 111 insert_use: InsertUseConfig {
112 merge: None,
113 prefix_kind: PrefixKind::Plain,
114 group: true,
115 },
112 }; 116 };
113 let res = do_work(&mut host, file_id, |analysis| { 117 let res = do_work(&mut host, file_id, |analysis| {
114 analysis.completions(&options, file_position) 118 analysis.completions(&options, file_position)
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 556fc2eeb..25df13554 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -16,7 +16,6 @@ use ide_db::helpers::{
16 insert_use::{InsertUseConfig, MergeBehavior}, 16 insert_use::{InsertUseConfig, MergeBehavior},
17 SnippetCap, 17 SnippetCap,
18}; 18};
19use itertools::Itertools;
20use lsp_types::{ClientCapabilities, MarkupKind}; 19use lsp_types::{ClientCapabilities, MarkupKind};
21use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; 20use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource};
22use rustc_hash::FxHashSet; 21use rustc_hash::FxHashSet;
@@ -35,7 +34,8 @@ config_data! {
35 assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"", 34 assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"",
36 /// The path structure for newly inserted paths to use. 35 /// The path structure for newly inserted paths to use.
37 assist_importPrefix: ImportPrefixDef = "\"plain\"", 36 assist_importPrefix: ImportPrefixDef = "\"plain\"",
38 37 /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
38 assist_importGroup: bool = "true",
39 /// Show function name and docs in parameter hints. 39 /// Show function name and docs in parameter hints.
40 callInfo_full: bool = "true", 40 callInfo_full: bool = "true",
41 41
@@ -46,9 +46,9 @@ config_data! {
46 cargo_allFeatures: bool = "false", 46 cargo_allFeatures: bool = "false",
47 /// List of features to activate. 47 /// List of features to activate.
48 cargo_features: Vec<String> = "[]", 48 cargo_features: Vec<String> = "[]",
49 /// Run `cargo check` on startup to get the correct value for package 49 /// Run build scripts (`build.rs`) for more precise code analysis.
50 /// OUT_DIRs. 50 cargo_runBuildScripts |
51 cargo_loadOutDirsFromCheck: bool = "false", 51 cargo_loadOutDirsFromCheck: bool = "true",
52 /// Do not activate the `default` feature. 52 /// Do not activate the `default` feature.
53 cargo_noDefaultFeatures: bool = "false", 53 cargo_noDefaultFeatures: bool = "false",
54 /// Compilation target (target triple). 54 /// Compilation target (target triple).
@@ -97,13 +97,15 @@ config_data! {
97 diagnostics_enableExperimental: bool = "true", 97 diagnostics_enableExperimental: bool = "true",
98 /// List of rust-analyzer diagnostics to disable. 98 /// List of rust-analyzer diagnostics to disable.
99 diagnostics_disabled: FxHashSet<String> = "[]", 99 diagnostics_disabled: FxHashSet<String> = "[]",
100 /// List of warnings that should be displayed with info severity.\n\nThe 100 /// List of warnings that should be displayed with info severity.
101 /// warnings will be indicated by a blue squiggly underline in code and 101 ///
102 /// a blue icon in the `Problems Panel`. 102 /// The warnings will be indicated by a blue squiggly underline in code
103 /// and a blue icon in the `Problems Panel`.
103 diagnostics_warningsAsHint: Vec<String> = "[]", 104 diagnostics_warningsAsHint: Vec<String> = "[]",
104 /// List of warnings that should be displayed with hint severity.\n\nThe 105 /// List of warnings that should be displayed with hint severity.
105 /// warnings will be indicated by faded text or three dots in code and 106 ///
106 /// will not show up in the `Problems Panel`. 107 /// The warnings will be indicated by faded text or three dots in code
108 /// and will not show up in the `Problems Panel`.
107 diagnostics_warningsAsInfo: Vec<String> = "[]", 109 diagnostics_warningsAsInfo: Vec<String> = "[]",
108 110
109 /// Controls file watching implementation. 111 /// Controls file watching implementation.
@@ -157,7 +159,9 @@ config_data! {
157 lens_references: bool = "false", 159 lens_references: bool = "false",
158 160
159 /// Disable project auto-discovery in favor of explicitly specified set 161 /// Disable project auto-discovery in favor of explicitly specified set
160 /// of projects.\n\nElements must be paths pointing to `Cargo.toml`, 162 /// of projects.
163 ///
164 /// Elements must be paths pointing to `Cargo.toml`,
161 /// `rust-project.json`, or JSON objects in `rust-project.json` format. 165 /// `rust-project.json`, or JSON objects in `rust-project.json` format.
162 linkedProjects: Vec<ManifestOrProjectJson> = "[]", 166 linkedProjects: Vec<ManifestOrProjectJson> = "[]",
163 167
@@ -167,8 +171,7 @@ config_data! {
167 /// Whether to show `can't find Cargo.toml` error message. 171 /// Whether to show `can't find Cargo.toml` error message.
168 notifications_cargoTomlNotFound: bool = "true", 172 notifications_cargoTomlNotFound: bool = "true",
169 173
170 /// Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be 174 /// Enable support for procedural macros, implies `#rust-analyzer.cargo.runBuildScripts#`.
171 /// enabled.
172 procMacro_enable: bool = "false", 175 procMacro_enable: bool = "false",
173 /// Internal config, path to proc-macro server executable (typically, 176 /// Internal config, path to proc-macro server executable (typically,
174 /// this is rust-analyzer itself, but we override this in tests). 177 /// this is rust-analyzer itself, but we override this in tests).
@@ -177,12 +180,17 @@ config_data! {
177 /// Command to be executed instead of 'cargo' for runnables. 180 /// Command to be executed instead of 'cargo' for runnables.
178 runnables_overrideCargo: Option<String> = "null", 181 runnables_overrideCargo: Option<String> = "null",
179 /// Additional arguments to be passed to cargo for runnables such as 182 /// Additional arguments to be passed to cargo for runnables such as
180 /// tests or binaries.\nFor example, it may be `--release`. 183 /// tests or binaries. For example, it may be `--release`.
181 runnables_cargoExtraArgs: Vec<String> = "[]", 184 runnables_cargoExtraArgs: Vec<String> = "[]",
182 185
183 /// Path to the rust compiler sources, for usage in rustc_private projects, or "discover" 186 /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
184 /// to try to automatically find it. 187 /// projects, or "discover" to try to automatically find it.
185 rustcSource : Option<String> = "null", 188 ///
189 /// Any project which uses rust-analyzer with the rustcPrivate
190 /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
191 ///
192 /// This option is not reloaded automatically; you must restart rust-analyzer for it to take effect.
193 rustcSource: Option<String> = "null",
186 194
187 /// Additional arguments to `rustfmt`. 195 /// Additional arguments to `rustfmt`.
188 rustfmt_extraArgs: Vec<String> = "[]", 196 rustfmt_extraArgs: Vec<String> = "[]",
@@ -480,8 +488,8 @@ impl Config {
480 pub fn cargo_autoreload(&self) -> bool { 488 pub fn cargo_autoreload(&self) -> bool {
481 self.data.cargo_autoreload 489 self.data.cargo_autoreload
482 } 490 }
483 pub fn load_out_dirs_from_check(&self) -> bool { 491 pub fn run_build_scripts(&self) -> bool {
484 self.data.cargo_loadOutDirsFromCheck 492 self.data.cargo_runBuildScripts || self.data.procMacro_enable
485 } 493 }
486 pub fn cargo(&self) -> CargoConfig { 494 pub fn cargo(&self) -> CargoConfig {
487 let rustc_source = self.data.rustcSource.as_ref().map(|rustc_src| { 495 let rustc_source = self.data.rustcSource.as_ref().map(|rustc_src| {
@@ -575,6 +583,7 @@ impl Config {
575 ImportPrefixDef::ByCrate => PrefixKind::ByCrate, 583 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
576 ImportPrefixDef::BySelf => PrefixKind::BySelf, 584 ImportPrefixDef::BySelf => PrefixKind::BySelf,
577 }, 585 },
586 group: self.data.assist_importGroup,
578 } 587 }
579 } 588 }
580 pub fn completion(&self) -> CompletionConfig { 589 pub fn completion(&self) -> CompletionConfig {
@@ -759,7 +768,8 @@ fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json:
759} 768}
760 769
761fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value { 770fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value {
762 let doc = doc.iter().map(|it| it.trim()).join(" "); 771 let doc = doc_comment_to_string(doc);
772 let doc = doc.trim_end_matches('\n');
763 assert!( 773 assert!(
764 doc.ends_with('.') && doc.starts_with(char::is_uppercase), 774 doc.ends_with('.') && doc.starts_with(char::is_uppercase),
765 "bad docs for {}: {:?}", 775 "bad docs for {}: {:?}",
@@ -848,21 +858,26 @@ fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String {
848 .iter() 858 .iter()
849 .map(|(field, _ty, doc, default)| { 859 .map(|(field, _ty, doc, default)| {
850 let name = format!("rust-analyzer.{}", field.replace("_", ".")); 860 let name = format!("rust-analyzer.{}", field.replace("_", "."));
851 format!("[[{}]]{} (default: `{}`)::\n{}\n", name, name, default, doc.join(" ")) 861 let doc = doc_comment_to_string(*doc);
862 format!("[[{}]]{} (default: `{}`)::\n+\n--\n{}--\n", name, name, default, doc)
852 }) 863 })
853 .collect::<String>() 864 .collect::<String>()
854} 865}
855 866
867fn doc_comment_to_string(doc: &[&str]) -> String {
868 doc.iter().map(|it| it.strip_prefix(' ').unwrap_or(it)).map(|it| format!("{}\n", it)).collect()
869}
870
856#[cfg(test)] 871#[cfg(test)]
857mod tests { 872mod tests {
858 use std::fs; 873 use std::fs;
859 874
860 use test_utils::project_dir; 875 use test_utils::{ensure_file_contents, project_root};
861 876
862 use super::*; 877 use super::*;
863 878
864 #[test] 879 #[test]
865 fn schema_in_sync_with_package_json() { 880 fn generate_package_json_config() {
866 let s = Config::json_schema(); 881 let s = Config::json_schema();
867 let schema = format!("{:#}", s); 882 let schema = format!("{:#}", s);
868 let mut schema = schema 883 let mut schema = schema
@@ -875,7 +890,7 @@ mod tests {
875 .to_string(); 890 .to_string();
876 schema.push_str(",\n"); 891 schema.push_str(",\n");
877 892
878 let package_json_path = project_dir().join("editors/code/package.json"); 893 let package_json_path = project_root().join("editors/code/package.json");
879 let mut package_json = fs::read_to_string(&package_json_path).unwrap(); 894 let mut package_json = fs::read_to_string(&package_json_path).unwrap();
880 895
881 let start_marker = " \"$generated-start\": false,\n"; 896 let start_marker = " \"$generated-start\": false,\n";
@@ -883,26 +898,20 @@ mod tests {
883 898
884 let start = package_json.find(start_marker).unwrap() + start_marker.len(); 899 let start = package_json.find(start_marker).unwrap() + start_marker.len();
885 let end = package_json.find(end_marker).unwrap(); 900 let end = package_json.find(end_marker).unwrap();
901
886 let p = remove_ws(&package_json[start..end]); 902 let p = remove_ws(&package_json[start..end]);
887 let s = remove_ws(&schema); 903 let s = remove_ws(&schema);
888
889 if !p.contains(&s) { 904 if !p.contains(&s) {
890 package_json.replace_range(start..end, &schema); 905 package_json.replace_range(start..end, &schema);
891 fs::write(&package_json_path, &mut package_json).unwrap(); 906 ensure_file_contents(&package_json_path, &package_json)
892 panic!("new config, updating package.json")
893 } 907 }
894 } 908 }
895 909
896 #[test] 910 #[test]
897 fn schema_in_sync_with_docs() { 911 fn generate_config_documentation() {
898 let docs_path = project_dir().join("docs/user/generated_config.adoc"); 912 let docs_path = project_root().join("docs/user/generated_config.adoc");
899 let current = fs::read_to_string(&docs_path).unwrap();
900 let expected = ConfigData::manual(); 913 let expected = ConfigData::manual();
901 914 ensure_file_contents(&docs_path, &expected);
902 if remove_ws(&current) != remove_ws(&expected) {
903 fs::write(&docs_path, expected).unwrap();
904 panic!("updated config manual");
905 }
906 } 915 }
907 916
908 fn remove_ws(text: &str) -> String { 917 fn remove_ws(text: &str) -> String {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 4f6f250d6..b5b2ffe50 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -697,7 +697,6 @@ pub(crate) fn handle_completion_resolve(
697 FilePosition { file_id, offset }, 697 FilePosition { file_id, offset },
698 &resolve_data.full_import_path, 698 &resolve_data.full_import_path,
699 resolve_data.imported_name, 699 resolve_data.imported_name,
700 resolve_data.import_for_trait_assoc_item,
701 )? 700 )?
702 .into_iter() 701 .into_iter()
703 .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel))) 702 .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
@@ -1135,20 +1134,13 @@ pub(crate) fn handle_document_highlight(
1135 None 1134 None
1136 }; 1135 };
1137 1136
1138 let res = refs 1137 let file_refs = refs.references.get(&position.file_id).map_or(&[][..], Vec::as_slice);
1139 .references 1138 let mut res = Vec::with_capacity(file_refs.len() + 1);
1140 .get(&position.file_id) 1139 res.extend(decl);
1141 .map(|file_refs| { 1140 res.extend(file_refs.iter().map(|&(range, access)| DocumentHighlight {
1142 file_refs 1141 range: to_proto::range(&line_index, range),
1143 .into_iter() 1142 kind: access.map(to_proto::document_highlight_kind),
1144 .map(|&(range, access)| DocumentHighlight { 1143 }));
1145 range: to_proto::range(&line_index, range),
1146 kind: access.map(to_proto::document_highlight_kind),
1147 })
1148 .chain(decl)
1149 .collect()
1150 })
1151 .unwrap_or_default();
1152 Ok(Some(res)) 1144 Ok(Some(res))
1153} 1145}
1154 1146
@@ -1525,7 +1517,6 @@ struct CompletionResolveData {
1525 position: lsp_types::TextDocumentPositionParams, 1517 position: lsp_types::TextDocumentPositionParams,
1526 full_import_path: String, 1518 full_import_path: String,
1527 imported_name: String, 1519 imported_name: String,
1528 import_for_trait_assoc_item: bool,
1529} 1520}
1530 1521
1531fn fill_resolve_data( 1522fn fill_resolve_data(
@@ -1534,15 +1525,13 @@ fn fill_resolve_data(
1534 position: &TextDocumentPositionParams, 1525 position: &TextDocumentPositionParams,
1535) -> Option<()> { 1526) -> Option<()> {
1536 let import_edit = item.import_to_add()?; 1527 let import_edit = item.import_to_add()?;
1537 let full_import_path = import_edit.import_path.to_string(); 1528 let import_path = &import_edit.import.import_path;
1538 let imported_name = import_edit.import_path.segments().last()?.to_string();
1539 1529
1540 *resolve_data = Some( 1530 *resolve_data = Some(
1541 to_value(CompletionResolveData { 1531 to_value(CompletionResolveData {
1542 position: position.to_owned(), 1532 position: position.to_owned(),
1543 full_import_path, 1533 full_import_path: import_path.to_string(),
1544 imported_name, 1534 imported_name: import_path.segments().last()?.to_string(),
1545 import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item,
1546 }) 1535 })
1547 .unwrap(), 1536 .unwrap(),
1548 ); 1537 );
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 2829d5970..f0cb309e4 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -312,7 +312,7 @@ impl GlobalState {
312 } else { 312 } else {
313 assert_eq!(n_done, n_total); 313 assert_eq!(n_done, n_total);
314 new_status = Status::Ready { 314 new_status = Status::Ready {
315 partial: self.config.load_out_dirs_from_check() 315 partial: self.config.run_build_scripts()
316 && self.workspace_build_data.is_none() 316 && self.workspace_build_data.is_none()
317 || config_version < self.vfs_config_version, 317 || config_version < self.vfs_config_version,
318 }; 318 };
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index c07efa330..aa8504c3d 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -337,7 +337,7 @@ impl GlobalState {
337 }; 337 };
338 change.set_crate_graph(crate_graph); 338 change.set_crate_graph(crate_graph);
339 339
340 if self.config.load_out_dirs_from_check() && workspace_build_data.is_none() { 340 if self.config.run_build_scripts() && workspace_build_data.is_none() {
341 let mut collector = BuildDataCollector::default(); 341 let mut collector = BuildDataCollector::default();
342 for ws in &workspaces { 342 for ws in &workspaces {
343 ws.collect_build_data_configs(&mut collector); 343 ws.collect_build_data_configs(&mut collector);
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index c1ca88df6..a9846fa70 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -173,20 +173,14 @@ pub(crate) fn snippet_text_edit_vec(
173 173
174pub(crate) fn completion_item( 174pub(crate) fn completion_item(
175 line_index: &LineIndex, 175 line_index: &LineIndex,
176 completion_item: CompletionItem, 176 item: CompletionItem,
177) -> Vec<lsp_types::CompletionItem> { 177) -> Vec<lsp_types::CompletionItem> {
178 fn set_score(res: &mut lsp_types::CompletionItem, label: &str) {
179 res.preselect = Some(true);
180 // HACK: sort preselect items first
181 res.sort_text = Some(format!(" {}", label));
182 }
183
184 let mut additional_text_edits = Vec::new(); 178 let mut additional_text_edits = Vec::new();
185 let mut text_edit = None; 179 let mut text_edit = None;
186 // LSP does not allow arbitrary edits in completion, so we have to do a 180 // LSP does not allow arbitrary edits in completion, so we have to do a
187 // non-trivial mapping here. 181 // non-trivial mapping here.
188 let source_range = completion_item.source_range(); 182 let source_range = item.source_range();
189 for indel in completion_item.text_edit().iter() { 183 for indel in item.text_edit().iter() {
190 if indel.delete.contains_range(source_range) { 184 if indel.delete.contains_range(source_range) {
191 text_edit = Some(if indel.delete == source_range { 185 text_edit = Some(if indel.delete == source_range {
192 self::text_edit(line_index, indel.clone()) 186 self::text_edit(line_index, indel.clone())
@@ -207,46 +201,52 @@ pub(crate) fn completion_item(
207 } 201 }
208 let text_edit = text_edit.unwrap(); 202 let text_edit = text_edit.unwrap();
209 203
210 let mut res = lsp_types::CompletionItem { 204 let mut lsp_item = lsp_types::CompletionItem {
211 label: completion_item.label().to_string(), 205 label: item.label().to_string(),
212 detail: completion_item.detail().map(|it| it.to_string()), 206 detail: item.detail().map(|it| it.to_string()),
213 filter_text: Some(completion_item.lookup().to_string()), 207 filter_text: Some(item.lookup().to_string()),
214 kind: completion_item.kind().map(completion_item_kind), 208 kind: item.kind().map(completion_item_kind),
215 text_edit: Some(text_edit.into()), 209 text_edit: Some(text_edit.into()),
216 additional_text_edits: Some(additional_text_edits), 210 additional_text_edits: Some(additional_text_edits),
217 documentation: completion_item.documentation().map(documentation), 211 documentation: item.documentation().map(documentation),
218 deprecated: Some(completion_item.deprecated()), 212 deprecated: Some(item.deprecated()),
219 ..Default::default() 213 ..Default::default()
220 }; 214 };
221 215
222 if completion_item.score().is_some() { 216 if item.relevance().is_relevant() {
223 set_score(&mut res, completion_item.label()); 217 lsp_item.preselect = Some(true);
218 // HACK: sort preselect items first
219 lsp_item.sort_text = Some(format!(" {}", item.label()));
224 } 220 }
225 221
226 if completion_item.deprecated() { 222 if item.deprecated() {
227 res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) 223 lsp_item.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated])
228 } 224 }
229 225
230 if completion_item.trigger_call_info() { 226 if item.trigger_call_info() {
231 res.command = Some(command::trigger_parameter_hints()); 227 lsp_item.command = Some(command::trigger_parameter_hints());
232 } 228 }
233 229
234 let mut all_results = match completion_item.ref_match() { 230 let mut res = match item.ref_match() {
235 Some(ref_match) => { 231 Some(mutability) => {
236 let mut refed = res.clone(); 232 let mut lsp_item_with_ref = lsp_item.clone();
237 let (mutability, _score) = ref_match; 233 lsp_item.preselect = Some(true);
238 let label = format!("&{}{}", mutability.as_keyword_for_ref(), refed.label); 234 lsp_item.sort_text = Some(format!(" {}", item.label()));
239 set_score(&mut refed, &label); 235 lsp_item_with_ref.label =
240 refed.label = label; 236 format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label);
241 vec![res, refed] 237 if let Some(lsp_types::CompletionTextEdit::Edit(it)) = &mut lsp_item_with_ref.text_edit
238 {
239 it.new_text = format!("&{}{}", mutability.as_keyword_for_ref(), it.new_text);
240 }
241 vec![lsp_item_with_ref, lsp_item]
242 } 242 }
243 None => vec![res], 243 None => vec![lsp_item],
244 }; 244 };
245 245
246 for mut r in all_results.iter_mut() { 246 for lsp_item in res.iter_mut() {
247 r.insert_text_format = Some(insert_text_format(completion_item.insert_text_format())); 247 lsp_item.insert_text_format = Some(insert_text_format(item.insert_text_format()));
248 } 248 }
249 all_results 249 res
250} 250}
251 251
252pub(crate) fn signature_help( 252pub(crate) fn signature_help(
@@ -1087,7 +1087,11 @@ mod tests {
1087 add_call_parenthesis: true, 1087 add_call_parenthesis: true,
1088 add_call_argument_snippets: true, 1088 add_call_argument_snippets: true,
1089 snippet_cap: SnippetCap::new(true), 1089 snippet_cap: SnippetCap::new(true),
1090 insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain }, 1090 insert_use: InsertUseConfig {
1091 merge: None,
1092 prefix_kind: PrefixKind::Plain,
1093 group: true,
1094 },
1091 }, 1095 },
1092 ide_db::base_db::FilePosition { file_id, offset }, 1096 ide_db::base_db::FilePosition { file_id, offset },
1093 ) 1097 )
@@ -1101,13 +1105,15 @@ mod tests {
1101 expect_test::expect![[r#" 1105 expect_test::expect![[r#"
1102 [ 1106 [
1103 ( 1107 (
1104 "arg", 1108 "&arg",
1105 None, 1109 Some(
1110 " arg",
1111 ),
1106 ), 1112 ),
1107 ( 1113 (
1108 "&arg", 1114 "arg",
1109 Some( 1115 Some(
1110 " &arg", 1116 " arg",
1111 ), 1117 ),
1112 ), 1118 ),
1113 ] 1119 ]
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
index 6b774073d..cd0c91481 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/support.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs
@@ -13,7 +13,7 @@ use project_model::ProjectManifest;
13use rust_analyzer::{config::Config, lsp_ext, main_loop}; 13use rust_analyzer::{config::Config, lsp_ext, main_loop};
14use serde::Serialize; 14use serde::Serialize;
15use serde_json::{json, to_string_pretty, Value}; 15use serde_json::{json, to_string_pretty, Value};
16use test_utils::{find_mismatch, Fixture}; 16use test_utils::Fixture;
17use vfs::AbsPathBuf; 17use vfs::AbsPathBuf;
18 18
19use crate::testdir::TestDir; 19use crate::testdir::TestDir;
@@ -279,3 +279,98 @@ fn recv_timeout(receiver: &Receiver<Message>) -> Result<Option<Message>, Timeout
279 recv(after(timeout)) -> _ => Err(Timeout), 279 recv(after(timeout)) -> _ => Err(Timeout),
280 } 280 }
281} 281}
282
283// Comparison functionality borrowed from cargo:
284
285/// Compares JSON object for approximate equality.
286/// You can use `[..]` wildcard in strings (useful for OS dependent things such
287/// as paths). You can use a `"{...}"` string literal as a wildcard for
288/// arbitrary nested JSON. Arrays are sorted before comparison.
289fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> {
290 match (expected, actual) {
291 (Value::Number(l), Value::Number(r)) if l == r => None,
292 (Value::Bool(l), Value::Bool(r)) if l == r => None,
293 (Value::String(l), Value::String(r)) if lines_match(l, r) => None,
294 (Value::Array(l), Value::Array(r)) => {
295 if l.len() != r.len() {
296 return Some((expected, actual));
297 }
298
299 let mut l = l.iter().collect::<Vec<_>>();
300 let mut r = r.iter().collect::<Vec<_>>();
301
302 l.retain(|l| match r.iter().position(|r| find_mismatch(l, r).is_none()) {
303 Some(i) => {
304 r.remove(i);
305 false
306 }
307 None => true,
308 });
309
310 if !l.is_empty() {
311 assert!(!r.is_empty());
312 Some((&l[0], &r[0]))
313 } else {
314 assert_eq!(r.len(), 0);
315 None
316 }
317 }
318 (Value::Object(l), Value::Object(r)) => {
319 fn sorted_values(obj: &serde_json::Map<String, Value>) -> Vec<&Value> {
320 let mut entries = obj.iter().collect::<Vec<_>>();
321 entries.sort_by_key(|it| it.0);
322 entries.into_iter().map(|(_k, v)| v).collect::<Vec<_>>()
323 }
324
325 let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k));
326 if !same_keys {
327 return Some((expected, actual));
328 }
329
330 let l = sorted_values(l);
331 let r = sorted_values(r);
332
333 l.into_iter().zip(r).filter_map(|(l, r)| find_mismatch(l, r)).next()
334 }
335 (Value::Null, Value::Null) => None,
336 // magic string literal "{...}" acts as wildcard for any sub-JSON
337 (Value::String(l), _) if l == "{...}" => None,
338 _ => Some((expected, actual)),
339 }
340}
341
342/// Compare a line with an expected pattern.
343/// - Use `[..]` as a wildcard to match 0 or more characters on the same line
344/// (similar to `.*` in a regex).
345fn lines_match(expected: &str, actual: &str) -> bool {
346 // Let's not deal with / vs \ (windows...)
347 // First replace backslash-escaped backslashes with forward slashes
348 // which can occur in, for example, JSON output
349 let expected = expected.replace(r"\\", "/").replace(r"\", "/");
350 let mut actual: &str = &actual.replace(r"\\", "/").replace(r"\", "/");
351 for (i, part) in expected.split("[..]").enumerate() {
352 match actual.find(part) {
353 Some(j) => {
354 if i == 0 && j != 0 {
355 return false;
356 }
357 actual = &actual[j + part.len()..];
358 }
359 None => return false,
360 }
361 }
362 actual.is_empty() || expected.ends_with("[..]")
363}
364
365#[test]
366fn lines_match_works() {
367 assert!(lines_match("a b", "a b"));
368 assert!(lines_match("a[..]b", "a b"));
369 assert!(lines_match("a[..]", "a b"));
370 assert!(lines_match("[..]", "a b"));
371 assert!(lines_match("[..]b", "a b"));
372
373 assert!(!lines_match("[..]b", "c"));
374 assert!(!lines_match("b", "c"));
375 assert!(!lines_match("b", "cb"));
376}
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 9ee3a8586..33bde099b 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -11,6 +11,7 @@ edition = "2018"
11doctest = false 11doctest = false
12 12
13[dependencies] 13[dependencies]
14cov-mark = "1.1"
14itertools = "0.10.0" 15itertools = "0.10.0"
15rowan = "0.12.2" 16rowan = "0.12.2"
16rustc_lexer = { version = "709.0.0", package = "rustc-ap-rustc_lexer" } 17rustc_lexer = { version = "709.0.0", package = "rustc-ap-rustc_lexer" }
@@ -24,10 +25,10 @@ serde = { version = "1.0.106", features = ["derive"] }
24stdx = { path = "../stdx", version = "0.0.0" } 25stdx = { path = "../stdx", version = "0.0.0" }
25text_edit = { path = "../text_edit", version = "0.0.0" } 26text_edit = { path = "../text_edit", version = "0.0.0" }
26parser = { path = "../parser", version = "0.0.0" } 27parser = { path = "../parser", version = "0.0.0" }
27test_utils = { path = "../test_utils", version = "0.0.0" }
28profile = { path = "../profile", version = "0.0.0" } 28profile = { path = "../profile", version = "0.0.0" }
29 29
30[dev-dependencies] 30[dev-dependencies]
31test_utils = { path = "../test_utils" }
31walkdir = "2.3.1" 32walkdir = "2.3.1"
32rayon = "1" 33rayon = "1"
33expect-test = "1.1" 34expect-test = "1.1"
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 2ff92f9f6..b13252eec 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -10,7 +10,6 @@ use std::{
10use indexmap::IndexMap; 10use indexmap::IndexMap;
11use itertools::Itertools; 11use itertools::Itertools;
12use rustc_hash::FxHashMap; 12use rustc_hash::FxHashMap;
13use test_utils::mark;
14use text_edit::TextEditBuilder; 13use text_edit::TextEditBuilder;
15 14
16use crate::{ 15use crate::{
@@ -184,7 +183,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
184 let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) { 183 let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) {
185 Some((lhs, rhs)) => (lhs, rhs), 184 Some((lhs, rhs)) => (lhs, rhs),
186 _ => { 185 _ => {
187 mark::hit!(diff_node_token_replace); 186 cov_mark::hit!(diff_node_token_replace);
188 diff.replacements.insert(lhs, rhs); 187 diff.replacements.insert(lhs, rhs);
189 return; 188 return;
190 } 189 }
@@ -202,19 +201,19 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
202 (None, Some(element)) => { 201 (None, Some(element)) => {
203 let insert_pos = match last_lhs.clone() { 202 let insert_pos = match last_lhs.clone() {
204 Some(prev) => { 203 Some(prev) => {
205 mark::hit!(diff_insert); 204 cov_mark::hit!(diff_insert);
206 TreeDiffInsertPos::After(prev) 205 TreeDiffInsertPos::After(prev)
207 } 206 }
208 // first iteration, insert into out parent as the first child 207 // first iteration, insert into out parent as the first child
209 None => { 208 None => {
210 mark::hit!(diff_insert_as_first_child); 209 cov_mark::hit!(diff_insert_as_first_child);
211 TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) 210 TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
212 } 211 }
213 }; 212 };
214 diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element); 213 diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element);
215 } 214 }
216 (Some(element), None) => { 215 (Some(element), None) => {
217 mark::hit!(diff_delete); 216 cov_mark::hit!(diff_delete);
218 diff.deletions.push(element); 217 diff.deletions.push(element);
219 } 218 }
220 (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {} 219 (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {}
@@ -228,7 +227,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
228 let mut insert = false; 227 let mut insert = false;
229 while let Some(rhs_child) = rhs_children_clone.next() { 228 while let Some(rhs_child) = rhs_children_clone.next() {
230 if syntax_element_eq(&lhs_ele, &rhs_child) { 229 if syntax_element_eq(&lhs_ele, &rhs_child) {
231 mark::hit!(diff_insertions); 230 cov_mark::hit!(diff_insertions);
232 insert = true; 231 insert = true;
233 break; 232 break;
234 } else { 233 } else {
@@ -240,7 +239,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
240 let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) { 239 let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) {
241 TreeDiffInsertPos::After(prev) 240 TreeDiffInsertPos::After(prev)
242 } else { 241 } else {
243 mark::hit!(insert_first_child); 242 cov_mark::hit!(insert_first_child);
244 TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) 243 TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
245 }; 244 };
246 245
@@ -635,14 +634,13 @@ mod tests {
635 use expect_test::{expect, Expect}; 634 use expect_test::{expect, Expect};
636 use itertools::Itertools; 635 use itertools::Itertools;
637 use parser::SyntaxKind; 636 use parser::SyntaxKind;
638 use test_utils::mark;
639 use text_edit::TextEdit; 637 use text_edit::TextEdit;
640 638
641 use crate::{AstNode, SyntaxElement}; 639 use crate::{AstNode, SyntaxElement};
642 640
643 #[test] 641 #[test]
644 fn replace_node_token() { 642 fn replace_node_token() {
645 mark::check!(diff_node_token_replace); 643 cov_mark::check!(diff_node_token_replace);
646 check_diff( 644 check_diff(
647 r#"use node;"#, 645 r#"use node;"#,
648 r#"ident"#, 646 r#"ident"#,
@@ -666,7 +664,7 @@ mod tests {
666 664
667 #[test] 665 #[test]
668 fn replace_parent() { 666 fn replace_parent() {
669 mark::check!(diff_insert_as_first_child); 667 cov_mark::check!(diff_insert_as_first_child);
670 check_diff( 668 check_diff(
671 r#""#, 669 r#""#,
672 r#"use foo::bar;"#, 670 r#"use foo::bar;"#,
@@ -689,7 +687,7 @@ mod tests {
689 687
690 #[test] 688 #[test]
691 fn insert_last() { 689 fn insert_last() {
692 mark::check!(diff_insert); 690 cov_mark::check!(diff_insert);
693 check_diff( 691 check_diff(
694 r#" 692 r#"
695use foo; 693use foo;
@@ -774,7 +772,7 @@ use baz;"#,
774 772
775 #[test] 773 #[test]
776 fn first_child_insertion() { 774 fn first_child_insertion() {
777 mark::check!(insert_first_child); 775 cov_mark::check!(insert_first_child);
778 check_diff( 776 check_diff(
779 r#"fn main() { 777 r#"fn main() {
780 stdi 778 stdi
@@ -804,7 +802,7 @@ use baz;"#,
804 802
805 #[test] 803 #[test]
806 fn delete_last() { 804 fn delete_last() {
807 mark::check!(diff_delete); 805 cov_mark::check!(diff_delete);
808 check_diff( 806 check_diff(
809 r#"use foo; 807 r#"use foo;
810 use bar;"#, 808 use bar;"#,
@@ -828,7 +826,7 @@ use baz;"#,
828 826
829 #[test] 827 #[test]
830 fn delete_middle() { 828 fn delete_middle() {
831 mark::check!(diff_insertions); 829 cov_mark::check!(diff_insertions);
832 check_diff( 830 check_diff(
833 r#" 831 r#"
834use expect_test::{expect, Expect}; 832use expect_test::{expect, Expect};
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index b6c5de658..70ba8adb4 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -91,6 +91,10 @@ pub fn path_from_segments(
91 }) 91 })
92} 92}
93 93
94pub fn path_from_text(text: &str) -> ast::Path {
95 ast_from_text(&format!("fn main() {{ let test = {}; }}", text))
96}
97
94pub fn glob_use_tree() -> ast::UseTree { 98pub fn glob_use_tree() -> ast::UseTree {
95 ast_from_text("use *;") 99 ast_from_text("use *;")
96} 100}
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index 11294c5b2..09e212e8c 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -37,6 +37,7 @@ pub mod algo;
37pub mod ast; 37pub mod ast;
38#[doc(hidden)] 38#[doc(hidden)]
39pub mod fuzz; 39pub mod fuzz;
40pub mod utils;
40 41
41use std::{marker::PhantomData, sync::Arc}; 42use std::{marker::PhantomData, sync::Arc};
42 43
diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs
index b2c06e24f..ba0ccfaed 100644
--- a/crates/syntax/src/tests.rs
+++ b/crates/syntax/src/tests.rs
@@ -7,7 +7,7 @@ use std::{
7use ast::NameOwner; 7use ast::NameOwner;
8use expect_test::expect_file; 8use expect_test::expect_file;
9use rayon::prelude::*; 9use rayon::prelude::*;
10use test_utils::{bench, bench_fixture, project_dir, skip_slow_tests}; 10use test_utils::{bench, bench_fixture, project_root, skip_slow_tests};
11 11
12use crate::{ast, fuzz, tokenize, AstNode, SourceFile, SyntaxError, TextRange, TextSize, Token}; 12use crate::{ast, fuzz, tokenize, AstNode, SourceFile, SyntaxError, TextRange, TextSize, Token};
13 13
@@ -153,7 +153,7 @@ fn reparse_fuzz_tests() {
153/// Test that Rust-analyzer can parse and validate the rust-analyzer 153/// Test that Rust-analyzer can parse and validate the rust-analyzer
154#[test] 154#[test]
155fn self_hosting_parsing() { 155fn self_hosting_parsing() {
156 let dir = project_dir().join("crates"); 156 let dir = project_root().join("crates");
157 let files = walkdir::WalkDir::new(dir) 157 let files = walkdir::WalkDir::new(dir)
158 .into_iter() 158 .into_iter()
159 .filter_entry(|entry| { 159 .filter_entry(|entry| {
@@ -193,7 +193,7 @@ fn self_hosting_parsing() {
193} 193}
194 194
195fn test_data_dir() -> PathBuf { 195fn test_data_dir() -> PathBuf {
196 project_dir().join("crates/syntax/test_data") 196 project_root().join("crates/syntax/test_data")
197} 197}
198 198
199fn assert_errors_are_present(errors: &[SyntaxError], path: &Path) { 199fn assert_errors_are_present(errors: &[SyntaxError], path: &Path) {
diff --git a/crates/syntax/src/utils.rs b/crates/syntax/src/utils.rs
new file mode 100644
index 000000000..f4c02518b
--- /dev/null
+++ b/crates/syntax/src/utils.rs
@@ -0,0 +1,43 @@
1//! A set of utils methods to reuse on other abstraction levels
2
3use itertools::Itertools;
4
5use crate::{ast, match_ast, AstNode};
6
7pub fn path_to_string_stripping_turbo_fish(path: &ast::Path) -> String {
8 path.syntax()
9 .children()
10 .filter_map(|node| {
11 match_ast! {
12 match node {
13 ast::PathSegment(it) => {
14 Some(it.name_ref()?.to_string())
15 },
16 ast::Path(it) => {
17 Some(path_to_string_stripping_turbo_fish(&it))
18 },
19 _ => None,
20 }
21 }
22 })
23 .join("::")
24}
25
26#[cfg(test)]
27mod tests {
28 use super::path_to_string_stripping_turbo_fish;
29 use crate::ast::make;
30
31 #[test]
32 fn turbofishes_are_stripped() {
33 assert_eq!("Vec", path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>")),);
34 assert_eq!(
35 "Vec::new",
36 path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>::new")),
37 );
38 assert_eq!(
39 "Vec::new",
40 path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::new()")),
41 );
42 }
43}
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml
index 2a65000b8..87bab7a08 100644
--- a/crates/test_utils/Cargo.toml
+++ b/crates/test_utils/Cargo.toml
@@ -13,7 +13,6 @@ doctest = false
13# Avoid adding deps here, this crate is widely used in tests it should compile fast! 13# Avoid adding deps here, this crate is widely used in tests it should compile fast!
14dissimilar = "1.0.2" 14dissimilar = "1.0.2"
15text-size = "1.0.0" 15text-size = "1.0.0"
16serde_json = "1.0.48"
17rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
18 17
19stdx = { path = "../stdx", version = "0.0.0" } 18stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/crates/test_utils/src/bench_fixture.rs b/crates/test_utils/src/bench_fixture.rs
index d775e2cc9..3a37c4473 100644
--- a/crates/test_utils/src/bench_fixture.rs
+++ b/crates/test_utils/src/bench_fixture.rs
@@ -4,7 +4,7 @@ use std::fs;
4 4
5use stdx::format_to; 5use stdx::format_to;
6 6
7use crate::project_dir; 7use crate::project_root;
8 8
9pub fn big_struct() -> String { 9pub fn big_struct() -> String {
10 let n = 1_000; 10 let n = 1_000;
@@ -32,11 +32,11 @@ struct S{} {{
32} 32}
33 33
34pub fn glorious_old_parser() -> String { 34pub fn glorious_old_parser() -> String {
35 let path = project_dir().join("bench_data/glorious_old_parser"); 35 let path = project_root().join("bench_data/glorious_old_parser");
36 fs::read_to_string(&path).unwrap() 36 fs::read_to_string(&path).unwrap()
37} 37}
38 38
39pub fn numerous_macro_rules() -> String { 39pub fn numerous_macro_rules() -> String {
40 let path = project_dir().join("bench_data/numerous_macro_rules"); 40 let path = project_root().join("bench_data/numerous_macro_rules");
41 fs::read_to_string(&path).unwrap() 41 fs::read_to_string(&path).unwrap()
42} 42}
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 5be4a64fc..c5f859790 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -6,20 +6,17 @@
6//! * Extracting markup (mainly, `$0` markers) out of fixture strings. 6//! * Extracting markup (mainly, `$0` markers) out of fixture strings.
7//! * marks (see the eponymous module). 7//! * marks (see the eponymous module).
8 8
9#[macro_use]
10pub mod mark;
11pub mod bench_fixture; 9pub mod bench_fixture;
12mod fixture; 10mod fixture;
13 11
14use std::{ 12use std::{
15 convert::{TryFrom, TryInto}, 13 convert::{TryFrom, TryInto},
16 env, fs, 14 env, fs,
17 path::PathBuf, 15 path::{Path, PathBuf},
18}; 16};
19 17
20use profile::StopWatch; 18use profile::StopWatch;
21use serde_json::Value; 19use stdx::{is_ci, lines_with_ends};
22use stdx::lines_with_ends;
23use text_size::{TextRange, TextSize}; 20use text_size::{TextRange, TextSize};
24 21
25pub use dissimilar::diff as __diff; 22pub use dissimilar::diff as __diff;
@@ -281,101 +278,6 @@ fn main() {
281 ); 278 );
282} 279}
283 280
284// Comparison functionality borrowed from cargo:
285
286/// Compare a line with an expected pattern.
287/// - Use `[..]` as a wildcard to match 0 or more characters on the same line
288/// (similar to `.*` in a regex).
289pub fn lines_match(expected: &str, actual: &str) -> bool {
290 // Let's not deal with / vs \ (windows...)
291 // First replace backslash-escaped backslashes with forward slashes
292 // which can occur in, for example, JSON output
293 let expected = expected.replace(r"\\", "/").replace(r"\", "/");
294 let mut actual: &str = &actual.replace(r"\\", "/").replace(r"\", "/");
295 for (i, part) in expected.split("[..]").enumerate() {
296 match actual.find(part) {
297 Some(j) => {
298 if i == 0 && j != 0 {
299 return false;
300 }
301 actual = &actual[j + part.len()..];
302 }
303 None => return false,
304 }
305 }
306 actual.is_empty() || expected.ends_with("[..]")
307}
308
309#[test]
310fn lines_match_works() {
311 assert!(lines_match("a b", "a b"));
312 assert!(lines_match("a[..]b", "a b"));
313 assert!(lines_match("a[..]", "a b"));
314 assert!(lines_match("[..]", "a b"));
315 assert!(lines_match("[..]b", "a b"));
316
317 assert!(!lines_match("[..]b", "c"));
318 assert!(!lines_match("b", "c"));
319 assert!(!lines_match("b", "cb"));
320}
321
322/// Compares JSON object for approximate equality.
323/// You can use `[..]` wildcard in strings (useful for OS dependent things such
324/// as paths). You can use a `"{...}"` string literal as a wildcard for
325/// arbitrary nested JSON. Arrays are sorted before comparison.
326pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> {
327 match (expected, actual) {
328 (Value::Number(l), Value::Number(r)) if l == r => None,
329 (Value::Bool(l), Value::Bool(r)) if l == r => None,
330 (Value::String(l), Value::String(r)) if lines_match(l, r) => None,
331 (Value::Array(l), Value::Array(r)) => {
332 if l.len() != r.len() {
333 return Some((expected, actual));
334 }
335
336 let mut l = l.iter().collect::<Vec<_>>();
337 let mut r = r.iter().collect::<Vec<_>>();
338
339 l.retain(|l| match r.iter().position(|r| find_mismatch(l, r).is_none()) {
340 Some(i) => {
341 r.remove(i);
342 false
343 }
344 None => true,
345 });
346
347 if !l.is_empty() {
348 assert!(!r.is_empty());
349 Some((&l[0], &r[0]))
350 } else {
351 assert_eq!(r.len(), 0);
352 None
353 }
354 }
355 (Value::Object(l), Value::Object(r)) => {
356 fn sorted_values(obj: &serde_json::Map<String, Value>) -> Vec<&Value> {
357 let mut entries = obj.iter().collect::<Vec<_>>();
358 entries.sort_by_key(|it| it.0);
359 entries.into_iter().map(|(_k, v)| v).collect::<Vec<_>>()
360 }
361
362 let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k));
363 if !same_keys {
364 return Some((expected, actual));
365 }
366
367 let l = sorted_values(l);
368 let r = sorted_values(r);
369
370 l.into_iter().zip(r).filter_map(|(l, r)| find_mismatch(l, r)).next()
371 }
372 (Value::Null, Value::Null) => None,
373 // magic string literal "{...}" acts as wildcard for any sub-JSON
374 (Value::String(l), _) if l == "{...}" => None,
375 _ => Some((expected, actual)),
376 }
377}
378
379/// Returns `false` if slow tests should not run, otherwise returns `true` and 281/// Returns `false` if slow tests should not run, otherwise returns `true` and
380/// also creates a file at `./target/.slow_tests_cookie` which serves as a flag 282/// also creates a file at `./target/.slow_tests_cookie` which serves as a flag
381/// that slow tests did run. 283/// that slow tests did run.
@@ -384,14 +286,14 @@ pub fn skip_slow_tests() -> bool {
384 if should_skip { 286 if should_skip {
385 eprintln!("ignoring slow test") 287 eprintln!("ignoring slow test")
386 } else { 288 } else {
387 let path = project_dir().join("./target/.slow_tests_cookie"); 289 let path = project_root().join("./target/.slow_tests_cookie");
388 fs::write(&path, ".").unwrap(); 290 fs::write(&path, ".").unwrap();
389 } 291 }
390 should_skip 292 should_skip
391} 293}
392 294
393/// Returns the path to the root directory of `rust-analyzer` project. 295/// Returns the path to the root directory of `rust-analyzer` project.
394pub fn project_dir() -> PathBuf { 296pub fn project_root() -> PathBuf {
395 let dir = env!("CARGO_MANIFEST_DIR"); 297 let dir = env!("CARGO_MANIFEST_DIR");
396 PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned() 298 PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned()
397} 299}
@@ -449,3 +351,39 @@ pub fn bench(label: &'static str) -> impl Drop {
449 351
450 Bencher { sw: StopWatch::start(), label } 352 Bencher { sw: StopWatch::start(), label }
451} 353}
354
355/// Checks that the `file` has the specified `contents`. If that is not the
356/// case, updates the file and then fails the test.
357pub fn ensure_file_contents(file: &Path, contents: &str) {
358 if let Err(()) = try_ensure_file_contents(file, contents) {
359 panic!("Some files were not up-to-date");
360 }
361}
362
363/// Checks that the `file` has the specified `contents`. If that is not the
364/// case, updates the file and return an Error.
365pub fn try_ensure_file_contents(file: &Path, contents: &str) -> Result<(), ()> {
366 match std::fs::read_to_string(file) {
367 Ok(old_contents) if normalize_newlines(&old_contents) == normalize_newlines(contents) => {
368 return Ok(())
369 }
370 _ => (),
371 }
372 let display_path = file.strip_prefix(&project_root()).unwrap_or(file);
373 eprintln!(
374 "\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n",
375 display_path.display()
376 );
377 if is_ci() {
378 eprintln!(" NOTE: run `cargo test` locally and commit the updated files\n");
379 }
380 if let Some(parent) = file.parent() {
381 let _ = std::fs::create_dir_all(parent);
382 }
383 std::fs::write(file, contents).unwrap();
384 Err(())
385}
386
387fn normalize_newlines(s: &str) -> String {
388 s.replace("\r\n", "\n")
389}
diff --git a/crates/test_utils/src/mark.rs b/crates/test_utils/src/mark.rs
deleted file mode 100644
index 97f5a93ad..000000000
--- a/crates/test_utils/src/mark.rs
+++ /dev/null
@@ -1,78 +0,0 @@
1//! This module implements manually tracked test coverage, which is useful for
2//! quickly finding a test responsible for testing a particular bit of code.
3//!
4//! See <https://matklad.github.io/2018/06/18/a-trick-for-test-maintenance.html>
5//! for details, but the TL;DR is that you write your test as
6//!
7//! ```
8//! #[test]
9//! fn test_foo() {
10//! mark::check!(test_foo);
11//! }
12//! ```
13//!
14//! and in the code under test you write
15//!
16//! ```
17//! # use test_utils::mark;
18//! # fn some_condition() -> bool { true }
19//! fn foo() {
20//! if some_condition() {
21//! mark::hit!(test_foo);
22//! }
23//! }
24//! ```
25//!
26//! This module then checks that executing the test indeed covers the specified
27//! function. This is useful if you come back to the `foo` function ten years
28//! later and wonder where the test are: now you can grep for `test_foo`.
29use std::sync::atomic::{AtomicUsize, Ordering};
30
31#[macro_export]
32macro_rules! _hit {
33 ($ident:ident) => {{
34 #[cfg(test)]
35 {
36 extern "C" {
37 #[no_mangle]
38 static $ident: std::sync::atomic::AtomicUsize;
39 }
40 unsafe {
41 $ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
42 }
43 }
44 }};
45}
46pub use _hit as hit;
47
48#[macro_export]
49macro_rules! _check {
50 ($ident:ident) => {
51 #[no_mangle]
52 static $ident: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
53 let _checker = $crate::mark::MarkChecker::new(&$ident);
54 };
55}
56pub use _check as check;
57
58pub struct MarkChecker {
59 mark: &'static AtomicUsize,
60 value_on_entry: usize,
61}
62
63impl MarkChecker {
64 pub fn new(mark: &'static AtomicUsize) -> MarkChecker {
65 let value_on_entry = mark.load(Ordering::Relaxed);
66 MarkChecker { mark, value_on_entry }
67 }
68}
69
70impl Drop for MarkChecker {
71 fn drop(&mut self) {
72 if std::thread::panicking() {
73 return;
74 }
75 let value_on_exit = self.mark.load(Ordering::Relaxed);
76 assert!(value_on_exit > self.value_on_entry, "mark was not hit")
77 }
78}
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md
index ead12616e..e2237ca95 100644
--- a/docs/dev/architecture.md
+++ b/docs/dev/architecture.md
@@ -46,7 +46,7 @@ This is *the* entry point, but it front-loads a lot of complexity, so its fine t
46 46
47`crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP. 47`crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP.
48 48
49`Analysis` and `AnalysisHost` types define the main API. 49`Analysis` and `AnalysisHost` types define the main API for consumers of IDE services.
50 50
51## Code Map 51## Code Map
52 52
@@ -308,9 +308,8 @@ This sections talks about the things which are everywhere and nowhere in particu
308### Code generation 308### Code generation
309 309
310Some of the components of this repository are generated through automatic processes. 310Some of the components of this repository are generated through automatic processes.
311`cargo xtask codegen` runs all generation tasks. 311Generated code is updated automatically on `cargo test`.
312Generated code is generally committed to the git repository. 312Generated code is generally committed to the git repository.
313There are tests to check that the generated code is fresh.
314 313
315In particular, we generate: 314In particular, we generate:
316 315
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 164c8482e..dd3ecc18d 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -39,7 +39,7 @@ If a language client does not know about `rust-analyzer`'s configuration options
39 39
40**Issue:** https://github.com/microsoft/language-server-protocol/issues/724 40**Issue:** https://github.com/microsoft/language-server-protocol/issues/724
41 41
42**Client Capability:** `{ "snippetTextEdit": boolean }` 42**Experimental Client Capability:** `{ "snippetTextEdit": boolean }`
43 43
44If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: 44If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s:
45 45
@@ -72,7 +72,7 @@ At the moment, rust-analyzer guarantees that only a single edit will have `Inser
72 72
73**Issue:** https://github.com/microsoft/language-server-protocol/issues/994 73**Issue:** https://github.com/microsoft/language-server-protocol/issues/994
74 74
75**Client Capability:** `{ "codeActionGroup": boolean }` 75**Experimental Client Capability:** `{ "codeActionGroup": boolean }`
76 76
77If this capability is set, `CodeAction` returned from the server contain an additional field, `group`: 77If this capability is set, `CodeAction` returned from the server contain an additional field, `group`:
78 78
@@ -119,7 +119,7 @@ Invoking code action at this position will yield two code actions for importing
119 119
120**Issue:** https://github.com/microsoft/language-server-protocol/issues/1002 120**Issue:** https://github.com/microsoft/language-server-protocol/issues/1002
121 121
122**Server Capability:** `{ "parentModule": boolean }` 122**Experimental Server Capability:** `{ "parentModule": boolean }`
123 123
124This request is sent from client to server to handle "Goto Parent Module" editor action. 124This request is sent from client to server to handle "Goto Parent Module" editor action.
125 125
@@ -153,7 +153,7 @@ mod foo;
153 153
154**Issue:** https://github.com/microsoft/language-server-protocol/issues/992 154**Issue:** https://github.com/microsoft/language-server-protocol/issues/992
155 155
156**Server Capability:** `{ "joinLines": boolean }` 156**Experimental Server Capability:** `{ "joinLines": boolean }`
157 157
158This request is sent from client to server to handle "Join Lines" editor action. 158This request is sent from client to server to handle "Join Lines" editor action.
159 159
@@ -200,7 +200,7 @@ fn main() {
200 200
201**Issue:** https://github.com/microsoft/language-server-protocol/issues/1001 201**Issue:** https://github.com/microsoft/language-server-protocol/issues/1001
202 202
203**Server Capability:** `{ "onEnter": boolean }` 203**Experimental Server Capability:** `{ "onEnter": boolean }`
204 204
205This request is sent from client to server to handle <kbd>Enter</kbd> keypress. 205This request is sent from client to server to handle <kbd>Enter</kbd> keypress.
206 206
@@ -251,7 +251,7 @@ As proper cursor positioning is raison-d'etat for `onEnter`, it uses `SnippetTex
251 251
252## Structural Search Replace (SSR) 252## Structural Search Replace (SSR)
253 253
254**Server Capability:** `{ "ssr": boolean }` 254**Experimental Server Capability:** `{ "ssr": boolean }`
255 255
256This request is sent from client to server to handle structural search replace -- automated syntax tree based transformation of the source. 256This request is sent from client to server to handle structural search replace -- automated syntax tree based transformation of the source.
257 257
@@ -293,7 +293,7 @@ SSR with query `foo($a, $b) ==>> ($a).foo($b)` will transform, eg `foo(y + 5, z)
293 293
294**Issue:** https://github.com/microsoft/language-server-protocol/issues/999 294**Issue:** https://github.com/microsoft/language-server-protocol/issues/999
295 295
296**Server Capability:** `{ "matchingBrace": boolean }` 296**Experimental Server Capability:** `{ "matchingBrace": boolean }`
297 297
298This request is sent from client to server to handle "Matching Brace" editor action. 298This request is sent from client to server to handle "Matching Brace" editor action.
299 299
@@ -338,7 +338,7 @@ Moreover, it would be cool if editors didn't need to implement even basic langua
338 338
339**Issue:** https://github.com/microsoft/language-server-protocol/issues/944 339**Issue:** https://github.com/microsoft/language-server-protocol/issues/944
340 340
341**Server Capability:** `{ "runnables": { "kinds": string[] } }` 341**Experimental Server Capability:** `{ "runnables": { "kinds": string[] } }`
342 342
343This request is sent from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`). 343This request is sent from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`).
344 344
@@ -421,7 +421,7 @@ Reloads project information (that is, re-executes `cargo metadata`).
421 421
422## Status Notification 422## Status Notification
423 423
424**Client Capability:** `{ "statusNotification": boolean }` 424**Experimental Client Capability:** `{ "statusNotification": boolean }`
425 425
426**Method:** `rust-analyzer/status` 426**Method:** `rust-analyzer/status`
427 427
@@ -519,7 +519,7 @@ interface InlayHint {
519 519
520## Hover Actions 520## Hover Actions
521 521
522**Client Capability:** `{ "hoverActions": boolean }` 522**Experimental Client Capability:** `{ "hoverActions": boolean }`
523 523
524If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`: 524If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`:
525 525
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 93ad98f20..46bd8b9b2 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -145,7 +145,7 @@ Formatting ensures that you can use your editor's "number of selected characters
145## Marked Tests 145## Marked Tests
146 146
147Use 147Use
148[`mark::hit! / mark::check!`](https://github.com/rust-analyzer/rust-analyzer/blob/71fe719dd5247ed8615641d9303d7ca1aa201c2f/crates/test_utils/src/mark.rs) 148[`cov_mark::hit! / cov_mark::check!`](https://github.com/matklad/cov-mark)
149when testing specific conditions. 149when testing specific conditions.
150Do not place several marks into a single test or condition. 150Do not place several marks into a single test or condition.
151Do not reuse marks between several tests. 151Do not reuse marks between several tests.
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index f91e04c31..042ba2d54 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -1,112 +1,322 @@
1[[rust-analyzer.assist.importMergeBehavior]]rust-analyzer.assist.importMergeBehavior (default: `"full"`):: 1[[rust-analyzer.assist.importMergeBehavior]]rust-analyzer.assist.importMergeBehavior (default: `"full"`)::
2 The strategy to use when inserting new imports or merging imports. 2+
3--
4The strategy to use when inserting new imports or merging imports.
5--
3[[rust-analyzer.assist.importPrefix]]rust-analyzer.assist.importPrefix (default: `"plain"`):: 6[[rust-analyzer.assist.importPrefix]]rust-analyzer.assist.importPrefix (default: `"plain"`)::
4 The path structure for newly inserted paths to use. 7+
8--
9The path structure for newly inserted paths to use.
10--
11[[rust-analyzer.assist.importGroup]]rust-analyzer.assist.importGroup (default: `true`)::
12+
13--
14Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
15--
5[[rust-analyzer.callInfo.full]]rust-analyzer.callInfo.full (default: `true`):: 16[[rust-analyzer.callInfo.full]]rust-analyzer.callInfo.full (default: `true`)::
6 Show function name and docs in parameter hints. 17+
18--
19Show function name and docs in parameter hints.
20--
7[[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`):: 21[[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`)::
8 Automatically refresh project info via `cargo metadata` on `Cargo.toml` changes. 22+
23--
24Automatically refresh project info via `cargo metadata` on
25`Cargo.toml` changes.
26--
9[[rust-analyzer.cargo.allFeatures]]rust-analyzer.cargo.allFeatures (default: `false`):: 27[[rust-analyzer.cargo.allFeatures]]rust-analyzer.cargo.allFeatures (default: `false`)::
10 Activate all available features (`--all-features`). 28+
29--
30Activate all available features (`--all-features`).
31--
11[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: 32[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`)::
12 List of features to activate. 33+
13[[rust-analyzer.cargo.loadOutDirsFromCheck]]rust-analyzer.cargo.loadOutDirsFromCheck (default: `false`):: 34--
14 Run `cargo check` on startup to get the correct value for package OUT_DIRs. 35List of features to activate.
36--
37[[rust-analyzer.cargo.runBuildScripts]]rust-analyzer.cargo.runBuildScripts (default: `true`)::
38+
39--
40Run build scripts (`build.rs`) for more precise code analysis.
41--
15[[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`):: 42[[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`)::
16 Do not activate the `default` feature. 43+
44--
45Do not activate the `default` feature.
46--
17[[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`):: 47[[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`)::
18 Compilation target (target triple). 48+
49--
50Compilation target (target triple).
51--
19[[rust-analyzer.cargo.noSysroot]]rust-analyzer.cargo.noSysroot (default: `false`):: 52[[rust-analyzer.cargo.noSysroot]]rust-analyzer.cargo.noSysroot (default: `false`)::
20 Internal config for debugging, disables loading of sysroot crates. 53+
54--
55Internal config for debugging, disables loading of sysroot crates.
56--
21[[rust-analyzer.checkOnSave.enable]]rust-analyzer.checkOnSave.enable (default: `true`):: 57[[rust-analyzer.checkOnSave.enable]]rust-analyzer.checkOnSave.enable (default: `true`)::
22 Run specified `cargo check` command for diagnostics on save. 58+
59--
60Run specified `cargo check` command for diagnostics on save.
61--
23[[rust-analyzer.checkOnSave.allFeatures]]rust-analyzer.checkOnSave.allFeatures (default: `null`):: 62[[rust-analyzer.checkOnSave.allFeatures]]rust-analyzer.checkOnSave.allFeatures (default: `null`)::
24 Check with all features (`--all-features`). Defaults to `#rust-analyzer.cargo.allFeatures#`. 63+
64--
65Check with all features (`--all-features`).
66Defaults to `#rust-analyzer.cargo.allFeatures#`.
67--
25[[rust-analyzer.checkOnSave.allTargets]]rust-analyzer.checkOnSave.allTargets (default: `true`):: 68[[rust-analyzer.checkOnSave.allTargets]]rust-analyzer.checkOnSave.allTargets (default: `true`)::
26 Check all targets and tests (`--all-targets`). 69+
70--
71Check all targets and tests (`--all-targets`).
72--
27[[rust-analyzer.checkOnSave.command]]rust-analyzer.checkOnSave.command (default: `"check"`):: 73[[rust-analyzer.checkOnSave.command]]rust-analyzer.checkOnSave.command (default: `"check"`)::
28 Cargo command to use for `cargo check`. 74+
75--
76Cargo command to use for `cargo check`.
77--
29[[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`):: 78[[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`)::
30 Do not activate the `default` feature. 79+
80--
81Do not activate the `default` feature.
82--
31[[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `null`):: 83[[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `null`)::
32 Check for a specific target. Defaults to `#rust-analyzer.cargo.target#`. 84+
85--
86Check for a specific target. Defaults to
87`#rust-analyzer.cargo.target#`.
88--
33[[rust-analyzer.checkOnSave.extraArgs]]rust-analyzer.checkOnSave.extraArgs (default: `[]`):: 89[[rust-analyzer.checkOnSave.extraArgs]]rust-analyzer.checkOnSave.extraArgs (default: `[]`)::
34 Extra arguments for `cargo check`. 90+
91--
92Extra arguments for `cargo check`.
93--
35[[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`):: 94[[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`)::
36 List of features to activate. Defaults to `#rust-analyzer.cargo.features#`. 95+
96--
97List of features to activate. Defaults to
98`#rust-analyzer.cargo.features#`.
99--
37[[rust-analyzer.checkOnSave.overrideCommand]]rust-analyzer.checkOnSave.overrideCommand (default: `null`):: 100[[rust-analyzer.checkOnSave.overrideCommand]]rust-analyzer.checkOnSave.overrideCommand (default: `null`)::
38 Advanced option, fully override the command rust-analyzer uses for checking. The command should include `--message-format=json` or similar option. 101+
102--
103Advanced option, fully override the command rust-analyzer uses for
104checking. The command should include `--message-format=json` or
105similar option.
106--
39[[rust-analyzer.completion.addCallArgumentSnippets]]rust-analyzer.completion.addCallArgumentSnippets (default: `true`):: 107[[rust-analyzer.completion.addCallArgumentSnippets]]rust-analyzer.completion.addCallArgumentSnippets (default: `true`)::
40 Whether to add argument snippets when completing functions. 108+
109--
110Whether to add argument snippets when completing functions.
111--
41[[rust-analyzer.completion.addCallParenthesis]]rust-analyzer.completion.addCallParenthesis (default: `true`):: 112[[rust-analyzer.completion.addCallParenthesis]]rust-analyzer.completion.addCallParenthesis (default: `true`)::
42 Whether to add parenthesis when completing functions. 113+
114--
115Whether to add parenthesis when completing functions.
116--
43[[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`):: 117[[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`)::
44 Whether to show postfix snippets like `dbg`, `if`, `not`, etc. 118+
119--
120Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
121--
45[[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: 122[[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`)::
46 Toggles the additional completions that automatically add imports when completed. Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. 123+
124--
125Toggles the additional completions that automatically add imports when completed.
126Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
127--
47[[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: 128[[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`)::
48 Whether to show native rust-analyzer diagnostics. 129+
130--
131Whether to show native rust-analyzer diagnostics.
132--
49[[rust-analyzer.diagnostics.enableExperimental]]rust-analyzer.diagnostics.enableExperimental (default: `true`):: 133[[rust-analyzer.diagnostics.enableExperimental]]rust-analyzer.diagnostics.enableExperimental (default: `true`)::
50 Whether to show experimental rust-analyzer diagnostics that might have more false positives than usual. 134+
135--
136Whether to show experimental rust-analyzer diagnostics that might
137have more false positives than usual.
138--
51[[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`):: 139[[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`)::
52 List of rust-analyzer diagnostics to disable. 140+
141--
142List of rust-analyzer diagnostics to disable.
143--
53[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: 144[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`)::
54 List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code and a blue icon in the `Problems Panel`. 145+
146--
147List of warnings that should be displayed with info severity.
148
149The warnings will be indicated by a blue squiggly underline in code
150and a blue icon in the `Problems Panel`.
151--
55[[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`):: 152[[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`)::
56 List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code and will not show up in the `Problems Panel`. 153+
154--
155List of warnings that should be displayed with hint severity.
156
157The warnings will be indicated by faded text or three dots in code
158and will not show up in the `Problems Panel`.
159--
57[[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: 160[[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`)::
58 Controls file watching implementation. 161+
162--
163Controls file watching implementation.
164--
59[[rust-analyzer.files.excludeDirs]]rust-analyzer.files.excludeDirs (default: `[]`):: 165[[rust-analyzer.files.excludeDirs]]rust-analyzer.files.excludeDirs (default: `[]`)::
60 These directories will be ignored by rust-analyzer. 166+
167--
168These directories will be ignored by rust-analyzer.
169--
61[[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`):: 170[[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`)::
62 Whether to show `Debug` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. 171+
172--
173Whether to show `Debug` action. Only applies when
174`#rust-analyzer.hoverActions.enable#` is set.
175--
63[[rust-analyzer.hoverActions.enable]]rust-analyzer.hoverActions.enable (default: `true`):: 176[[rust-analyzer.hoverActions.enable]]rust-analyzer.hoverActions.enable (default: `true`)::
64 Whether to show HoverActions in Rust files. 177+
178--
179Whether to show HoverActions in Rust files.
180--
65[[rust-analyzer.hoverActions.gotoTypeDef]]rust-analyzer.hoverActions.gotoTypeDef (default: `true`):: 181[[rust-analyzer.hoverActions.gotoTypeDef]]rust-analyzer.hoverActions.gotoTypeDef (default: `true`)::
66 Whether to show `Go to Type Definition` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. 182+
183--
184Whether to show `Go to Type Definition` action. Only applies when
185`#rust-analyzer.hoverActions.enable#` is set.
186--
67[[rust-analyzer.hoverActions.implementations]]rust-analyzer.hoverActions.implementations (default: `true`):: 187[[rust-analyzer.hoverActions.implementations]]rust-analyzer.hoverActions.implementations (default: `true`)::
68 Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. 188+
189--
190Whether to show `Implementations` action. Only applies when
191`#rust-analyzer.hoverActions.enable#` is set.
192--
69[[rust-analyzer.hoverActions.run]]rust-analyzer.hoverActions.run (default: `true`):: 193[[rust-analyzer.hoverActions.run]]rust-analyzer.hoverActions.run (default: `true`)::
70 Whether to show `Run` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. 194+
195--
196Whether to show `Run` action. Only applies when
197`#rust-analyzer.hoverActions.enable#` is set.
198--
71[[rust-analyzer.hoverActions.linksInHover]]rust-analyzer.hoverActions.linksInHover (default: `true`):: 199[[rust-analyzer.hoverActions.linksInHover]]rust-analyzer.hoverActions.linksInHover (default: `true`)::
72 Use markdown syntax for links in hover. 200+
201--
202Use markdown syntax for links in hover.
203--
73[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`):: 204[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`)::
74 Whether to show inlay type hints for method chains. 205+
206--
207Whether to show inlay type hints for method chains.
208--
75[[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `null`):: 209[[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `null`)::
76 Maximum length for inlay hints. Default is unlimited. 210+
211--
212Maximum length for inlay hints. Default is unlimited.
213--
77[[rust-analyzer.inlayHints.parameterHints]]rust-analyzer.inlayHints.parameterHints (default: `true`):: 214[[rust-analyzer.inlayHints.parameterHints]]rust-analyzer.inlayHints.parameterHints (default: `true`)::
78 Whether to show function parameter name inlay hints at the call site. 215+
216--
217Whether to show function parameter name inlay hints at the call
218site.
219--
79[[rust-analyzer.inlayHints.typeHints]]rust-analyzer.inlayHints.typeHints (default: `true`):: 220[[rust-analyzer.inlayHints.typeHints]]rust-analyzer.inlayHints.typeHints (default: `true`)::
80 Whether to show inlay type hints for variables. 221+
222--
223Whether to show inlay type hints for variables.
224--
81[[rust-analyzer.lens.debug]]rust-analyzer.lens.debug (default: `true`):: 225[[rust-analyzer.lens.debug]]rust-analyzer.lens.debug (default: `true`)::
82 Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set. 226+
227--
228Whether to show `Debug` lens. Only applies when
229`#rust-analyzer.lens.enable#` is set.
230--
83[[rust-analyzer.lens.enable]]rust-analyzer.lens.enable (default: `true`):: 231[[rust-analyzer.lens.enable]]rust-analyzer.lens.enable (default: `true`)::
84 Whether to show CodeLens in Rust files. 232+
233--
234Whether to show CodeLens in Rust files.
235--
85[[rust-analyzer.lens.implementations]]rust-analyzer.lens.implementations (default: `true`):: 236[[rust-analyzer.lens.implementations]]rust-analyzer.lens.implementations (default: `true`)::
86 Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set. 237+
238--
239Whether to show `Implementations` lens. Only applies when
240`#rust-analyzer.lens.enable#` is set.
241--
87[[rust-analyzer.lens.run]]rust-analyzer.lens.run (default: `true`):: 242[[rust-analyzer.lens.run]]rust-analyzer.lens.run (default: `true`)::
88 Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set. 243+
244--
245Whether to show `Run` lens. Only applies when
246`#rust-analyzer.lens.enable#` is set.
247--
89[[rust-analyzer.lens.methodReferences]]rust-analyzer.lens.methodReferences (default: `false`):: 248[[rust-analyzer.lens.methodReferences]]rust-analyzer.lens.methodReferences (default: `false`)::
90 Whether to show `Method References` lens. Only applies when `#rust-analyzer.lens.enable#` is set. 249+
250--
251Whether to show `Method References` lens. Only applies when
252`#rust-analyzer.lens.enable#` is set.
253--
91[[rust-analyzer.lens.references]]rust-analyzer.lens.references (default: `false`):: 254[[rust-analyzer.lens.references]]rust-analyzer.lens.references (default: `false`)::
92 Whether to show `References` lens. Only applies when `#rust-analyzer.lens.enable#` is set. 255+
256--
257Whether to show `References` lens. Only applies when
258`#rust-analyzer.lens.enable#` is set.
259--
93[[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: 260[[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`)::
94 Disable project auto-discovery in favor of explicitly specified set of projects.\n\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format. 261+
262--
263Disable project auto-discovery in favor of explicitly specified set
264of projects.
265
266Elements must be paths pointing to `Cargo.toml`,
267`rust-project.json`, or JSON objects in `rust-project.json` format.
268--
95[[rust-analyzer.lruCapacity]]rust-analyzer.lruCapacity (default: `null`):: 269[[rust-analyzer.lruCapacity]]rust-analyzer.lruCapacity (default: `null`)::
96 Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. 270+
271--
272Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
273--
97[[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`):: 274[[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`)::
98 Whether to show `can't find Cargo.toml` error message. 275+
276--
277Whether to show `can't find Cargo.toml` error message.
278--
99[[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `false`):: 279[[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `false`)::
100 Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be enabled. 280+
281--
282Enable support for procedural macros, implies `#rust-analyzer.cargo.runBuildScripts#`.
283--
101[[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`):: 284[[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`)::
102 Internal config, path to proc-macro server executable (typically, this is rust-analyzer itself, but we override this in tests). 285+
286--
287Internal config, path to proc-macro server executable (typically,
288this is rust-analyzer itself, but we override this in tests).
289--
103[[rust-analyzer.runnables.overrideCargo]]rust-analyzer.runnables.overrideCargo (default: `null`):: 290[[rust-analyzer.runnables.overrideCargo]]rust-analyzer.runnables.overrideCargo (default: `null`)::
104 Command to be executed instead of 'cargo' for runnables. 291+
292--
293Command to be executed instead of 'cargo' for runnables.
294--
105[[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`):: 295[[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`)::
106 Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be `--release`. 296+
297--
298Additional arguments to be passed to cargo for runnables such as
299tests or binaries. For example, it may be `--release`.
300--
107[[rust-analyzer.rustcSource]]rust-analyzer.rustcSource (default: `null`):: 301[[rust-analyzer.rustcSource]]rust-analyzer.rustcSource (default: `null`)::
108 Path to the rust compiler sources, for usage in rustc_private projects, or "discover" to try to automatically find it. 302+
303--
304Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
305projects, or "discover" to try to automatically find it.
306
307Any project which uses rust-analyzer with the rustcPrivate
308crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
309
310This option is not reloaded automatically; you must restart rust-analyzer for it to take effect.
311--
109[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: 312[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`)::
110 Additional arguments to `rustfmt`. 313+
314--
315Additional arguments to `rustfmt`.
316--
111[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: 317[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`)::
112 Advanced option, fully override the command rust-analyzer uses for formatting. 318+
319--
320Advanced option, fully override the command rust-analyzer uses for
321formatting.
322--
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 9f28237ff..4f2217546 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -226,6 +226,8 @@ The are several LSP client implementations for vim or neovim:
226 * inlay hints for variables and method chaining, _Neovim Only_ 226 * inlay hints for variables and method chaining, _Neovim Only_
227 * semantic highlighting is not implemented yet 227 * semantic highlighting is not implemented yet
228 228
229Note: for code actions, use `coc-codeaction-cursor` and `coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line` are unlikely to be useful.
230
229==== LanguageClient-neovim 231==== LanguageClient-neovim
230 232
2311. Install LanguageClient-neovim by following the instructions 2331. Install LanguageClient-neovim by following the instructions
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 9d0d1d4ec..198c17556 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -9,6 +9,7 @@
9 "version": "0.4.0-dev", 9 "version": "0.4.0-dev",
10 "license": "MIT OR Apache-2.0", 10 "license": "MIT OR Apache-2.0",
11 "dependencies": { 11 "dependencies": {
12 "https-proxy-agent": "^5.0.0",
12 "node-fetch": "^2.6.1", 13 "node-fetch": "^2.6.1",
13 "vscode-languageclient": "^7.1.0-next.4" 14 "vscode-languageclient": "^7.1.0-next.4"
14 }, 15 },
@@ -515,7 +516,6 @@
515 "version": "6.0.2", 516 "version": "6.0.2",
516 "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 517 "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
517 "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 518 "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
518 "dev": true,
519 "dependencies": { 519 "dependencies": {
520 "debug": "4" 520 "debug": "4"
521 }, 521 },
@@ -830,6 +830,7 @@
830 "dependencies": { 830 "dependencies": {
831 "anymatch": "~3.1.1", 831 "anymatch": "~3.1.1",
832 "braces": "~3.0.2", 832 "braces": "~3.0.2",
833 "fsevents": "~2.3.1",
833 "glob-parent": "~5.1.0", 834 "glob-parent": "~5.1.0",
834 "is-binary-path": "~2.1.0", 835 "is-binary-path": "~2.1.0",
835 "is-glob": "~4.0.1", 836 "is-glob": "~4.0.1",
@@ -959,7 +960,6 @@
959 "version": "4.3.1", 960 "version": "4.3.1",
960 "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 961 "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
961 "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 962 "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
962 "dev": true,
963 "dependencies": { 963 "dependencies": {
964 "ms": "2.1.2" 964 "ms": "2.1.2"
965 }, 965 },
@@ -1759,7 +1759,6 @@
1759 "version": "5.0.0", 1759 "version": "5.0.0",
1760 "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", 1760 "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
1761 "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", 1761 "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
1762 "dev": true,
1763 "dependencies": { 1762 "dependencies": {
1764 "agent-base": "6", 1763 "agent-base": "6",
1765 "debug": "4" 1764 "debug": "4"
@@ -2236,8 +2235,7 @@
2236 "node_modules/ms": { 2235 "node_modules/ms": {
2237 "version": "2.1.2", 2236 "version": "2.1.2",
2238 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 2237 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
2239 "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 2238 "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
2240 "dev": true
2241 }, 2239 },
2242 "node_modules/mute-stream": { 2240 "node_modules/mute-stream": {
2243 "version": "0.0.8", 2241 "version": "0.0.8",
@@ -2682,6 +2680,9 @@
2682 "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.39.1.tgz", 2680 "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.39.1.tgz",
2683 "integrity": "sha512-9rfr0Z6j+vE+eayfNVFr1KZ+k+jiUl2+0e4quZafy1x6SFCjzFspfRSO2ZZQeWeX9noeDTUDgg6eCENiEPFvQg==", 2681 "integrity": "sha512-9rfr0Z6j+vE+eayfNVFr1KZ+k+jiUl2+0e4quZafy1x6SFCjzFspfRSO2ZZQeWeX9noeDTUDgg6eCENiEPFvQg==",
2684 "dev": true, 2682 "dev": true,
2683 "dependencies": {
2684 "fsevents": "~2.3.1"
2685 },
2685 "bin": { 2686 "bin": {
2686 "rollup": "dist/bin/rollup" 2687 "rollup": "dist/bin/rollup"
2687 }, 2688 },
@@ -3843,7 +3844,6 @@
3843 "version": "6.0.2", 3844 "version": "6.0.2",
3844 "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 3845 "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
3845 "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 3846 "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
3846 "dev": true,
3847 "requires": { 3847 "requires": {
3848 "debug": "4" 3848 "debug": "4"
3849 } 3849 }
@@ -4190,7 +4190,6 @@
4190 "version": "4.3.1", 4190 "version": "4.3.1",
4191 "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 4191 "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
4192 "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 4192 "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
4193 "dev": true,
4194 "requires": { 4193 "requires": {
4195 "ms": "2.1.2" 4194 "ms": "2.1.2"
4196 } 4195 }
@@ -4798,7 +4797,6 @@
4798 "version": "5.0.0", 4797 "version": "5.0.0",
4799 "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", 4798 "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
4800 "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", 4799 "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
4801 "dev": true,
4802 "requires": { 4800 "requires": {
4803 "agent-base": "6", 4801 "agent-base": "6",
4804 "debug": "4" 4802 "debug": "4"
@@ -5175,8 +5173,7 @@
5175 "ms": { 5173 "ms": {
5176 "version": "2.1.2", 5174 "version": "2.1.2",
5177 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 5175 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
5178 "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 5176 "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
5179 "dev": true
5180 }, 5177 },
5181 "mute-stream": { 5178 "mute-stream": {
5182 "version": "0.0.8", 5179 "version": "0.0.8",
diff --git a/editors/code/package.json b/editors/code/package.json
index e3e0ebff0..b29f006f0 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -35,6 +35,7 @@
35 "test": "node ./out/tests/runTests.js" 35 "test": "node ./out/tests/runTests.js"
36 }, 36 },
37 "dependencies": { 37 "dependencies": {
38 "https-proxy-agent": "^5.0.0",
38 "node-fetch": "^2.6.1", 39 "node-fetch": "^2.6.1",
39 "vscode-languageclient": "^7.1.0-next.4" 40 "vscode-languageclient": "^7.1.0-next.4"
40 }, 41 },
@@ -385,13 +386,18 @@
385 "Force import paths to be absolute by always starting them with `crate` or the crate name they refer to." 386 "Force import paths to be absolute by always starting them with `crate` or the crate name they refer to."
386 ] 387 ]
387 }, 388 },
389 "rust-analyzer.assist.importGroup": {
390 "markdownDescription": "Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.",
391 "default": true,
392 "type": "boolean"
393 },
388 "rust-analyzer.callInfo.full": { 394 "rust-analyzer.callInfo.full": {
389 "markdownDescription": "Show function name and docs in parameter hints.", 395 "markdownDescription": "Show function name and docs in parameter hints.",
390 "default": true, 396 "default": true,
391 "type": "boolean" 397 "type": "boolean"
392 }, 398 },
393 "rust-analyzer.cargo.autoreload": { 399 "rust-analyzer.cargo.autoreload": {
394 "markdownDescription": "Automatically refresh project info via `cargo metadata` on `Cargo.toml` changes.", 400 "markdownDescription": "Automatically refresh project info via `cargo metadata` on\n`Cargo.toml` changes.",
395 "default": true, 401 "default": true,
396 "type": "boolean" 402 "type": "boolean"
397 }, 403 },
@@ -408,9 +414,9 @@
408 "type": "string" 414 "type": "string"
409 } 415 }
410 }, 416 },
411 "rust-analyzer.cargo.loadOutDirsFromCheck": { 417 "rust-analyzer.cargo.runBuildScripts": {
412 "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs.", 418 "markdownDescription": "Run build scripts (`build.rs`) for more precise code analysis.",
413 "default": false, 419 "default": true,
414 "type": "boolean" 420 "type": "boolean"
415 }, 421 },
416 "rust-analyzer.cargo.noDefaultFeatures": { 422 "rust-analyzer.cargo.noDefaultFeatures": {
@@ -437,7 +443,7 @@
437 "type": "boolean" 443 "type": "boolean"
438 }, 444 },
439 "rust-analyzer.checkOnSave.allFeatures": { 445 "rust-analyzer.checkOnSave.allFeatures": {
440 "markdownDescription": "Check with all features (`--all-features`). Defaults to `#rust-analyzer.cargo.allFeatures#`.", 446 "markdownDescription": "Check with all features (`--all-features`).\nDefaults to `#rust-analyzer.cargo.allFeatures#`.",
441 "default": null, 447 "default": null,
442 "type": [ 448 "type": [
443 "null", 449 "null",
@@ -463,7 +469,7 @@
463 ] 469 ]
464 }, 470 },
465 "rust-analyzer.checkOnSave.target": { 471 "rust-analyzer.checkOnSave.target": {
466 "markdownDescription": "Check for a specific target. Defaults to `#rust-analyzer.cargo.target#`.", 472 "markdownDescription": "Check for a specific target. Defaults to\n`#rust-analyzer.cargo.target#`.",
467 "default": null, 473 "default": null,
468 "type": [ 474 "type": [
469 "null", 475 "null",
@@ -479,7 +485,7 @@
479 } 485 }
480 }, 486 },
481 "rust-analyzer.checkOnSave.features": { 487 "rust-analyzer.checkOnSave.features": {
482 "markdownDescription": "List of features to activate. Defaults to `#rust-analyzer.cargo.features#`.", 488 "markdownDescription": "List of features to activate. Defaults to\n`#rust-analyzer.cargo.features#`.",
483 "default": null, 489 "default": null,
484 "type": [ 490 "type": [
485 "null", 491 "null",
@@ -490,7 +496,7 @@
490 } 496 }
491 }, 497 },
492 "rust-analyzer.checkOnSave.overrideCommand": { 498 "rust-analyzer.checkOnSave.overrideCommand": {
493 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for checking. The command should include `--message-format=json` or similar option.", 499 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nchecking. The command should include `--message-format=json` or\nsimilar option.",
494 "default": null, 500 "default": null,
495 "type": [ 501 "type": [
496 "null", 502 "null",
@@ -516,7 +522,7 @@
516 "type": "boolean" 522 "type": "boolean"
517 }, 523 },
518 "rust-analyzer.completion.autoimport.enable": { 524 "rust-analyzer.completion.autoimport.enable": {
519 "markdownDescription": "Toggles the additional completions that automatically add imports when completed. Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.", 525 "markdownDescription": "Toggles the additional completions that automatically add imports when completed.\nNote that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.",
520 "default": true, 526 "default": true,
521 "type": "boolean" 527 "type": "boolean"
522 }, 528 },
@@ -526,7 +532,7 @@
526 "type": "boolean" 532 "type": "boolean"
527 }, 533 },
528 "rust-analyzer.diagnostics.enableExperimental": { 534 "rust-analyzer.diagnostics.enableExperimental": {
529 "markdownDescription": "Whether to show experimental rust-analyzer diagnostics that might have more false positives than usual.", 535 "markdownDescription": "Whether to show experimental rust-analyzer diagnostics that might\nhave more false positives than usual.",
530 "default": true, 536 "default": true,
531 "type": "boolean" 537 "type": "boolean"
532 }, 538 },
@@ -540,7 +546,7 @@
540 "uniqueItems": true 546 "uniqueItems": true
541 }, 547 },
542 "rust-analyzer.diagnostics.warningsAsHint": { 548 "rust-analyzer.diagnostics.warningsAsHint": {
543 "markdownDescription": "List of warnings that should be displayed with info severity.\\n\\nThe warnings will be indicated by a blue squiggly underline in code and a blue icon in the `Problems Panel`.", 549 "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.",
544 "default": [], 550 "default": [],
545 "type": "array", 551 "type": "array",
546 "items": { 552 "items": {
@@ -548,7 +554,7 @@
548 } 554 }
549 }, 555 },
550 "rust-analyzer.diagnostics.warningsAsInfo": { 556 "rust-analyzer.diagnostics.warningsAsInfo": {
551 "markdownDescription": "List of warnings that should be displayed with hint severity.\\n\\nThe warnings will be indicated by faded text or three dots in code and will not show up in the `Problems Panel`.", 557 "markdownDescription": "List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code\nand will not show up in the `Problems Panel`.",
552 "default": [], 558 "default": [],
553 "type": "array", 559 "type": "array",
554 "items": { 560 "items": {
@@ -569,7 +575,7 @@
569 } 575 }
570 }, 576 },
571 "rust-analyzer.hoverActions.debug": { 577 "rust-analyzer.hoverActions.debug": {
572 "markdownDescription": "Whether to show `Debug` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", 578 "markdownDescription": "Whether to show `Debug` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.",
573 "default": true, 579 "default": true,
574 "type": "boolean" 580 "type": "boolean"
575 }, 581 },
@@ -579,17 +585,17 @@
579 "type": "boolean" 585 "type": "boolean"
580 }, 586 },
581 "rust-analyzer.hoverActions.gotoTypeDef": { 587 "rust-analyzer.hoverActions.gotoTypeDef": {
582 "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", 588 "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.",
583 "default": true, 589 "default": true,
584 "type": "boolean" 590 "type": "boolean"
585 }, 591 },
586 "rust-analyzer.hoverActions.implementations": { 592 "rust-analyzer.hoverActions.implementations": {
587 "markdownDescription": "Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", 593 "markdownDescription": "Whether to show `Implementations` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.",
588 "default": true, 594 "default": true,
589 "type": "boolean" 595 "type": "boolean"
590 }, 596 },
591 "rust-analyzer.hoverActions.run": { 597 "rust-analyzer.hoverActions.run": {
592 "markdownDescription": "Whether to show `Run` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", 598 "markdownDescription": "Whether to show `Run` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.",
593 "default": true, 599 "default": true,
594 "type": "boolean" 600 "type": "boolean"
595 }, 601 },
@@ -613,7 +619,7 @@
613 "minimum": 0 619 "minimum": 0
614 }, 620 },
615 "rust-analyzer.inlayHints.parameterHints": { 621 "rust-analyzer.inlayHints.parameterHints": {
616 "markdownDescription": "Whether to show function parameter name inlay hints at the call site.", 622 "markdownDescription": "Whether to show function parameter name inlay hints at the call\nsite.",
617 "default": true, 623 "default": true,
618 "type": "boolean" 624 "type": "boolean"
619 }, 625 },
@@ -623,7 +629,7 @@
623 "type": "boolean" 629 "type": "boolean"
624 }, 630 },
625 "rust-analyzer.lens.debug": { 631 "rust-analyzer.lens.debug": {
626 "markdownDescription": "Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 632 "markdownDescription": "Whether to show `Debug` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
627 "default": true, 633 "default": true,
628 "type": "boolean" 634 "type": "boolean"
629 }, 635 },
@@ -633,27 +639,27 @@
633 "type": "boolean" 639 "type": "boolean"
634 }, 640 },
635 "rust-analyzer.lens.implementations": { 641 "rust-analyzer.lens.implementations": {
636 "markdownDescription": "Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 642 "markdownDescription": "Whether to show `Implementations` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
637 "default": true, 643 "default": true,
638 "type": "boolean" 644 "type": "boolean"
639 }, 645 },
640 "rust-analyzer.lens.run": { 646 "rust-analyzer.lens.run": {
641 "markdownDescription": "Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 647 "markdownDescription": "Whether to show `Run` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
642 "default": true, 648 "default": true,
643 "type": "boolean" 649 "type": "boolean"
644 }, 650 },
645 "rust-analyzer.lens.methodReferences": { 651 "rust-analyzer.lens.methodReferences": {
646 "markdownDescription": "Whether to show `Method References` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 652 "markdownDescription": "Whether to show `Method References` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
647 "default": false, 653 "default": false,
648 "type": "boolean" 654 "type": "boolean"
649 }, 655 },
650 "rust-analyzer.lens.references": { 656 "rust-analyzer.lens.references": {
651 "markdownDescription": "Whether to show `References` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 657 "markdownDescription": "Whether to show `References` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
652 "default": false, 658 "default": false,
653 "type": "boolean" 659 "type": "boolean"
654 }, 660 },
655 "rust-analyzer.linkedProjects": { 661 "rust-analyzer.linkedProjects": {
656 "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects.\\n\\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format.", 662 "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set\nof projects.\n\nElements must be paths pointing to `Cargo.toml`,\n`rust-project.json`, or JSON objects in `rust-project.json` format.",
657 "default": [], 663 "default": [],
658 "type": "array", 664 "type": "array",
659 "items": { 665 "items": {
@@ -678,12 +684,12 @@
678 "type": "boolean" 684 "type": "boolean"
679 }, 685 },
680 "rust-analyzer.procMacro.enable": { 686 "rust-analyzer.procMacro.enable": {
681 "markdownDescription": "Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be enabled.", 687 "markdownDescription": "Enable support for procedural macros, implies `#rust-analyzer.cargo.runBuildScripts#`.",
682 "default": false, 688 "default": false,
683 "type": "boolean" 689 "type": "boolean"
684 }, 690 },
685 "rust-analyzer.procMacro.server": { 691 "rust-analyzer.procMacro.server": {
686 "markdownDescription": "Internal config, path to proc-macro server executable (typically, this is rust-analyzer itself, but we override this in tests).", 692 "markdownDescription": "Internal config, path to proc-macro server executable (typically,\nthis is rust-analyzer itself, but we override this in tests).",
687 "default": null, 693 "default": null,
688 "type": [ 694 "type": [
689 "null", 695 "null",
@@ -699,7 +705,7 @@
699 ] 705 ]
700 }, 706 },
701 "rust-analyzer.runnables.cargoExtraArgs": { 707 "rust-analyzer.runnables.cargoExtraArgs": {
702 "markdownDescription": "Additional arguments to be passed to cargo for runnables such as tests or binaries.\\nFor example, it may be `--release`.", 708 "markdownDescription": "Additional arguments to be passed to cargo for runnables such as\ntests or binaries. For example, it may be `--release`.",
703 "default": [], 709 "default": [],
704 "type": "array", 710 "type": "array",
705 "items": { 711 "items": {
@@ -707,7 +713,7 @@
707 } 713 }
708 }, 714 },
709 "rust-analyzer.rustcSource": { 715 "rust-analyzer.rustcSource": {
710 "markdownDescription": "Path to the rust compiler sources, for usage in rustc_private projects, or \"discover\" to try to automatically find it.", 716 "markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option is not reloaded automatically; you must restart rust-analyzer for it to take effect.",
711 "default": null, 717 "default": null,
712 "type": [ 718 "type": [
713 "null", 719 "null",
@@ -723,7 +729,7 @@
723 } 729 }
724 }, 730 },
725 "rust-analyzer.rustfmt.overrideCommand": { 731 "rust-analyzer.rustfmt.overrideCommand": {
726 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for formatting.", 732 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting.",
727 "default": null, 733 "default": null,
728 "type": [ 734 "type": [
729 "null", 735 "null",
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index ddb5cfbd3..82f0a0566 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -100,6 +100,14 @@ export class Config {
100 get channel() { return this.get<UpdatesChannel>("updates.channel"); } 100 get channel() { return this.get<UpdatesChannel>("updates.channel"); }
101 get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); } 101 get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); }
102 get traceExtension() { return this.get<boolean>("trace.extension"); } 102 get traceExtension() { return this.get<boolean>("trace.extension"); }
103 get httpProxy() {
104 const httpProxy = vscode
105 .workspace
106 .getConfiguration('http')
107 .get<null | string>("proxy")!;
108
109 return httpProxy || process.env["https_proxy"] || process.env["HTTPS_PROXY"];
110 }
103 111
104 get inlayHints() { 112 get inlayHints() {
105 return { 113 return {
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 00393d6e8..1be4f1758 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -183,7 +183,7 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
183 } 183 }
184 184
185 const release = await downloadWithRetryDialog(state, async () => { 185 const release = await downloadWithRetryDialog(state, async () => {
186 return await fetchRelease("nightly", state.githubToken); 186 return await fetchRelease("nightly", state.githubToken, config.httpProxy);
187 }).catch(async (e) => { 187 }).catch(async (e) => {
188 log.error(e); 188 log.error(e);
189 if (state.releaseId === undefined) { // Show error only for the initial download 189 if (state.releaseId === undefined) { // Show error only for the initial download
@@ -209,6 +209,7 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
209 url: artifact.browser_download_url, 209 url: artifact.browser_download_url,
210 dest, 210 dest,
211 progressTitle: "Downloading rust-analyzer extension", 211 progressTitle: "Downloading rust-analyzer extension",
212 httpProxy: config.httpProxy,
212 }); 213 });
213 }); 214 });
214 215
@@ -331,7 +332,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string
331 332
332 const releaseTag = config.package.releaseTag; 333 const releaseTag = config.package.releaseTag;
333 const release = await downloadWithRetryDialog(state, async () => { 334 const release = await downloadWithRetryDialog(state, async () => {
334 return await fetchRelease(releaseTag, state.githubToken); 335 return await fetchRelease(releaseTag, state.githubToken, config.httpProxy);
335 }); 336 });
336 const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`); 337 const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`);
337 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 338 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
@@ -343,6 +344,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string
343 progressTitle: "Downloading rust-analyzer server", 344 progressTitle: "Downloading rust-analyzer server",
344 gunzip: true, 345 gunzip: true,
345 mode: 0o755, 346 mode: 0o755,
347 httpProxy: config.httpProxy,
346 }); 348 });
347 }); 349 });
348 350
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
index d39dc1baf..07ebc615c 100644
--- a/editors/code/src/net.ts
+++ b/editors/code/src/net.ts
@@ -1,4 +1,6 @@
1import fetch from "node-fetch"; 1import fetch from "node-fetch";
2var HttpsProxyAgent = require('https-proxy-agent');
3
2import * as vscode from "vscode"; 4import * as vscode from "vscode";
3import * as stream from "stream"; 5import * as stream from "stream";
4import * as crypto from "crypto"; 6import * as crypto from "crypto";
@@ -17,6 +19,7 @@ const REPO = "rust-analyzer";
17export async function fetchRelease( 19export async function fetchRelease(
18 releaseTag: string, 20 releaseTag: string,
19 githubToken: string | null | undefined, 21 githubToken: string | null | undefined,
22 httpProxy: string | null | undefined,
20): Promise<GithubRelease> { 23): Promise<GithubRelease> {
21 24
22 const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`; 25 const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`;
@@ -30,7 +33,14 @@ export async function fetchRelease(
30 headers.Authorization = "token " + githubToken; 33 headers.Authorization = "token " + githubToken;
31 } 34 }
32 35
33 const response = await fetch(requestUrl, { headers: headers }); 36 const response = await (() => {
37 if (httpProxy) {
38 log.debug(`Fetching release metadata via proxy: ${httpProxy}`);
39 return fetch(requestUrl, { headers: headers, agent: new HttpsProxyAgent(httpProxy) });
40 }
41
42 return fetch(requestUrl, { headers: headers });
43 })();
34 44
35 if (!response.ok) { 45 if (!response.ok) {
36 log.error("Error fetching artifact release info", { 46 log.error("Error fetching artifact release info", {
@@ -73,6 +83,7 @@ interface DownloadOpts {
73 dest: string; 83 dest: string;
74 mode?: number; 84 mode?: number;
75 gunzip?: boolean; 85 gunzip?: boolean;
86 httpProxy?: string;
76} 87}
77 88
78export async function download(opts: DownloadOpts) { 89export async function download(opts: DownloadOpts) {
@@ -91,7 +102,7 @@ export async function download(opts: DownloadOpts) {
91 }, 102 },
92 async (progress, _cancellationToken) => { 103 async (progress, _cancellationToken) => {
93 let lastPercentage = 0; 104 let lastPercentage = 0;
94 await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, (readBytes, totalBytes) => { 105 await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, opts.httpProxy, (readBytes, totalBytes) => {
95 const newPercentage = Math.round((readBytes / totalBytes) * 100); 106 const newPercentage = Math.round((readBytes / totalBytes) * 100);
96 if (newPercentage !== lastPercentage) { 107 if (newPercentage !== lastPercentage) {
97 progress.report({ 108 progress.report({
@@ -113,9 +124,17 @@ async function downloadFile(
113 destFilePath: fs.PathLike, 124 destFilePath: fs.PathLike,
114 mode: number | undefined, 125 mode: number | undefined,
115 gunzip: boolean, 126 gunzip: boolean,
127 httpProxy: string | null | undefined,
116 onProgress: (readBytes: number, totalBytes: number) => void 128 onProgress: (readBytes: number, totalBytes: number) => void
117): Promise<void> { 129): Promise<void> {
118 const res = await fetch(url); 130 const res = await (() => {
131 if (httpProxy) {
132 log.debug(`Downloading ${url} via proxy: ${httpProxy}`);
133 return fetch(url, { agent: new HttpsProxyAgent(httpProxy) });
134 }
135
136 return fetch(url);
137 })();
119 138
120 if (!res.ok) { 139 if (!res.ok) {
121 log.error("Error", res.status, "while downloading file from", url); 140 log.error("Error", res.status, "while downloading file from", url);
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index b17dde598..e084f0df6 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -15,5 +15,5 @@ ungrammar = "=1.11"
15walkdir = "2.3.1" 15walkdir = "2.3.1"
16write-json = "0.1.0" 16write-json = "0.1.0"
17xshell = "0.1" 17xshell = "0.1"
18xflags = "0.1.2" 18xflags = "0.2.1"
19# Avoid adding more dependencies to this crate 19# Avoid adding more dependencies to this crate
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index 2f56c5ad0..2cf3c6fdc 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -7,68 +7,66 @@
7 7
8mod gen_syntax; 8mod gen_syntax;
9mod gen_parser_tests; 9mod gen_parser_tests;
10mod gen_lint_completions;
10mod gen_assists_docs; 11mod gen_assists_docs;
11mod gen_feature_docs; 12mod gen_feature_docs;
12mod gen_lint_completions;
13mod gen_diagnostic_docs; 13mod gen_diagnostic_docs;
14 14
15use std::{ 15use std::{
16 fmt, mem, 16 fmt, mem,
17 path::{Path, PathBuf}, 17 path::{Path, PathBuf},
18}; 18};
19use xshell::{cmd, pushenv, read_file, write_file}; 19use xshell::{cmd, pushenv};
20 20
21use crate::{ensure_rustfmt, flags, project_root, Result}; 21use crate::{ensure_rustfmt, project_root, Result};
22 22
23pub(crate) use self::{ 23pub(crate) use self::{
24 gen_assists_docs::{generate_assists_docs, generate_assists_tests}, 24 gen_assists_docs::generate_assists_tests, gen_lint_completions::generate_lint_completions,
25 gen_diagnostic_docs::generate_diagnostic_docs, 25 gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax,
26 gen_feature_docs::generate_feature_docs,
27 gen_lint_completions::generate_lint_completions,
28 gen_parser_tests::generate_parser_tests,
29 gen_syntax::generate_syntax,
30}; 26};
31 27
32#[derive(Debug, PartialEq, Eq, Clone, Copy)] 28pub(crate) fn docs() -> Result<()> {
33pub(crate) enum Mode { 29 // We don't commit docs to the repo, so we can just overwrite them.
34 Overwrite, 30 gen_assists_docs::generate_assists_docs()?;
35 Verify, 31 gen_feature_docs::generate_feature_docs()?;
32 gen_diagnostic_docs::generate_diagnostic_docs()?;
33 Ok(())
36} 34}
37 35
38impl flags::Codegen { 36#[allow(unused)]
39 pub(crate) fn run(self) -> Result<()> { 37fn used() {
40 if self.features { 38 generate_parser_tests();
41 generate_lint_completions(Mode::Overwrite)?; 39 generate_assists_tests();
42 } 40 generate_syntax();
43 generate_syntax(Mode::Overwrite)?; 41 generate_lint_completions();
44 generate_parser_tests(Mode::Overwrite)?;
45 generate_assists_tests(Mode::Overwrite)?;
46 generate_assists_docs(Mode::Overwrite)?;
47 generate_feature_docs(Mode::Overwrite)?;
48 generate_diagnostic_docs(Mode::Overwrite)?;
49 Ok(())
50 }
51} 42}
52 43
53/// A helper to update file on disk if it has changed. 44/// Checks that the `file` has the specified `contents`. If that is not the
54/// With verify = false, 45/// case, updates the file and then fails the test.
55fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { 46pub(crate) fn ensure_file_contents(file: &Path, contents: &str) -> Result<()> {
56 match read_file(path) { 47 match std::fs::read_to_string(file) {
57 Ok(old_contents) if normalize(&old_contents) == normalize(contents) => { 48 Ok(old_contents) if normalize_newlines(&old_contents) == normalize_newlines(contents) => {
58 return Ok(()); 49 return Ok(())
59 } 50 }
60 _ => (), 51 _ => (),
61 } 52 }
62 if mode == Mode::Verify { 53 let display_path = file.strip_prefix(&project_root()).unwrap_or(file);
63 anyhow::bail!("`{}` is not up-to-date", path.display()); 54 eprintln!(
55 "\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n",
56 display_path.display()
57 );
58 if std::env::var("CI").is_ok() {
59 eprintln!(" NOTE: run `cargo test` locally and commit the updated files\n");
64 } 60 }
65 eprintln!("updating {}", path.display()); 61 if let Some(parent) = file.parent() {
66 write_file(path, contents)?; 62 let _ = std::fs::create_dir_all(parent);
67 return Ok(());
68
69 fn normalize(s: &str) -> String {
70 s.replace("\r\n", "\n")
71 } 63 }
64 std::fs::write(file, contents).unwrap();
65 anyhow::bail!("some file were not up to date")
66}
67
68fn normalize_newlines(s: &str) -> String {
69 s.replace("\r\n", "\n")
72} 70}
73 71
74const PREAMBLE: &str = "Generated file, do not edit by hand, see `xtask/src/codegen`"; 72const PREAMBLE: &str = "Generated file, do not edit by hand, see `xtask/src/codegen`";
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs
index c469b388d..158680993 100644
--- a/xtask/src/codegen/gen_assists_docs.rs
+++ b/xtask/src/codegen/gen_assists_docs.rs
@@ -2,22 +2,25 @@
2 2
3use std::{fmt, path::Path}; 3use std::{fmt, path::Path};
4 4
5use xshell::write_file;
6
5use crate::{ 7use crate::{
6 codegen::{self, extract_comment_blocks_with_empty_lines, reformat, Location, Mode, PREAMBLE}, 8 codegen::{self, extract_comment_blocks_with_empty_lines, reformat, Location, PREAMBLE},
7 project_root, rust_files_in, Result, 9 project_root, rust_files_in, Result,
8}; 10};
9 11
10pub(crate) fn generate_assists_tests(mode: Mode) -> Result<()> { 12pub(crate) fn generate_assists_tests() -> Result<()> {
11 let assists = Assist::collect()?; 13 let assists = Assist::collect()?;
12 generate_tests(&assists, mode) 14 generate_tests(&assists)
13} 15}
14 16
15pub(crate) fn generate_assists_docs(mode: Mode) -> Result<()> { 17pub(crate) fn generate_assists_docs() -> Result<()> {
16 let assists = Assist::collect()?; 18 let assists = Assist::collect()?;
17 let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); 19 let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
18 let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); 20 let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim());
19 let dst = project_root().join("docs/user/generated_assists.adoc"); 21 let dst = project_root().join("docs/user/generated_assists.adoc");
20 codegen::update(&dst, &contents, mode) 22 write_file(dst, &contents)?;
23 Ok(())
21} 24}
22 25
23#[derive(Debug)] 26#[derive(Debug)]
@@ -111,7 +114,7 @@ impl fmt::Display for Assist {
111 } 114 }
112} 115}
113 116
114fn generate_tests(assists: &[Assist], mode: Mode) -> Result<()> { 117fn generate_tests(assists: &[Assist]) -> Result<()> {
115 let mut buf = String::from("use super::check_doc_test;\n"); 118 let mut buf = String::from("use super::check_doc_test;\n");
116 119
117 for assist in assists.iter() { 120 for assist in assists.iter() {
@@ -135,7 +138,10 @@ r#####"
135 buf.push_str(&test) 138 buf.push_str(&test)
136 } 139 }
137 let buf = reformat(&buf)?; 140 let buf = reformat(&buf)?;
138 codegen::update(&project_root().join("crates/ide_assists/src/tests/generated.rs"), &buf, mode) 141 codegen::ensure_file_contents(
142 &project_root().join("crates/ide_assists/src/tests/generated.rs"),
143 &buf,
144 )
139} 145}
140 146
141fn hide_hash_comments(text: &str) -> String { 147fn hide_hash_comments(text: &str) -> String {
diff --git a/xtask/src/codegen/gen_diagnostic_docs.rs b/xtask/src/codegen/gen_diagnostic_docs.rs
index a2561817b..9cf4d0a88 100644
--- a/xtask/src/codegen/gen_diagnostic_docs.rs
+++ b/xtask/src/codegen/gen_diagnostic_docs.rs
@@ -2,18 +2,20 @@
2 2
3use std::{fmt, path::PathBuf}; 3use std::{fmt, path::PathBuf};
4 4
5use xshell::write_file;
6
5use crate::{ 7use crate::{
6 codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode, PREAMBLE}, 8 codegen::{extract_comment_blocks_with_empty_lines, Location, PREAMBLE},
7 project_root, rust_files, Result, 9 project_root, rust_files, Result,
8}; 10};
9 11
10pub(crate) fn generate_diagnostic_docs(mode: Mode) -> Result<()> { 12pub(crate) fn generate_diagnostic_docs() -> Result<()> {
11 let diagnostics = Diagnostic::collect()?; 13 let diagnostics = Diagnostic::collect()?;
12 let contents = 14 let contents =
13 diagnostics.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); 15 diagnostics.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
14 let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); 16 let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim());
15 let dst = project_root().join("docs/user/generated_diagnostic.adoc"); 17 let dst = project_root().join("docs/user/generated_diagnostic.adoc");
16 codegen::update(&dst, &contents, mode)?; 18 write_file(&dst, &contents)?;
17 Ok(()) 19 Ok(())
18} 20}
19 21
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs
index cad7ff477..c373d7d70 100644
--- a/xtask/src/codegen/gen_feature_docs.rs
+++ b/xtask/src/codegen/gen_feature_docs.rs
@@ -2,17 +2,19 @@
2 2
3use std::{fmt, path::PathBuf}; 3use std::{fmt, path::PathBuf};
4 4
5use xshell::write_file;
6
5use crate::{ 7use crate::{
6 codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode, PREAMBLE}, 8 codegen::{extract_comment_blocks_with_empty_lines, Location, PREAMBLE},
7 project_root, rust_files, Result, 9 project_root, rust_files, Result,
8}; 10};
9 11
10pub(crate) fn generate_feature_docs(mode: Mode) -> Result<()> { 12pub(crate) fn generate_feature_docs() -> Result<()> {
11 let features = Feature::collect()?; 13 let features = Feature::collect()?;
12 let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); 14 let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
13 let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); 15 let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim());
14 let dst = project_root().join("docs/user/generated_features.adoc"); 16 let dst = project_root().join("docs/user/generated_features.adoc");
15 codegen::update(&dst, &contents, mode)?; 17 write_file(&dst, &contents)?;
16 Ok(()) 18 Ok(())
17} 19}
18 20
diff --git a/xtask/src/codegen/gen_lint_completions.rs b/xtask/src/codegen/gen_lint_completions.rs
index b1c057037..24dbc6a39 100644
--- a/xtask/src/codegen/gen_lint_completions.rs
+++ b/xtask/src/codegen/gen_lint_completions.rs
@@ -5,13 +5,10 @@ use std::path::{Path, PathBuf};
5use walkdir::WalkDir; 5use walkdir::WalkDir;
6use xshell::{cmd, read_file}; 6use xshell::{cmd, read_file};
7 7
8use crate::{ 8use crate::codegen::{ensure_file_contents, project_root, reformat, Result};
9 codegen::{project_root, reformat, update, Mode, Result},
10 run_rustfmt,
11};
12 9
13pub(crate) fn generate_lint_completions(mode: Mode) -> Result<()> { 10pub(crate) fn generate_lint_completions() -> Result<()> {
14 if !Path::new("./target/rust").exists() { 11 if !project_root().join("./target/rust").exists() {
15 cmd!("git clone --depth=1 https://github.com/rust-lang/rust ./target/rust").run()?; 12 cmd!("git clone --depth=1 https://github.com/rust-lang/rust ./target/rust").run()?;
16 } 13 }
17 14
@@ -25,8 +22,7 @@ pub(crate) fn generate_lint_completions(mode: Mode) -> Result<()> {
25 22
26 let destination = 23 let destination =
27 project_root().join("crates/ide_completion/src/generated_lint_completions.rs"); 24 project_root().join("crates/ide_completion/src/generated_lint_completions.rs");
28 update(destination.as_path(), &contents, mode)?; 25 ensure_file_contents(destination.as_path(), &contents)?;
29 run_rustfmt(mode)?;
30 26
31 Ok(()) 27 Ok(())
32} 28}
diff --git a/xtask/src/codegen/gen_parser_tests.rs b/xtask/src/codegen/gen_parser_tests.rs
index cb8939063..096590653 100644
--- a/xtask/src/codegen/gen_parser_tests.rs
+++ b/xtask/src/codegen/gen_parser_tests.rs
@@ -8,13 +8,13 @@ use std::{
8}; 8};
9 9
10use crate::{ 10use crate::{
11 codegen::{extract_comment_blocks, update, Mode}, 11 codegen::{ensure_file_contents, extract_comment_blocks},
12 project_root, Result, 12 project_root, Result,
13}; 13};
14 14
15pub(crate) fn generate_parser_tests(mode: Mode) -> Result<()> { 15pub(crate) fn generate_parser_tests() -> Result<()> {
16 let tests = tests_from_dir(&project_root().join(Path::new("crates/parser/src/grammar")))?; 16 let tests = tests_from_dir(&project_root().join(Path::new("crates/parser/src/grammar")))?;
17 fn install_tests(tests: &HashMap<String, Test>, into: &str, mode: Mode) -> Result<()> { 17 fn install_tests(tests: &HashMap<String, Test>, into: &str) -> Result<()> {
18 let tests_dir = project_root().join(into); 18 let tests_dir = project_root().join(into);
19 if !tests_dir.is_dir() { 19 if !tests_dir.is_dir() {
20 fs::create_dir_all(&tests_dir)?; 20 fs::create_dir_all(&tests_dir)?;
@@ -35,12 +35,12 @@ pub(crate) fn generate_parser_tests(mode: Mode) -> Result<()> {
35 tests_dir.join(file_name) 35 tests_dir.join(file_name)
36 } 36 }
37 }; 37 };
38 update(&path, &test.text, mode)?; 38 ensure_file_contents(&path, &test.text)?;
39 } 39 }
40 Ok(()) 40 Ok(())
41 } 41 }
42 install_tests(&tests.ok, "crates/syntax/test_data/parser/inline/ok", mode)?; 42 install_tests(&tests.ok, "crates/syntax/test_data/parser/inline/ok")?;
43 install_tests(&tests.err, "crates/syntax/test_data/parser/inline/err", mode) 43 install_tests(&tests.err, "crates/syntax/test_data/parser/inline/err")
44} 44}
45 45
46#[derive(Debug)] 46#[derive(Debug)]
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs
index 191bc0e9d..80f26e8f5 100644
--- a/xtask/src/codegen/gen_syntax.rs
+++ b/xtask/src/codegen/gen_syntax.rs
@@ -14,25 +14,25 @@ use ungrammar::{rust_grammar, Grammar, Rule};
14 14
15use crate::{ 15use crate::{
16 ast_src::{AstEnumSrc, AstNodeSrc, AstSrc, Cardinality, Field, KindsSrc, KINDS_SRC}, 16 ast_src::{AstEnumSrc, AstNodeSrc, AstSrc, Cardinality, Field, KindsSrc, KINDS_SRC},
17 codegen::{reformat, update, Mode}, 17 codegen::{ensure_file_contents, reformat},
18 project_root, Result, 18 project_root, Result,
19}; 19};
20 20
21pub(crate) fn generate_syntax(mode: Mode) -> Result<()> { 21pub(crate) fn generate_syntax() -> Result<()> {
22 let grammar = rust_grammar(); 22 let grammar = rust_grammar();
23 let ast = lower(&grammar); 23 let ast = lower(&grammar);
24 24
25 let syntax_kinds_file = project_root().join("crates/parser/src/syntax_kind/generated.rs"); 25 let syntax_kinds_file = project_root().join("crates/parser/src/syntax_kind/generated.rs");
26 let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; 26 let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?;
27 update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?; 27 ensure_file_contents(syntax_kinds_file.as_path(), &syntax_kinds)?;
28 28
29 let ast_tokens_file = project_root().join("crates/syntax/src/ast/generated/tokens.rs"); 29 let ast_tokens_file = project_root().join("crates/syntax/src/ast/generated/tokens.rs");
30 let contents = generate_tokens(&ast)?; 30 let contents = generate_tokens(&ast)?;
31 update(ast_tokens_file.as_path(), &contents, mode)?; 31 ensure_file_contents(ast_tokens_file.as_path(), &contents)?;
32 32
33 let ast_nodes_file = project_root().join("crates/syntax/src/ast/generated/nodes.rs"); 33 let ast_nodes_file = project_root().join("crates/syntax/src/ast/generated/nodes.rs");
34 let contents = generate_nodes(KINDS_SRC, &ast)?; 34 let contents = generate_nodes(KINDS_SRC, &ast)?;
35 update(ast_nodes_file.as_path(), &contents, mode)?; 35 ensure_file_contents(ast_nodes_file.as_path(), &contents)?;
36 36
37 Ok(()) 37 Ok(())
38} 38}
diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs
index 5710fbdb5..48d1ad45e 100644
--- a/xtask/src/flags.rs
+++ b/xtask/src/flags.rs
@@ -1,6 +1,10 @@
1#![allow(unreachable_pub)] 1#![allow(unreachable_pub)]
2 2
3xflags::args_parser! { 3use crate::install::{ClientOpt, Malloc, ServerOpt};
4
5xflags::xflags! {
6 src "./src/flags.rs"
7
4 /// Run custom build command. 8 /// Run custom build command.
5 cmd xtask { 9 cmd xtask {
6 default cmd help { 10 default cmd help {
@@ -23,10 +27,6 @@ xflags::args_parser! {
23 optional --jemalloc 27 optional --jemalloc
24 } 28 }
25 29
26 cmd codegen {
27 optional --features
28 }
29
30 cmd lint {} 30 cmd lint {}
31 cmd fuzz-tests {} 31 cmd fuzz-tests {}
32 cmd pre-cache {} 32 cmd pre-cache {}
@@ -53,7 +53,7 @@ xflags::args_parser! {
53 53
54// generated start 54// generated start
55// The following code is generated by `xflags` macro. 55// The following code is generated by `xflags` macro.
56// Run `env XFLAGS_DUMP= cargo build` to regenerate. 56// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate.
57#[derive(Debug)] 57#[derive(Debug)]
58pub struct Xtask { 58pub struct Xtask {
59 pub subcommand: XtaskCmd, 59 pub subcommand: XtaskCmd,
@@ -63,7 +63,6 @@ pub struct Xtask {
63pub enum XtaskCmd { 63pub enum XtaskCmd {
64 Help(Help), 64 Help(Help),
65 Install(Install), 65 Install(Install),
66 Codegen(Codegen),
67 Lint(Lint), 66 Lint(Lint),
68 FuzzTests(FuzzTests), 67 FuzzTests(FuzzTests),
69 PreCache(PreCache), 68 PreCache(PreCache),
@@ -89,18 +88,13 @@ pub struct Install {
89} 88}
90 89
91#[derive(Debug)] 90#[derive(Debug)]
92pub struct Codegen { 91pub struct Lint;
93 pub features: bool,
94}
95
96#[derive(Debug)]
97pub struct Lint {}
98 92
99#[derive(Debug)] 93#[derive(Debug)]
100pub struct FuzzTests {} 94pub struct FuzzTests;
101 95
102#[derive(Debug)] 96#[derive(Debug)]
103pub struct PreCache {} 97pub struct PreCache;
104 98
105#[derive(Debug)] 99#[derive(Debug)]
106pub struct Release { 100pub struct Release {
@@ -129,11 +123,32 @@ pub struct Bb {
129} 123}
130 124
131impl Xtask { 125impl Xtask {
132 pub const HELP: &'static str = Self::_HELP; 126 pub const HELP: &'static str = Self::HELP_;
133 127
134 pub fn from_env() -> xflags::Result<Self> { 128 pub fn from_env() -> xflags::Result<Self> {
135 let mut p = xflags::rt::Parser::new_from_env(); 129 Self::from_env_()
136 Self::_parse(&mut p)
137 } 130 }
138} 131}
139// generated end 132// generated end
133
134impl Install {
135 pub(crate) fn server(&self) -> Option<ServerOpt> {
136 if self.client && !self.server {
137 return None;
138 }
139 let malloc = if self.mimalloc {
140 Malloc::Mimalloc
141 } else if self.jemalloc {
142 Malloc::Jemalloc
143 } else {
144 Malloc::System
145 };
146 Some(ServerOpt { malloc })
147 }
148 pub(crate) fn client(&self) -> Option<ClientOpt> {
149 if !self.client && self.server {
150 return None;
151 }
152 Some(ClientOpt { code_bin: self.code_bin.clone() })
153 }
154}
diff --git a/xtask/src/install.rs b/xtask/src/install.rs
index ea2194248..177028b08 100644
--- a/xtask/src/install.rs
+++ b/xtask/src/install.rs
@@ -5,60 +5,32 @@ use std::{env, path::PathBuf, str};
5use anyhow::{bail, format_err, Context, Result}; 5use anyhow::{bail, format_err, Context, Result};
6use xshell::{cmd, pushd}; 6use xshell::{cmd, pushd};
7 7
8use crate::flags;
9
8// Latest stable, feel free to send a PR if this lags behind. 10// Latest stable, feel free to send a PR if this lags behind.
9const REQUIRED_RUST_VERSION: u32 = 50; 11const REQUIRED_RUST_VERSION: u32 = 50;
10 12
11pub(crate) struct InstallCmd { 13impl flags::Install {
12 pub(crate) client: Option<ClientOpt>, 14 pub(crate) fn run(self) -> Result<()> {
13 pub(crate) server: Option<ServerOpt>, 15 if cfg!(target_os = "macos") {
14} 16 fix_path_for_mac().context("Fix path for mac")?
15 17 }
16#[derive(Clone, Copy)] 18 if let Some(server) = self.server() {
17pub(crate) enum ClientOpt { 19 install_server(server).context("install server")?;
18 VsCode, 20 }
19 VsCodeExploration, 21 if let Some(client) = self.client() {
20 VsCodeInsiders, 22 install_client(client).context("install client")?;
21 VsCodium,
22 VsCodeOss,
23 Any,
24}
25
26impl ClientOpt {
27 pub(crate) const fn as_cmds(&self) -> &'static [&'static str] {
28 match self {
29 ClientOpt::VsCode => &["code"],
30 ClientOpt::VsCodeExploration => &["code-exploration"],
31 ClientOpt::VsCodeInsiders => &["code-insiders"],
32 ClientOpt::VsCodium => &["codium"],
33 ClientOpt::VsCodeOss => &["code-oss"],
34 ClientOpt::Any => &["code", "code-exploration", "code-insiders", "codium", "code-oss"],
35 } 23 }
24 Ok(())
36 } 25 }
37} 26}
38 27
39impl Default for ClientOpt { 28#[derive(Clone)]
40 fn default() -> Self { 29pub(crate) struct ClientOpt {
41 ClientOpt::Any 30 pub(crate) code_bin: Option<String>,
42 }
43} 31}
44 32
45impl std::str::FromStr for ClientOpt { 33const VS_CODES: &[&str] = &["code", "code-exploration", "code-insiders", "codium", "code-oss"];
46 type Err = anyhow::Error;
47
48 fn from_str(s: &str) -> Result<Self, Self::Err> {
49 [
50 ClientOpt::VsCode,
51 ClientOpt::VsCodeExploration,
52 ClientOpt::VsCodeInsiders,
53 ClientOpt::VsCodium,
54 ClientOpt::VsCodeOss,
55 ]
56 .iter()
57 .copied()
58 .find(|c| [s] == c.as_cmds())
59 .ok_or_else(|| anyhow::format_err!("no such client"))
60 }
61}
62 34
63pub(crate) struct ServerOpt { 35pub(crate) struct ServerOpt {
64 pub(crate) malloc: Malloc, 36 pub(crate) malloc: Malloc,
@@ -70,21 +42,6 @@ pub(crate) enum Malloc {
70 Jemalloc, 42 Jemalloc,
71} 43}
72 44
73impl InstallCmd {
74 pub(crate) fn run(self) -> Result<()> {
75 if cfg!(target_os = "macos") {
76 fix_path_for_mac().context("Fix path for mac")?
77 }
78 if let Some(server) = self.server {
79 install_server(server).context("install server")?;
80 }
81 if let Some(client) = self.client {
82 install_client(client).context("install client")?;
83 }
84 Ok(())
85 }
86}
87
88fn fix_path_for_mac() -> Result<()> { 45fn fix_path_for_mac() -> Result<()> {
89 let mut vscode_path: Vec<PathBuf> = { 46 let mut vscode_path: Vec<PathBuf> = {
90 const COMMON_APP_PATH: &str = 47 const COMMON_APP_PATH: &str =
@@ -121,21 +78,12 @@ fn fix_path_for_mac() -> Result<()> {
121fn install_client(client_opt: ClientOpt) -> Result<()> { 78fn install_client(client_opt: ClientOpt) -> Result<()> {
122 let _dir = pushd("./editors/code"); 79 let _dir = pushd("./editors/code");
123 80
124 let find_code = |f: fn(&str) -> bool| -> Result<&'static str> { 81 // Package extension.
125 client_opt.as_cmds().iter().copied().find(|bin| f(bin)).ok_or_else(|| { 82 if cfg!(unix) {
126 format_err!("Can't execute `code --version`. Perhaps it is not in $PATH?")
127 })
128 };
129
130 let installed_extensions = if cfg!(unix) {
131 cmd!("npm --version").run().context("`npm` is required to build the VS Code plugin")?; 83 cmd!("npm --version").run().context("`npm` is required to build the VS Code plugin")?;
132 cmd!("npm ci").run()?; 84 cmd!("npm ci").run()?;
133 85
134 cmd!("npm run package --scripts-prepend-node-path").run()?; 86 cmd!("npm run package --scripts-prepend-node-path").run()?;
135
136 let code = find_code(|bin| cmd!("{bin} --version").read().is_ok())?;
137 cmd!("{code} --install-extension rust-analyzer.vsix --force").run()?;
138 cmd!("{code} --list-extensions").read()?
139 } else { 87 } else {
140 cmd!("cmd.exe /c npm --version") 88 cmd!("cmd.exe /c npm --version")
141 .run() 89 .run()
@@ -143,8 +91,36 @@ fn install_client(client_opt: ClientOpt) -> Result<()> {
143 cmd!("cmd.exe /c npm ci").run()?; 91 cmd!("cmd.exe /c npm ci").run()?;
144 92
145 cmd!("cmd.exe /c npm run package").run()?; 93 cmd!("cmd.exe /c npm run package").run()?;
94 };
95
96 // Find the appropriate VS Code binary.
97 let lifetime_extender;
98 let candidates: &[&str] = match client_opt.code_bin.as_deref() {
99 Some(it) => {
100 lifetime_extender = [it];
101 &lifetime_extender[..]
102 }
103 None => VS_CODES,
104 };
105 let code = candidates
106 .iter()
107 .copied()
108 .find(|&bin| {
109 if cfg!(unix) {
110 cmd!("{bin} --version").read().is_ok()
111 } else {
112 cmd!("cmd.exe /c {bin}.cmd --version").read().is_ok()
113 }
114 })
115 .ok_or_else(|| {
116 format_err!("Can't execute `{} --version`. Perhaps it is not in $PATH?", candidates[0])
117 })?;
146 118
147 let code = find_code(|bin| cmd!("cmd.exe /c {bin}.cmd --version").read().is_ok())?; 119 // Install & verify.
120 let installed_extensions = if cfg!(unix) {
121 cmd!("{code} --install-extension rust-analyzer.vsix --force").run()?;
122 cmd!("{code} --list-extensions").read()?
123 } else {
148 cmd!("cmd.exe /c {code}.cmd --install-extension rust-analyzer.vsix --force").run()?; 124 cmd!("cmd.exe /c {code}.cmd --install-extension rust-analyzer.vsix --force").run()?;
149 cmd!("cmd.exe /c {code}.cmd --list-extensions").read()? 125 cmd!("cmd.exe /c {code}.cmd --list-extensions").read()?
150 }; 126 };
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index e419db7a7..35cc7c108 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -28,11 +28,7 @@ use std::{
28use walkdir::{DirEntry, WalkDir}; 28use walkdir::{DirEntry, WalkDir};
29use xshell::{cmd, cp, pushd, pushenv}; 29use xshell::{cmd, cp, pushd, pushenv};
30 30
31use crate::{ 31use crate::dist::DistCmd;
32 codegen::Mode,
33 dist::DistCmd,
34 install::{InstallCmd, Malloc, ServerOpt},
35};
36 32
37fn main() -> Result<()> { 33fn main() -> Result<()> {
38 let _d = pushd(project_root())?; 34 let _d = pushd(project_root())?;
@@ -43,32 +39,7 @@ fn main() -> Result<()> {
43 println!("{}", flags::Xtask::HELP); 39 println!("{}", flags::Xtask::HELP);
44 return Ok(()); 40 return Ok(());
45 } 41 }
46 flags::XtaskCmd::Install(flags) => { 42 flags::XtaskCmd::Install(cmd) => cmd.run(),
47 if flags.server && flags.client {
48 eprintln!(
49 "error: The argument `--server` cannot be used with `--client`\n\n\
50 For more information try --help"
51 );
52 return Ok(());
53 }
54
55 let malloc = if flags.mimalloc {
56 Malloc::Mimalloc
57 } else if flags.jemalloc {
58 Malloc::Jemalloc
59 } else {
60 Malloc::System
61 };
62
63 let client_bin = flags.code_bin.map(|it| it.parse()).transpose()?;
64
65 InstallCmd {
66 client: if flags.server { None } else { client_bin },
67 server: if flags.client { None } else { Some(ServerOpt { malloc }) },
68 }
69 .run()
70 }
71 flags::XtaskCmd::Codegen(cmd) => cmd.run(),
72 flags::XtaskCmd::Lint(_) => run_clippy(), 43 flags::XtaskCmd::Lint(_) => run_clippy(),
73 flags::XtaskCmd::FuzzTests(_) => run_fuzzer(), 44 flags::XtaskCmd::FuzzTests(_) => run_fuzzer(),
74 flags::XtaskCmd::PreCache(cmd) => cmd.run(), 45 flags::XtaskCmd::PreCache(cmd) => cmd.run(),
@@ -113,18 +84,6 @@ fn rust_files_in(path: &Path) -> impl Iterator<Item = PathBuf> {
113 files_in(path, "rs") 84 files_in(path, "rs")
114} 85}
115 86
116fn run_rustfmt(mode: Mode) -> Result<()> {
117 let _dir = pushd(project_root())?;
118 let _e = pushenv("RUSTUP_TOOLCHAIN", "stable");
119 ensure_rustfmt()?;
120 let check = match mode {
121 Mode::Overwrite => &[][..],
122 Mode::Verify => &["--", "--check"],
123 };
124 cmd!("cargo fmt {check...}").run()?;
125 Ok(())
126}
127
128fn ensure_rustfmt() -> Result<()> { 87fn ensure_rustfmt() -> Result<()> {
129 let out = cmd!("rustfmt --version").read()?; 88 let out = cmd!("rustfmt --version").read()?;
130 if !out.contains("stable") { 89 if !out.contains("stable") {
diff --git a/xtask/src/release.rs b/xtask/src/release.rs
index d8d86fd63..dde5d14ee 100644
--- a/xtask/src/release.rs
+++ b/xtask/src/release.rs
@@ -2,7 +2,7 @@ use std::fmt::Write;
2 2
3use xshell::{cmd, cp, pushd, read_dir, write_file}; 3use xshell::{cmd, cp, pushd, read_dir, write_file};
4 4
5use crate::{codegen, date_iso, flags, is_release_tag, project_root, Mode, Result}; 5use crate::{codegen, date_iso, flags, is_release_tag, project_root, Result};
6 6
7impl flags::Release { 7impl flags::Release {
8 pub(crate) fn run(self) -> Result<()> { 8 pub(crate) fn run(self) -> Result<()> {
@@ -12,8 +12,7 @@ impl flags::Release {
12 cmd!("git reset --hard tags/nightly").run()?; 12 cmd!("git reset --hard tags/nightly").run()?;
13 cmd!("git push").run()?; 13 cmd!("git push").run()?;
14 } 14 }
15 codegen::generate_assists_docs(Mode::Overwrite)?; 15 codegen::docs()?;
16 codegen::generate_feature_docs(Mode::Overwrite)?;
17 16
18 let website_root = project_root().join("../rust-analyzer.github.io"); 17 let website_root = project_root().join("../rust-analyzer.github.io");
19 let changelog_dir = website_root.join("./thisweek/_posts"); 18 let changelog_dir = website_root.join("./thisweek/_posts");
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs
index 349ca14d0..1352d1218 100644
--- a/xtask/src/tidy.rs
+++ b/xtask/src/tidy.rs
@@ -3,48 +3,48 @@ use std::{
3 path::{Path, PathBuf}, 3 path::{Path, PathBuf},
4}; 4};
5 5
6use xshell::{cmd, read_file}; 6use xshell::{cmd, pushd, pushenv, read_file};
7 7
8use crate::{ 8use crate::{cargo_files, codegen, project_root, rust_files};
9 cargo_files,
10 codegen::{self, Mode},
11 project_root, run_rustfmt, rust_files,
12};
13 9
14#[test] 10#[test]
15fn generated_grammar_is_fresh() { 11fn generate_grammar() {
16 if let Err(error) = codegen::generate_syntax(Mode::Verify) { 12 codegen::generate_syntax().unwrap()
17 panic!("{}. Please update it by running `cargo xtask codegen`", error);
18 }
19} 13}
20 14
21#[test] 15#[test]
22fn generated_tests_are_fresh() { 16fn generate_parser_tests() {
23 if let Err(error) = codegen::generate_parser_tests(Mode::Verify) { 17 codegen::generate_parser_tests().unwrap()
24 panic!("{}. Please update tests by running `cargo xtask codegen`", error);
25 }
26} 18}
27 19
28#[test] 20#[test]
29fn generated_assists_are_fresh() { 21fn generate_assists_tests() {
30 if let Err(error) = codegen::generate_assists_tests(Mode::Verify) { 22 codegen::generate_assists_tests().unwrap();
31 panic!("{}. Please update assists by running `cargo xtask codegen`", error); 23}
32 } 24
25/// This clones rustc repo, and so is not worth to keep up-to-date. We update
26/// manually by un-ignoring the test from time to time.
27#[test]
28#[ignore]
29fn generate_lint_completions() {
30 codegen::generate_lint_completions().unwrap()
33} 31}
34 32
35#[test] 33#[test]
36fn check_code_formatting() { 34fn check_code_formatting() {
37 if let Err(error) = run_rustfmt(Mode::Verify) { 35 let _dir = pushd(project_root()).unwrap();
38 panic!("{}. Please format the code by running `cargo format`", error); 36 let _e = pushenv("RUSTUP_TOOLCHAIN", "stable");
37 crate::ensure_rustfmt().unwrap();
38 let res = cmd!("cargo fmt -- --check").run();
39 if !res.is_ok() {
40 let _ = cmd!("cargo fmt").run();
39 } 41 }
42 res.unwrap()
40} 43}
41 44
42#[test] 45#[test]
43fn smoke_test_docs_generation() { 46fn smoke_test_generate_documentation() {
44 // We don't commit docs to the repo, so we can just overwrite in tests. 47 codegen::docs().unwrap()
45 codegen::generate_assists_docs(Mode::Overwrite).unwrap();
46 codegen::generate_feature_docs(Mode::Overwrite).unwrap();
47 codegen::generate_diagnostic_docs(Mode::Overwrite).unwrap();
48} 48}
49 49
50#[test] 50#[test]