aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock75
-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.rs2147
-rw-r--r--crates/hir/src/semantics.rs5
-rw-r--r--crates/hir/src/source_analyzer.rs5
-rw-r--r--crates/hir_def/Cargo.toml1
-rw-r--r--crates/hir_def/src/attr.rs3
-rw-r--r--crates/hir_def/src/body.rs7
-rw-r--r--crates/hir_def/src/body/lower.rs3
-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.rs4
-rw-r--r--crates/hir_def/src/find_path.rs22
-rw-r--r--crates/hir_def/src/import_map.rs101
-rw-r--r--crates/hir_def/src/item_scope.rs3
-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/nameres/collector.rs17
-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.rs24
-rw-r--r--crates/hir_ty/Cargo.toml7
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs11
-rw-r--r--crates/hir_ty/src/infer/coerce.rs5
-rw-r--r--crates/hir_ty/src/infer/expr.rs3
-rw-r--r--crates/hir_ty/src/infer/pat.rs3
-rw-r--r--crates/hir_ty/src/infer/unify.rs8
-rw-r--r--crates/hir_ty/src/lower.rs5
-rw-r--r--crates/hir_ty/src/method_resolution.rs2
-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.rs5
-rw-r--r--crates/ide/Cargo.toml1
-rw-r--r--crates/ide/src/hover.rs9
-rw-r--r--crates/ide/src/join_lines.rs8
-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.rs8
-rw-r--r--crates/ide/src/typing/on_enter.rs8
-rw-r--r--crates/ide_assists/Cargo.toml1
-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.rs3
-rw-r--r--crates/ide_assists/src/handlers/change_visibility.rs7
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs21
-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/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.rs13
-rw-r--r--crates/ide_assists/src/handlers/qualify_path.rs23
-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_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.toml1
-rw-r--r--crates/ide_completion/src/completions/dot.rs6
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs10
-rw-r--r--crates/ide_completion/src/completions/keyword.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.rs11
-rw-r--r--crates/ide_completion/src/lib.rs2
-rw-r--r--crates/ide_completion/src/render.rs18
-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.toml1
-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/insert_use.rs24
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs44
-rw-r--r--crates/ide_ssr/Cargo.toml1
-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.toml1
-rw-r--r--crates/mbe/src/lib.rs5
-rw-r--r--crates/mbe/src/tests.rs6
-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.rs141
-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.rs24
-rw-r--r--crates/rust-analyzer/src/to_proto.rs6
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs97
-rw-r--r--crates/syntax/Cargo.toml1
-rw-r--r--crates/syntax/src/algo.rs26
-rw-r--r--crates/syntax/src/tests.rs6
-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/style.md2
-rw-r--r--docs/user/generated_config.adoc6
-rw-r--r--editors/code/package-lock.json17
-rw-r--r--editors/code/package.json10
-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.rs27
-rw-r--r--xtask/src/main.rs15
-rw-r--r--xtask/src/release.rs5
-rw-r--r--xtask/src/tidy.rs50
151 files changed, 3671 insertions, 3200 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 39f27098a..51a07abe3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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",
@@ -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",
@@ -911,9 +916,9 @@ dependencies = [
911 916
912[[package]] 917[[package]]
913name = "mimalloc" 918name = "mimalloc"
914version = "0.1.24" 919version = "0.1.25"
915source = "registry+https://github.com/rust-lang/crates.io-index" 920source = "registry+https://github.com/rust-lang/crates.io-index"
916checksum = "757efec188b3d2088949d912e01ea2fe87164ed6376b6c5d7dd4f3ce1668a93d" 921checksum = "1e7c6b11afd1e5e689ac96b6d18b1fc763398fe3d7eed99e8773426bc2033dfb"
917dependencies = [ 922dependencies = [
918 "libmimalloc-sys", 923 "libmimalloc-sys",
919] 924]
@@ -1466,18 +1471,18 @@ dependencies = [
1466 1471
1467[[package]] 1472[[package]]
1468name = "serde" 1473name = "serde"
1469version = "1.0.123" 1474version = "1.0.124"
1470source = "registry+https://github.com/rust-lang/crates.io-index" 1475source = "registry+https://github.com/rust-lang/crates.io-index"
1471checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" 1476checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f"
1472dependencies = [ 1477dependencies = [
1473 "serde_derive", 1478 "serde_derive",
1474] 1479]
1475 1480
1476[[package]] 1481[[package]]
1477name = "serde_derive" 1482name = "serde_derive"
1478version = "1.0.123" 1483version = "1.0.124"
1479source = "registry+https://github.com/rust-lang/crates.io-index" 1484source = "registry+https://github.com/rust-lang/crates.io-index"
1480checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" 1485checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b"
1481dependencies = [ 1486dependencies = [
1482 "proc-macro2", 1487 "proc-macro2",
1483 "quote", 1488 "quote",
@@ -1561,9 +1566,9 @@ dependencies = [
1561 1566
1562[[package]] 1567[[package]]
1563name = "syn" 1568name = "syn"
1564version = "1.0.61" 1569version = "1.0.62"
1565source = "registry+https://github.com/rust-lang/crates.io-index" 1570source = "registry+https://github.com/rust-lang/crates.io-index"
1566checksum = "ed22b90a0e734a23a7610f4283ac9e5acfb96cbb30dfefa540d66f866f1c09c5" 1571checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512"
1567dependencies = [ 1572dependencies = [
1568 "proc-macro2", 1573 "proc-macro2",
1569 "quote", 1574 "quote",
@@ -1587,6 +1592,7 @@ name = "syntax"
1587version = "0.0.0" 1592version = "0.0.0"
1588dependencies = [ 1593dependencies = [
1589 "arrayvec", 1594 "arrayvec",
1595 "cov-mark",
1590 "expect-test", 1596 "expect-test",
1591 "indexmap", 1597 "indexmap",
1592 "itertools", 1598 "itertools",
@@ -1621,7 +1627,6 @@ dependencies = [
1621 "dissimilar", 1627 "dissimilar",
1622 "profile", 1628 "profile",
1623 "rustc-hash", 1629 "rustc-hash",
1624 "serde_json",
1625 "stdx", 1630 "stdx",
1626 "text-size", 1631 "text-size",
1627] 1632]
@@ -1919,18 +1924,18 @@ checksum = "06069a848f95fceae3e5e03c0ddc8cb78452b56654ee0c8e68f938cf790fb9e3"
1919 1924
1920[[package]] 1925[[package]]
1921name = "xflags" 1926name = "xflags"
1922version = "0.1.4" 1927version = "0.2.1"
1923source = "registry+https://github.com/rust-lang/crates.io-index" 1928source = "registry+https://github.com/rust-lang/crates.io-index"
1924checksum = "222e914b43cec5d7305ac5116d10a14b3a52c50e9062d642c92631f3beabc729" 1929checksum = "59ad6ce6a0b7224130015b4ebac796478ac04e0079f5d222a690efea06a9208a"
1925dependencies = [ 1930dependencies = [
1926 "xflags-macros", 1931 "xflags-macros",
1927] 1932]
1928 1933
1929[[package]] 1934[[package]]
1930name = "xflags-macros" 1935name = "xflags-macros"
1931version = "0.1.4" 1936version = "0.2.1"
1932source = "registry+https://github.com/rust-lang/crates.io-index" 1937source = "registry+https://github.com/rust-lang/crates.io-index"
1933checksum = "52f18f5b4aa7f95e209d5b9274f6164c3938920b4d5c75f97f0dd16daee25ddd" 1938checksum = "c8037d3ca14996158b03c0fa905d0834906ef0fc7044df72c1f5ff690e5e62c9"
1934dependencies = [ 1939dependencies = [
1935 "proc-macro2", 1940 "proc-macro2",
1936] 1941]
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
deleted file mode 100644
index 7656db974..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(hir_ty::AdtId(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(hir_ty::AdtId(AdtId::StructId(s)), ref substs) => (s.into(), substs),
1732 Ty::Adt(hir_ty::AdtId(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..62692c2c1 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -20,49 +20,2138 @@
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.module(db.upcast()) }
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(
560 db,
561 self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
562 self.id,
563 )
564 }
565
566 pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> {
567 db.struct_data(self.id).repr.clone()
568 }
569
570 pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
571 self.variant_data(db).kind()
572 }
573
574 fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
575 db.struct_data(self.id).variant_data.clone()
576 }
577}
578
579#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
580pub struct Union {
581 pub(crate) id: UnionId,
582}
583
584impl Union {
585 pub fn name(self, db: &dyn HirDatabase) -> Name {
586 db.union_data(self.id).name.clone()
587 }
588
589 pub fn module(self, db: &dyn HirDatabase) -> Module {
590 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) }
591 }
592
593 pub fn ty(self, db: &dyn HirDatabase) -> Type {
594 Type::from_def(
595 db,
596 self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
597 self.id,
598 )
599 }
600
601 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
602 db.union_data(self.id)
603 .variant_data
604 .fields()
605 .iter()
606 .map(|(id, _)| Field { parent: self.into(), id })
607 .collect()
608 }
609
610 fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
611 db.union_data(self.id).variant_data.clone()
612 }
613}
614
615#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
616pub struct Enum {
617 pub(crate) id: EnumId,
618}
619
620impl Enum {
621 pub fn module(self, db: &dyn HirDatabase) -> Module {
622 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) }
623 }
624
625 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
626 Some(self.module(db).krate())
627 }
628
629 pub fn name(self, db: &dyn HirDatabase) -> Name {
630 db.enum_data(self.id).name.clone()
631 }
632
633 pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> {
634 db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect()
635 }
636
637 pub fn ty(self, db: &dyn HirDatabase) -> Type {
638 Type::from_def(
639 db,
640 self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
641 self.id,
642 )
643 }
644}
645
646#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
647pub struct Variant {
648 pub(crate) parent: Enum,
649 pub(crate) id: LocalEnumVariantId,
650}
651
652impl Variant {
653 pub fn module(self, db: &dyn HirDatabase) -> Module {
654 self.parent.module(db)
655 }
656 pub fn parent_enum(self, _db: &dyn HirDatabase) -> Enum {
657 self.parent
658 }
659
660 pub fn name(self, db: &dyn HirDatabase) -> Name {
661 db.enum_data(self.parent.id).variants[self.id].name.clone()
662 }
663
664 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
665 self.variant_data(db)
666 .fields()
667 .iter()
668 .map(|(id, _)| Field { parent: self.into(), id })
669 .collect()
670 }
671
672 pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
673 self.variant_data(db).kind()
674 }
675
676 pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
677 db.enum_data(self.parent.id).variants[self.id].variant_data.clone()
678 }
679}
680
681/// A Data Type
682#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
683pub enum Adt {
684 Struct(Struct),
685 Union(Union),
686 Enum(Enum),
687}
688impl_from!(Struct, Union, Enum for Adt);
689
690impl Adt {
691 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
692 let subst = db.generic_defaults(self.into());
693 subst.iter().any(|ty| &ty.value == &Ty::Unknown)
694 }
695
696 /// Turns this ADT into a type. Any type parameters of the ADT will be
697 /// turned into unknown types, which is good for e.g. finding the most
698 /// general set of completions, but will not look very nice when printed.
699 pub fn ty(self, db: &dyn HirDatabase) -> Type {
700 let id = AdtId::from(self);
701 Type::from_def(db, id.module(db.upcast()).krate(), id)
702 }
703
704 pub fn module(self, db: &dyn HirDatabase) -> Module {
705 match self {
706 Adt::Struct(s) => s.module(db),
707 Adt::Union(s) => s.module(db),
708 Adt::Enum(e) => e.module(db),
709 }
710 }
711
712 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
713 Some(self.module(db).krate())
714 }
715
716 pub fn name(self, db: &dyn HirDatabase) -> Name {
717 match self {
718 Adt::Struct(s) => s.name(db),
719 Adt::Union(u) => u.name(db),
720 Adt::Enum(e) => e.name(db),
721 }
722 }
723}
724
725#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
726pub enum VariantDef {
727 Struct(Struct),
728 Union(Union),
729 Variant(Variant),
730}
731impl_from!(Struct, Union, Variant for VariantDef);
732
733impl VariantDef {
734 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
735 match self {
736 VariantDef::Struct(it) => it.fields(db),
737 VariantDef::Union(it) => it.fields(db),
738 VariantDef::Variant(it) => it.fields(db),
739 }
740 }
741
742 pub fn module(self, db: &dyn HirDatabase) -> Module {
743 match self {
744 VariantDef::Struct(it) => it.module(db),
745 VariantDef::Union(it) => it.module(db),
746 VariantDef::Variant(it) => it.module(db),
747 }
748 }
749
750 pub fn name(&self, db: &dyn HirDatabase) -> Name {
751 match self {
752 VariantDef::Struct(s) => s.name(db),
753 VariantDef::Union(u) => u.name(db),
754 VariantDef::Variant(e) => e.name(db),
755 }
756 }
757
758 pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
759 match self {
760 VariantDef::Struct(it) => it.variant_data(db),
761 VariantDef::Union(it) => it.variant_data(db),
762 VariantDef::Variant(it) => it.variant_data(db),
763 }
764 }
765}
766
767/// The defs which have a body.
768#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
769pub enum DefWithBody {
770 Function(Function),
771 Static(Static),
772 Const(Const),
773}
774impl_from!(Function, Const, Static for DefWithBody);
775
776impl DefWithBody {
777 pub fn module(self, db: &dyn HirDatabase) -> Module {
778 match self {
779 DefWithBody::Const(c) => c.module(db),
780 DefWithBody::Function(f) => f.module(db),
781 DefWithBody::Static(s) => s.module(db),
782 }
783 }
784
785 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
786 match self {
787 DefWithBody::Function(f) => Some(f.name(db)),
788 DefWithBody::Static(s) => s.name(db),
789 DefWithBody::Const(c) => c.name(db),
790 }
791 }
792}
793
794#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
795pub struct Function {
796 pub(crate) id: FunctionId,
797}
798
799impl Function {
800 pub fn module(self, db: &dyn HirDatabase) -> Module {
801 self.id.lookup(db.upcast()).module(db.upcast()).into()
802 }
803
804 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
805 Some(self.module(db).krate())
806 }
807
808 pub fn name(self, db: &dyn HirDatabase) -> Name {
809 db.function_data(self.id).name.clone()
810 }
811
812 /// Get this function's return type
813 pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
814 let resolver = self.id.resolver(db.upcast());
815 let ret_type = &db.function_data(self.id).ret_type;
816 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
817 let environment = TraitEnvironment::lower(db, &resolver);
818 Type {
819 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
820 ty: InEnvironment { value: Ty::from_hir_ext(&ctx, ret_type).0, environment },
821 }
822 }
823
824 pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
825 if !db.function_data(self.id).has_self_param {
826 return None;
827 }
828 Some(SelfParam { func: self.id })
829 }
830
831 pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> {
832 let resolver = self.id.resolver(db.upcast());
833 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
834 let environment = TraitEnvironment::lower(db, &resolver);
835 db.function_data(self.id)
836 .params
837 .iter()
838 .map(|type_ref| {
839 let ty = Type {
840 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
841 ty: InEnvironment {
842 value: Ty::from_hir_ext(&ctx, type_ref).0,
843 environment: environment.clone(),
844 },
845 };
846 Param { ty }
847 })
848 .collect()
849 }
850 pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
851 if self.self_param(db).is_none() {
852 return None;
853 }
854 let mut res = self.assoc_fn_params(db);
855 res.remove(0);
856 Some(res)
857 }
858
859 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
860 db.function_data(self.id).is_unsafe
861 }
862
863 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
864 let krate = self.module(db).id.krate();
865 hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink);
866 hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink);
867 hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
868 }
869
870 /// Whether this function declaration has a definition.
871 ///
872 /// This is false in the case of required (not provided) trait methods.
873 pub fn has_body(self, db: &dyn HirDatabase) -> bool {
874 db.function_data(self.id).has_body
875 }
876
877 /// A textual representation of the HIR of this function for debugging purposes.
878 pub fn debug_hir(self, db: &dyn HirDatabase) -> String {
879 let body = db.body(self.id.into());
880
881 let mut result = String::new();
882 format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db));
883 for (id, expr) in body.exprs.iter() {
884 format_to!(result, "{:?}: {:?}\n", id, expr);
885 }
886
887 result
888 }
889}
890
891// Note: logically, this belongs to `hir_ty`, but we are not using it there yet.
892pub enum Access {
893 Shared,
894 Exclusive,
895 Owned,
896}
897
898impl From<hir_ty::Mutability> for Access {
899 fn from(mutability: hir_ty::Mutability) -> Access {
900 match mutability {
901 hir_ty::Mutability::Not => Access::Shared,
902 hir_ty::Mutability::Mut => Access::Exclusive,
903 }
904 }
905}
906
907#[derive(Debug)]
908pub struct Param {
909 ty: Type,
910}
911
912impl Param {
913 pub fn ty(&self) -> &Type {
914 &self.ty
915 }
916}
917
918#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
919pub struct SelfParam {
920 func: FunctionId,
921}
922
923impl SelfParam {
924 pub fn access(self, db: &dyn HirDatabase) -> Access {
925 let func_data = db.function_data(self.func);
926 func_data
927 .params
928 .first()
929 .map(|param| match *param {
930 TypeRef::Reference(.., mutability) => match mutability {
931 hir_def::type_ref::Mutability::Shared => Access::Shared,
932 hir_def::type_ref::Mutability::Mut => Access::Exclusive,
933 },
934 _ => Access::Owned,
935 })
936 .unwrap_or(Access::Owned)
937 }
938}
939
940impl HasVisibility for Function {
941 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
942 let function_data = db.function_data(self.id);
943 let visibility = &function_data.visibility;
944 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
945 }
946}
947
948#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
949pub struct Const {
950 pub(crate) id: ConstId,
951}
952
953impl Const {
954 pub fn module(self, db: &dyn HirDatabase) -> Module {
955 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
956 }
957
958 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
959 Some(self.module(db).krate())
960 }
961
962 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
963 db.const_data(self.id).name.clone()
964 }
965}
966
967impl HasVisibility for Const {
968 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
969 let function_data = db.const_data(self.id);
970 let visibility = &function_data.visibility;
971 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
972 }
973}
974
975#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
976pub struct Static {
977 pub(crate) id: StaticId,
978}
979
980impl Static {
981 pub fn module(self, db: &dyn HirDatabase) -> Module {
982 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
983 }
984
985 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
986 Some(self.module(db).krate())
987 }
988
989 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
990 db.static_data(self.id).name.clone()
991 }
992
993 pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
994 db.static_data(self.id).mutable
995 }
996}
997
998#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
999pub struct Trait {
1000 pub(crate) id: TraitId,
1001}
1002
1003impl Trait {
1004 pub fn module(self, db: &dyn HirDatabase) -> Module {
1005 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) }
1006 }
1007
1008 pub fn name(self, db: &dyn HirDatabase) -> Name {
1009 db.trait_data(self.id).name.clone()
1010 }
1011
1012 pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
1013 db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect()
1014 }
1015
1016 pub fn is_auto(self, db: &dyn HirDatabase) -> bool {
1017 db.trait_data(self.id).auto
1018 }
1019}
1020
1021#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1022pub struct TypeAlias {
1023 pub(crate) id: TypeAliasId,
1024}
1025
1026impl TypeAlias {
1027 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
1028 let subst = db.generic_defaults(self.id.into());
1029 subst.iter().any(|ty| &ty.value == &Ty::Unknown)
1030 }
1031
1032 pub fn module(self, db: &dyn HirDatabase) -> Module {
1033 Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
1034 }
1035
1036 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
1037 Some(self.module(db).krate())
1038 }
1039
1040 pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> {
1041 db.type_alias_data(self.id).type_ref.clone()
1042 }
1043
1044 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1045 Type::from_def(db, self.id.lookup(db.upcast()).module(db.upcast()).krate(), self.id)
1046 }
1047
1048 pub fn name(self, db: &dyn HirDatabase) -> Name {
1049 db.type_alias_data(self.id).name.clone()
1050 }
1051}
1052
1053impl HasVisibility for TypeAlias {
1054 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
1055 let function_data = db.type_alias_data(self.id);
1056 let visibility = &function_data.visibility;
1057 visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
1058 }
1059}
1060
1061#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1062pub struct BuiltinType {
1063 pub(crate) inner: hir_def::builtin_type::BuiltinType,
1064}
1065
1066impl BuiltinType {
1067 pub fn ty(self, db: &dyn HirDatabase, module: Module) -> Type {
1068 let resolver = module.id.resolver(db.upcast());
1069 Type::new_with_resolver(db, &resolver, Ty::builtin(self.inner))
1070 .expect("crate not present in resolver")
1071 }
1072
1073 pub fn name(self) -> Name {
1074 self.inner.as_name()
1075 }
1076}
1077
1078#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1079pub struct MacroDef {
1080 pub(crate) id: MacroDefId,
1081}
1082
1083impl MacroDef {
1084 /// FIXME: right now, this just returns the root module of the crate that
1085 /// defines this macro. The reasons for this is that macros are expanded
1086 /// early, in `hir_expand`, where modules simply do not exist yet.
1087 pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
1088 let krate = self.id.krate;
1089 let def_map = db.crate_def_map(krate);
1090 let module_id = def_map.root();
1091 Some(Module { id: def_map.module_id(module_id) })
1092 }
1093
1094 /// XXX: this parses the file
1095 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1096 self.source(db)?.value.name().map(|it| it.as_name())
1097 }
1098
1099 /// Indicate it is a proc-macro
1100 pub fn is_proc_macro(&self) -> bool {
1101 matches!(self.id.kind, MacroDefKind::ProcMacro(_))
1102 }
1103
1104 /// Indicate it is a derive macro
1105 pub fn is_derive_macro(&self) -> bool {
1106 matches!(self.id.kind, MacroDefKind::ProcMacro(_) | MacroDefKind::BuiltInDerive(_))
1107 }
1108}
1109
1110/// Invariant: `inner.as_assoc_item(db).is_some()`
1111/// We do not actively enforce this invariant.
1112#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1113pub enum AssocItem {
1114 Function(Function),
1115 Const(Const),
1116 TypeAlias(TypeAlias),
1117}
1118pub enum AssocItemContainer {
1119 Trait(Trait),
1120 Impl(Impl),
1121}
1122pub trait AsAssocItem {
1123 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem>;
1124}
1125
1126impl AsAssocItem for Function {
1127 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1128 as_assoc_item(db, AssocItem::Function, self.id)
1129 }
1130}
1131impl AsAssocItem for Const {
1132 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1133 as_assoc_item(db, AssocItem::Const, self.id)
1134 }
1135}
1136impl AsAssocItem for TypeAlias {
1137 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1138 as_assoc_item(db, AssocItem::TypeAlias, self.id)
1139 }
1140}
1141impl AsAssocItem for ModuleDef {
1142 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1143 match self {
1144 ModuleDef::Function(it) => it.as_assoc_item(db),
1145 ModuleDef::Const(it) => it.as_assoc_item(db),
1146 ModuleDef::TypeAlias(it) => it.as_assoc_item(db),
1147 _ => None,
1148 }
1149 }
1150}
1151fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem>
1152where
1153 ID: Lookup<Data = AssocItemLoc<AST>>,
1154 DEF: From<ID>,
1155 CTOR: FnOnce(DEF) -> AssocItem,
1156 AST: ItemTreeNode,
1157{
1158 match id.lookup(db.upcast()).container {
1159 AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
1160 AssocContainerId::ContainerId(_) => None,
1161 }
1162}
1163
1164impl AssocItem {
1165 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1166 match self {
1167 AssocItem::Function(it) => Some(it.name(db)),
1168 AssocItem::Const(it) => it.name(db),
1169 AssocItem::TypeAlias(it) => Some(it.name(db)),
1170 }
1171 }
1172 pub fn module(self, db: &dyn HirDatabase) -> Module {
1173 match self {
1174 AssocItem::Function(f) => f.module(db),
1175 AssocItem::Const(c) => c.module(db),
1176 AssocItem::TypeAlias(t) => t.module(db),
1177 }
1178 }
1179 pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer {
1180 let container = match self {
1181 AssocItem::Function(it) => it.id.lookup(db.upcast()).container,
1182 AssocItem::Const(it) => it.id.lookup(db.upcast()).container,
1183 AssocItem::TypeAlias(it) => it.id.lookup(db.upcast()).container,
1184 };
1185 match container {
1186 AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()),
1187 AssocContainerId::ImplId(id) => AssocItemContainer::Impl(id.into()),
1188 AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"),
1189 }
1190 }
1191
1192 pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> {
1193 match self.container(db) {
1194 AssocItemContainer::Trait(t) => Some(t),
1195 _ => None,
1196 }
1197 }
1198}
1199
1200impl HasVisibility for AssocItem {
1201 fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
1202 match self {
1203 AssocItem::Function(f) => f.visibility(db),
1204 AssocItem::Const(c) => c.visibility(db),
1205 AssocItem::TypeAlias(t) => t.visibility(db),
1206 }
1207 }
1208}
1209
1210#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
1211pub enum GenericDef {
1212 Function(Function),
1213 Adt(Adt),
1214 Trait(Trait),
1215 TypeAlias(TypeAlias),
1216 Impl(Impl),
1217 // enum variants cannot have generics themselves, but their parent enums
1218 // can, and this makes some code easier to write
1219 Variant(Variant),
1220 // consts can have type parameters from their parents (i.e. associated consts of traits)
1221 Const(Const),
1222}
1223impl_from!(
1224 Function,
1225 Adt(Struct, Enum, Union),
1226 Trait,
1227 TypeAlias,
1228 Impl,
1229 Variant,
1230 Const
1231 for GenericDef
1232);
1233
1234impl GenericDef {
1235 pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> {
1236 let generics = db.generic_params(self.into());
1237 let ty_params = generics
1238 .types
1239 .iter()
1240 .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } })
1241 .map(GenericParam::TypeParam);
1242 let lt_params = generics
1243 .lifetimes
1244 .iter()
1245 .map(|(local_id, _)| LifetimeParam {
1246 id: LifetimeParamId { parent: self.into(), local_id },
1247 })
1248 .map(GenericParam::LifetimeParam);
1249 let const_params = generics
1250 .consts
1251 .iter()
1252 .map(|(local_id, _)| ConstParam { id: ConstParamId { parent: self.into(), local_id } })
1253 .map(GenericParam::ConstParam);
1254 ty_params.chain(lt_params).chain(const_params).collect()
1255 }
1256
1257 pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> {
1258 let generics = db.generic_params(self.into());
1259 generics
1260 .types
1261 .iter()
1262 .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } })
1263 .collect()
1264 }
1265}
1266
1267#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1268pub struct Local {
1269 pub(crate) parent: DefWithBodyId,
1270 pub(crate) pat_id: PatId,
1271}
1272
1273impl Local {
1274 pub fn is_param(self, db: &dyn HirDatabase) -> bool {
1275 let src = self.source(db);
1276 match src.value {
1277 Either::Left(bind_pat) => {
1278 bind_pat.syntax().ancestors().any(|it| ast::Param::can_cast(it.kind()))
1279 }
1280 Either::Right(_self_param) => true,
1281 }
1282 }
1283
1284 // FIXME: why is this an option? It shouldn't be?
1285 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
1286 let body = db.body(self.parent.into());
1287 match &body[self.pat_id] {
1288 Pat::Bind { name, .. } => Some(name.clone()),
1289 _ => None,
1290 }
1291 }
1292
1293 pub fn is_self(self, db: &dyn HirDatabase) -> bool {
1294 self.name(db) == Some(name![self])
1295 }
1296
1297 pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
1298 let body = db.body(self.parent.into());
1299 match &body[self.pat_id] {
1300 Pat::Bind { mode, .. } => match mode {
1301 BindingAnnotation::Mutable | BindingAnnotation::RefMut => true,
1302 _ => false,
1303 },
1304 _ => false,
1305 }
1306 }
1307
1308 pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
1309 self.parent.into()
1310 }
1311
1312 pub fn module(self, db: &dyn HirDatabase) -> Module {
1313 self.parent(db).module(db)
1314 }
1315
1316 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1317 let def = DefWithBodyId::from(self.parent);
1318 let infer = db.infer(def);
1319 let ty = infer[self.pat_id].clone();
1320 let krate = def.module(db.upcast()).krate();
1321 Type::new(db, krate, def, ty)
1322 }
1323
1324 pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> {
1325 let (_body, source_map) = db.body_with_source_map(self.parent.into());
1326 let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
1327 let root = src.file_syntax(db.upcast());
1328 src.map(|ast| {
1329 ast.map_left(|it| it.cast().unwrap().to_node(&root)).map_right(|it| it.to_node(&root))
1330 })
1331 }
1332}
1333
1334#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1335pub struct Label {
1336 pub(crate) parent: DefWithBodyId,
1337 pub(crate) label_id: LabelId,
1338}
1339
1340impl Label {
1341 pub fn module(self, db: &dyn HirDatabase) -> Module {
1342 self.parent(db).module(db)
1343 }
1344
1345 pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
1346 self.parent.into()
1347 }
1348
1349 pub fn name(self, db: &dyn HirDatabase) -> Name {
1350 let body = db.body(self.parent.into());
1351 body[self.label_id].name.clone()
1352 }
1353
1354 pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> {
1355 let (_body, source_map) = db.body_with_source_map(self.parent.into());
1356 let src = source_map.label_syntax(self.label_id);
1357 let root = src.file_syntax(db.upcast());
1358 src.map(|ast| ast.to_node(&root))
1359 }
1360}
1361
1362#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1363pub enum GenericParam {
1364 TypeParam(TypeParam),
1365 LifetimeParam(LifetimeParam),
1366 ConstParam(ConstParam),
1367}
1368impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam);
1369
1370impl GenericParam {
1371 pub fn module(self, db: &dyn HirDatabase) -> Module {
1372 match self {
1373 GenericParam::TypeParam(it) => it.module(db),
1374 GenericParam::LifetimeParam(it) => it.module(db),
1375 GenericParam::ConstParam(it) => it.module(db),
1376 }
1377 }
1378
1379 pub fn name(self, db: &dyn HirDatabase) -> Name {
1380 match self {
1381 GenericParam::TypeParam(it) => it.name(db),
1382 GenericParam::LifetimeParam(it) => it.name(db),
1383 GenericParam::ConstParam(it) => it.name(db),
1384 }
1385 }
1386}
1387
1388#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1389pub struct TypeParam {
1390 pub(crate) id: TypeParamId,
1391}
1392
1393impl TypeParam {
1394 pub fn name(self, db: &dyn HirDatabase) -> Name {
1395 let params = db.generic_params(self.id.parent);
1396 params.types[self.id.local_id].name.clone().unwrap_or_else(Name::missing)
1397 }
1398
1399 pub fn module(self, db: &dyn HirDatabase) -> Module {
1400 self.id.parent.module(db.upcast()).into()
1401 }
1402
1403 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1404 let resolver = self.id.parent.resolver(db.upcast());
1405 let environment = TraitEnvironment::lower(db, &resolver);
1406 let ty = Ty::Placeholder(self.id);
1407 Type {
1408 krate: self.id.parent.module(db.upcast()).krate(),
1409 ty: InEnvironment { value: ty, environment },
1410 }
1411 }
1412
1413 pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
1414 db.generic_predicates_for_param(self.id)
1415 .into_iter()
1416 .filter_map(|pred| match &pred.value {
1417 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1418 Some(Trait::from(trait_ref.trait_))
1419 }
1420 _ => None,
1421 })
1422 .collect()
1423 }
1424
1425 pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
1426 let params = db.generic_defaults(self.id.parent);
1427 let local_idx = hir_ty::param_idx(db, self.id)?;
1428 let resolver = self.id.parent.resolver(db.upcast());
1429 let environment = TraitEnvironment::lower(db, &resolver);
1430 let ty = params.get(local_idx)?.clone();
1431 let subst = Substs::type_params(db, self.id.parent);
1432 let ty = ty.subst(&subst.prefix(local_idx));
1433 Some(Type {
1434 krate: self.id.parent.module(db.upcast()).krate(),
1435 ty: InEnvironment { value: ty, environment },
1436 })
1437 }
1438}
1439
1440impl HirDisplay for TypeParam {
1441 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
1442 write!(f, "{}", self.name(f.db))?;
1443 let bounds = f.db.generic_predicates_for_param(self.id);
1444 let substs = Substs::type_params(f.db, self.id.parent);
1445 let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>();
1446 if !(predicates.is_empty() || f.omit_verbose_types()) {
1447 write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?;
1448 }
1449 Ok(())
1450 }
1451}
1452
1453#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1454pub struct LifetimeParam {
1455 pub(crate) id: LifetimeParamId,
1456}
1457
1458impl LifetimeParam {
1459 pub fn name(self, db: &dyn HirDatabase) -> Name {
1460 let params = db.generic_params(self.id.parent);
1461 params.lifetimes[self.id.local_id].name.clone()
1462 }
1463
1464 pub fn module(self, db: &dyn HirDatabase) -> Module {
1465 self.id.parent.module(db.upcast()).into()
1466 }
1467
1468 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1469 self.id.parent.into()
1470 }
1471}
1472
1473#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1474pub struct ConstParam {
1475 pub(crate) id: ConstParamId,
1476}
1477
1478impl ConstParam {
1479 pub fn name(self, db: &dyn HirDatabase) -> Name {
1480 let params = db.generic_params(self.id.parent);
1481 params.consts[self.id.local_id].name.clone()
1482 }
1483
1484 pub fn module(self, db: &dyn HirDatabase) -> Module {
1485 self.id.parent.module(db.upcast()).into()
1486 }
1487
1488 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1489 self.id.parent.into()
1490 }
1491
1492 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1493 let def = self.id.parent;
1494 let krate = def.module(db.upcast()).krate();
1495 Type::new(db, krate, def, db.const_param_ty(self.id))
1496 }
1497}
1498
1499#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1500pub struct Impl {
1501 pub(crate) id: ImplId,
1502}
1503
1504impl Impl {
1505 pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<Impl> {
1506 let inherent = db.inherent_impls_in_crate(krate.id);
1507 let trait_ = db.trait_impls_in_crate(krate.id);
1508
1509 inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect()
1510 }
1511 pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<Impl> {
1512 let impls = db.trait_impls_in_crate(krate.id);
1513 impls.for_trait(trait_.id).map(Self::from).collect()
1514 }
1515
1516 // FIXME: the return type is wrong. This should be a hir version of
1517 // `TraitRef` (ie, resolved `TypeRef`).
1518 pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> {
1519 db.impl_data(self.id).target_trait.clone()
1520 }
1521
1522 pub fn target_ty(self, db: &dyn HirDatabase) -> Type {
1523 let impl_data = db.impl_data(self.id);
1524 let resolver = self.id.resolver(db.upcast());
1525 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
1526 let environment = TraitEnvironment::lower(db, &resolver);
1527 let ty = Ty::from_hir(&ctx, &impl_data.target_type);
1528 Type {
1529 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
1530 ty: InEnvironment { value: ty, environment },
1531 }
1532 }
1533
1534 pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
1535 db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect()
1536 }
1537
1538 pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
1539 db.impl_data(self.id).is_negative
1540 }
1541
1542 pub fn module(self, db: &dyn HirDatabase) -> Module {
1543 self.id.lookup(db.upcast()).container.module(db.upcast()).into()
1544 }
1545
1546 pub fn krate(self, db: &dyn HirDatabase) -> Crate {
1547 Crate { id: self.module(db).id.krate() }
1548 }
1549
1550 pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> {
1551 let src = self.source(db)?;
1552 let item = src.file_id.is_builtin_derive(db.upcast())?;
1553 let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id);
1554
1555 // FIXME: handle `cfg_attr`
1556 let attr = item
1557 .value
1558 .attrs()
1559 .filter_map(|it| {
1560 let path = ModPath::from_src(it.path()?, &hygenic)?;
1561 if path.as_ident()?.to_string() == "derive" {
1562 Some(it)
1563 } else {
1564 None
1565 }
1566 })
1567 .last()?;
1568
1569 Some(item.with_value(attr))
1570 }
1571}
1572
1573#[derive(Clone, PartialEq, Eq, Debug)]
1574pub struct Type {
1575 krate: CrateId,
1576 ty: InEnvironment<Ty>,
1577}
1578
1579impl Type {
1580 pub(crate) fn new_with_resolver(
1581 db: &dyn HirDatabase,
1582 resolver: &Resolver,
1583 ty: Ty,
1584 ) -> Option<Type> {
1585 let krate = resolver.krate()?;
1586 Some(Type::new_with_resolver_inner(db, krate, resolver, ty))
1587 }
1588 pub(crate) fn new_with_resolver_inner(
1589 db: &dyn HirDatabase,
1590 krate: CrateId,
1591 resolver: &Resolver,
1592 ty: Ty,
1593 ) -> Type {
1594 let environment = TraitEnvironment::lower(db, &resolver);
1595 Type { krate, ty: InEnvironment { value: ty, environment } }
1596 }
1597
1598 fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type {
1599 let resolver = lexical_env.resolver(db.upcast());
1600 let environment = TraitEnvironment::lower(db, &resolver);
1601 Type { krate, ty: InEnvironment { value: ty, environment } }
1602 }
1603
1604 fn from_def(
1605 db: &dyn HirDatabase,
1606 krate: CrateId,
1607 def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>,
1608 ) -> Type {
1609 let substs = Substs::build_for_def(db, def).fill_with_unknown().build();
1610 let ty = db.ty(def.into()).subst(&substs);
1611 Type::new(db, krate, def, ty)
1612 }
1613
1614 pub fn is_unit(&self) -> bool {
1615 matches!(self.ty.value, Ty::Tuple(0, ..))
1616 }
1617 pub fn is_bool(&self) -> bool {
1618 matches!(self.ty.value, Ty::Scalar(Scalar::Bool))
1619 }
1620
1621 pub fn is_mutable_reference(&self) -> bool {
1622 matches!(self.ty.value, Ty::Ref(hir_ty::Mutability::Mut, ..))
1623 }
1624
1625 pub fn remove_ref(&self) -> Option<Type> {
1626 if let Ty::Ref(.., substs) = &self.ty.value {
1627 Some(self.derived(substs[0].clone()))
1628 } else {
1629 None
1630 }
1631 }
1632
1633 pub fn is_unknown(&self) -> bool {
1634 matches!(self.ty.value, Ty::Unknown)
1635 }
1636
1637 /// Checks that particular type `ty` implements `std::future::Future`.
1638 /// This function is used in `.await` syntax completion.
1639 pub fn impls_future(&self, db: &dyn HirDatabase) -> bool {
1640 // No special case for the type of async block, since Chalk can figure it out.
1641
1642 let krate = self.krate;
1643
1644 let std_future_trait =
1645 db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait());
1646 let std_future_trait = match std_future_trait {
1647 Some(it) => it,
1648 None => return false,
1649 };
1650
1651 let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1652 method_resolution::implements_trait(
1653 &canonical_ty,
1654 db,
1655 self.ty.environment.clone(),
1656 krate,
1657 std_future_trait,
1658 )
1659 }
1660
1661 /// Checks that particular type `ty` implements `std::ops::FnOnce`.
1662 ///
1663 /// This function can be used to check if a particular type is callable, since FnOnce is a
1664 /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce.
1665 pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool {
1666 let krate = self.krate;
1667
1668 let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) {
1669 Some(it) => it,
1670 None => return false,
1671 };
1672
1673 let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1674 method_resolution::implements_trait_unique(
1675 &canonical_ty,
1676 db,
1677 self.ty.environment.clone(),
1678 krate,
1679 fnonce_trait,
1680 )
1681 }
1682
1683 pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool {
1684 let trait_ref = hir_ty::TraitRef {
1685 trait_: trait_.id,
1686 substs: Substs::build_for_def(db, trait_.id)
1687 .push(self.ty.value.clone())
1688 .fill(args.iter().map(|t| t.ty.value.clone()))
1689 .build(),
1690 };
1691
1692 let goal = Canonical {
1693 value: hir_ty::InEnvironment::new(
1694 self.ty.environment.clone(),
1695 hir_ty::Obligation::Trait(trait_ref),
1696 ),
1697 kinds: Arc::new([]),
1698 };
1699
1700 db.trait_solve(self.krate, goal).is_some()
1701 }
1702
1703 pub fn normalize_trait_assoc_type(
1704 &self,
1705 db: &dyn HirDatabase,
1706 trait_: Trait,
1707 args: &[Type],
1708 alias: TypeAlias,
1709 ) -> Option<Type> {
1710 let subst = Substs::build_for_def(db, trait_.id)
1711 .push(self.ty.value.clone())
1712 .fill(args.iter().map(|t| t.ty.value.clone()))
1713 .build();
1714 let predicate = ProjectionPredicate {
1715 projection_ty: ProjectionTy { associated_ty: alias.id, parameters: subst },
1716 ty: Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)),
1717 };
1718 let goal = Canonical {
1719 value: InEnvironment::new(
1720 self.ty.environment.clone(),
1721 Obligation::Projection(predicate),
1722 ),
1723 kinds: Arc::new([TyVariableKind::General]),
1724 };
1725
1726 match db.trait_solve(self.krate, goal)? {
1727 Solution::Unique(SolutionVariables(subst)) => subst.value.first().cloned(),
1728 Solution::Ambig(_) => None,
1729 }
1730 .map(|ty| Type {
1731 krate: self.krate,
1732 ty: InEnvironment { value: ty, environment: Arc::clone(&self.ty.environment) },
1733 })
1734 }
1735
1736 pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
1737 let lang_item = db.lang_item(self.krate, SmolStr::new("copy"));
1738 let copy_trait = match lang_item {
1739 Some(LangItemTarget::TraitId(it)) => it,
1740 _ => return false,
1741 };
1742 self.impls_trait(db, copy_trait.into(), &[])
1743 }
1744
1745 pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
1746 let def = match self.ty.value {
1747 Ty::FnDef(def, _) => Some(def),
1748 _ => None,
1749 };
1750
1751 let sig = self.ty.value.callable_sig(db)?;
1752 Some(Callable { ty: self.clone(), sig, def, is_bound_method: false })
1753 }
1754
1755 pub fn is_closure(&self) -> bool {
1756 matches!(&self.ty.value, Ty::Closure { .. })
1757 }
1758
1759 pub fn is_fn(&self) -> bool {
1760 matches!(&self.ty.value, Ty::FnDef(..) | Ty::Function { .. })
1761 }
1762
1763 pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
1764 let adt_id = match self.ty.value {
1765 Ty::Adt(hir_ty::AdtId(adt_id), ..) => adt_id,
1766 _ => return false,
1767 };
1768
1769 let adt = adt_id.into();
1770 match adt {
1771 Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)),
1772 _ => false,
1773 }
1774 }
1775
1776 pub fn is_raw_ptr(&self) -> bool {
1777 matches!(&self.ty.value, Ty::Raw(..))
1778 }
1779
1780 pub fn contains_unknown(&self) -> bool {
1781 return go(&self.ty.value);
1782
1783 fn go(ty: &Ty) -> bool {
1784 match ty {
1785 Ty::Unknown => true,
1786 _ => ty.substs().map_or(false, |substs| substs.iter().any(go)),
1787 }
1788 }
1789 }
1790
1791 pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> {
1792 let (variant_id, substs) = match self.ty.value {
1793 Ty::Adt(hir_ty::AdtId(AdtId::StructId(s)), ref substs) => (s.into(), substs),
1794 Ty::Adt(hir_ty::AdtId(AdtId::UnionId(u)), ref substs) => (u.into(), substs),
1795 _ => return Vec::new(),
1796 };
1797
1798 db.field_types(variant_id)
1799 .iter()
1800 .map(|(local_id, ty)| {
1801 let def = Field { parent: variant_id.into(), id: local_id };
1802 let ty = ty.clone().subst(substs);
1803 (def, self.derived(ty))
1804 })
1805 .collect()
1806 }
1807
1808 pub fn tuple_fields(&self, _db: &dyn HirDatabase) -> Vec<Type> {
1809 if let Ty::Tuple(_, substs) = &self.ty.value {
1810 substs.iter().map(|ty| self.derived(ty.clone())).collect()
1811 } else {
1812 Vec::new()
1813 }
1814 }
1815
1816 pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
1817 // There should be no inference vars in types passed here
1818 // FIXME check that?
1819 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1820 let environment = self.ty.environment.clone();
1821 let ty = InEnvironment { value: canonical, environment };
1822 autoderef(db, Some(self.krate), ty)
1823 .map(|canonical| canonical.value)
1824 .map(move |ty| self.derived(ty))
1825 }
1826
1827 // This would be nicer if it just returned an iterator, but that runs into
1828 // lifetime problems, because we need to borrow temp `CrateImplDefs`.
1829 pub fn iterate_assoc_items<T>(
1830 self,
1831 db: &dyn HirDatabase,
1832 krate: Crate,
1833 mut callback: impl FnMut(AssocItem) -> Option<T>,
1834 ) -> Option<T> {
1835 for krate in self.ty.value.def_crates(db, krate.id)? {
1836 let impls = db.inherent_impls_in_crate(krate);
1837
1838 for impl_def in impls.for_self_ty(&self.ty.value) {
1839 for &item in db.impl_data(*impl_def).items.iter() {
1840 if let Some(result) = callback(item.into()) {
1841 return Some(result);
1842 }
1843 }
1844 }
1845 }
1846 None
1847 }
1848
1849 pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ {
1850 self.ty
1851 .value
1852 .strip_references()
1853 .substs()
1854 .into_iter()
1855 .flat_map(|substs| substs.iter())
1856 .map(move |ty| self.derived(ty.clone()))
1857 }
1858
1859 pub fn iterate_method_candidates<T>(
1860 &self,
1861 db: &dyn HirDatabase,
1862 krate: Crate,
1863 traits_in_scope: &FxHashSet<TraitId>,
1864 name: Option<&Name>,
1865 mut callback: impl FnMut(&Ty, Function) -> Option<T>,
1866 ) -> Option<T> {
1867 // There should be no inference vars in types passed here
1868 // FIXME check that?
1869 // FIXME replace Unknown by bound vars here
1870 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1871
1872 let env = self.ty.environment.clone();
1873 let krate = krate.id;
1874
1875 method_resolution::iterate_method_candidates(
1876 &canonical,
1877 db,
1878 env,
1879 krate,
1880 traits_in_scope,
1881 name,
1882 method_resolution::LookupMode::MethodCall,
1883 |ty, it| match it {
1884 AssocItemId::FunctionId(f) => callback(ty, f.into()),
1885 _ => None,
1886 },
1887 )
1888 }
1889
1890 pub fn iterate_path_candidates<T>(
1891 &self,
1892 db: &dyn HirDatabase,
1893 krate: Crate,
1894 traits_in_scope: &FxHashSet<TraitId>,
1895 name: Option<&Name>,
1896 mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
1897 ) -> Option<T> {
1898 // There should be no inference vars in types passed here
1899 // FIXME check that?
1900 // FIXME replace Unknown by bound vars here
1901 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1902
1903 let env = self.ty.environment.clone();
1904 let krate = krate.id;
1905
1906 method_resolution::iterate_method_candidates(
1907 &canonical,
1908 db,
1909 env,
1910 krate,
1911 traits_in_scope,
1912 name,
1913 method_resolution::LookupMode::Path,
1914 |ty, it| callback(ty, it.into()),
1915 )
1916 }
1917
1918 pub fn as_adt(&self) -> Option<Adt> {
1919 let (adt, _subst) = self.ty.value.as_adt()?;
1920 Some(adt.into())
1921 }
1922
1923 pub fn as_dyn_trait(&self) -> Option<Trait> {
1924 self.ty.value.dyn_trait().map(Into::into)
1925 }
1926
1927 pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> {
1928 self.ty.value.impl_trait_bounds(db).map(|it| {
1929 it.into_iter()
1930 .filter_map(|pred| match pred {
1931 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1932 Some(Trait::from(trait_ref.trait_))
1933 }
1934 _ => None,
1935 })
1936 .collect()
1937 })
1938 }
1939
1940 pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> {
1941 self.ty.value.associated_type_parent_trait(db).map(Into::into)
1942 }
1943
1944 // FIXME: provide required accessors such that it becomes implementable from outside.
1945 pub fn is_equal_for_find_impls(&self, other: &Type) -> bool {
1946 let rref = other.remove_ref();
1947 self.ty.value.equals_ctor(rref.as_ref().map_or(&other.ty.value, |it| &it.ty.value))
1948 }
1949
1950 fn derived(&self, ty: Ty) -> Type {
1951 Type {
1952 krate: self.krate,
1953 ty: InEnvironment { value: ty, environment: self.ty.environment.clone() },
1954 }
1955 }
1956
1957 pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) {
1958 // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself.
1959 // We need a different order here.
1960
1961 fn walk_substs(
1962 db: &dyn HirDatabase,
1963 type_: &Type,
1964 substs: &Substs,
1965 cb: &mut impl FnMut(Type),
1966 ) {
1967 for ty in substs.iter() {
1968 walk_type(db, &type_.derived(ty.clone()), cb);
1969 }
1970 }
1971
1972 fn walk_bounds(
1973 db: &dyn HirDatabase,
1974 type_: &Type,
1975 bounds: &[GenericPredicate],
1976 cb: &mut impl FnMut(Type),
1977 ) {
1978 for pred in bounds {
1979 match pred {
1980 GenericPredicate::Implemented(trait_ref) => {
1981 cb(type_.clone());
1982 walk_substs(db, type_, &trait_ref.substs, cb);
1983 }
1984 _ => (),
1985 }
1986 }
1987 }
1988
1989 fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
1990 let ty = type_.ty.value.strip_references();
1991 match ty {
1992 Ty::Adt(..) => {
1993 cb(type_.derived(ty.clone()));
1994 }
1995 Ty::AssociatedType(..) => {
1996 if let Some(_) = ty.associated_type_parent_trait(db) {
1997 cb(type_.derived(ty.clone()));
1998 }
1999 }
2000 Ty::OpaqueType(..) => {
2001 if let Some(bounds) = ty.impl_trait_bounds(db) {
2002 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
2003 }
2004 }
2005 Ty::Alias(AliasTy::Opaque(opaque_ty)) => {
2006 if let Some(bounds) = ty.impl_trait_bounds(db) {
2007 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
2008 }
2009
2010 walk_substs(db, type_, &opaque_ty.parameters, cb);
2011 }
2012 Ty::Placeholder(_) => {
2013 if let Some(bounds) = ty.impl_trait_bounds(db) {
2014 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
2015 }
2016 }
2017 Ty::Dyn(bounds) => {
2018 walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb);
2019 }
2020
2021 _ => {}
2022 }
2023 if let Some(substs) = ty.substs() {
2024 walk_substs(db, type_, &substs, cb);
2025 }
2026 }
2027
2028 walk_type(db, self, &mut cb);
2029 }
2030}
2031
2032impl HirDisplay for Type {
2033 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
2034 self.ty.value.hir_fmt(f)
2035 }
2036}
2037
2038// FIXME: closures
2039#[derive(Debug)]
2040pub struct Callable {
2041 ty: Type,
2042 sig: CallableSig,
2043 def: Option<CallableDefId>,
2044 pub(crate) is_bound_method: bool,
2045}
2046
2047pub enum CallableKind {
2048 Function(Function),
2049 TupleStruct(Struct),
2050 TupleEnumVariant(Variant),
2051 Closure,
2052}
2053
2054impl Callable {
2055 pub fn kind(&self) -> CallableKind {
2056 match self.def {
2057 Some(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()),
2058 Some(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()),
2059 Some(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()),
2060 None => CallableKind::Closure,
2061 }
2062 }
2063 pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {
2064 let func = match self.def {
2065 Some(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
2066 _ => return None,
2067 };
2068 let src = func.lookup(db.upcast()).source(db.upcast());
2069 let param_list = src.value.param_list()?;
2070 param_list.self_param()
2071 }
2072 pub fn n_params(&self) -> usize {
2073 self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
2074 }
2075 pub fn params(
2076 &self,
2077 db: &dyn HirDatabase,
2078 ) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> {
2079 let types = self
2080 .sig
2081 .params()
2082 .iter()
2083 .skip(if self.is_bound_method { 1 } else { 0 })
2084 .map(|ty| self.ty.derived(ty.clone()));
2085 let patterns = match self.def {
2086 Some(CallableDefId::FunctionId(func)) => {
2087 let src = func.lookup(db.upcast()).source(db.upcast());
2088 src.value.param_list().map(|param_list| {
2089 param_list
2090 .self_param()
2091 .map(|it| Some(Either::Left(it)))
2092 .filter(|_| !self.is_bound_method)
2093 .into_iter()
2094 .chain(param_list.params().map(|it| it.pat().map(Either::Right)))
2095 })
2096 }
2097 _ => None,
2098 };
2099 patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect()
2100 }
2101 pub fn return_type(&self) -> Type {
2102 self.ty.derived(self.sig.ret().clone())
2103 }
2104}
2105
2106/// For IDE only
2107#[derive(Debug, PartialEq, Eq, Hash)]
2108pub enum ScopeDef {
2109 ModuleDef(ModuleDef),
2110 MacroDef(MacroDef),
2111 GenericParam(GenericParam),
2112 ImplSelfType(Impl),
2113 AdtSelfType(Adt),
2114 Local(Local),
2115 Unknown,
2116}
2117
2118impl ScopeDef {
2119 pub fn all_items(def: PerNs) -> ArrayVec<[Self; 3]> {
2120 let mut items = ArrayVec::new();
2121
2122 match (def.take_types(), def.take_values()) {
2123 (Some(m1), None) => items.push(ScopeDef::ModuleDef(m1.into())),
2124 (None, Some(m2)) => items.push(ScopeDef::ModuleDef(m2.into())),
2125 (Some(m1), Some(m2)) => {
2126 // Some items, like unit structs and enum variants, are
2127 // returned as both a type and a value. Here we want
2128 // to de-duplicate them.
2129 if m1 != m2 {
2130 items.push(ScopeDef::ModuleDef(m1.into()));
2131 items.push(ScopeDef::ModuleDef(m2.into()));
2132 } else {
2133 items.push(ScopeDef::ModuleDef(m1.into()));
2134 }
2135 }
2136 (None, None) => {}
2137 };
2138
2139 if let Some(macro_def_id) = def.take_macros() {
2140 items.push(ScopeDef::MacroDef(macro_def_id.into()));
2141 }
2142
2143 if items.is_empty() {
2144 items.push(ScopeDef::Unknown);
2145 }
2146
2147 items
2148 }
2149}
2150
2151pub trait HasVisibility {
2152 fn visibility(&self, db: &dyn HirDatabase) -> Visibility;
2153 fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool {
2154 let vis = self.visibility(db);
2155 vis.is_visible_from(db.upcast(), module.id)
2156 }
2157}
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..2f07b6d01 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"
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 24ffa6c3a..b716d5f6e 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
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index ff4b4a0cf..b1a3fe1cb 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
@@ -87,11 +86,11 @@ impl Expander {
87 module: ModuleId, 86 module: ModuleId,
88 ) -> Expander { 87 ) -> Expander {
89 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); 88 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
90 let crate_def_map = module.def_map(db); 89 let def_map = module.def_map(db);
91 let ast_id_map = db.ast_id_map(current_file_id); 90 let ast_id_map = db.ast_id_map(current_file_id);
92 Expander { 91 Expander {
93 cfg_expander, 92 cfg_expander,
94 def_map: crate_def_map, 93 def_map,
95 current_file_id, 94 current_file_id,
96 ast_id_map, 95 ast_id_map,
97 module: module.local_id, 96 module: module.local_id,
@@ -105,7 +104,7 @@ impl Expander {
105 macro_call: ast::MacroCall, 104 macro_call: ast::MacroCall,
106 ) -> ExpandResult<Option<(Mark, T)>> { 105 ) -> ExpandResult<Option<(Mark, T)>> {
107 if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT { 106 if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT {
108 mark::hit!(your_stack_belongs_to_me); 107 cov_mark::hit!(your_stack_belongs_to_me);
109 return ExpandResult::str_err("reached recursion limit during macro expansion".into()); 108 return ExpandResult::str_err("reached recursion limit during macro expansion".into());
110 } 109 }
111 110
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 40beb2f7a..d4abe819d 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -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,
@@ -286,7 +285,7 @@ impl ExprCollector<'_> {
286 None => self.collect_expr_opt(condition.expr()), 285 None => self.collect_expr_opt(condition.expr()),
287 // if let -- desugar to match 286 // if let -- desugar to match
288 Some(pat) => { 287 Some(pat) => {
289 mark::hit!(infer_resolve_while_let); 288 cov_mark::hit!(infer_resolve_while_let);
290 let pat = self.collect_pat(pat); 289 let pat = self.collect_pat(pat);
291 let match_expr = self.collect_expr_opt(condition.expr()); 290 let match_expr = self.collect_expr_opt(condition.expr());
292 let placeholder_pat = self.missing_pat(); 291 let placeholder_pat = self.missing_pat();
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..75c2d756b 100644
--- a/crates/hir_def/src/child_by_source.rs
+++ b/crates/hir_def/src/child_by_source.rs
@@ -74,8 +74,8 @@ impl ChildBySource for ImplId {
74 74
75impl ChildBySource for ModuleId { 75impl ChildBySource for ModuleId {
76 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 76 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
77 let crate_def_map = self.def_map(db); 77 let def_map = self.def_map(db);
78 let module_data = &crate_def_map[self.local_id]; 78 let module_data = &def_map[self.local_id];
79 module_data.scope.child_by_source(db) 79 module_data.scope.child_by_source(db)
80 } 80 }
81} 81}
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/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..919933813 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,
@@ -237,7 +236,7 @@ impl ItemScope {
237 if $glob_imports.$field.contains(&$lookup) 236 if $glob_imports.$field.contains(&$lookup)
238 && matches!($def_import_type, ImportType::Named) => 237 && matches!($def_import_type, ImportType::Named) =>
239 { 238 {
240 mark::hit!(import_shadowed); 239 cov_mark::hit!(import_shadowed);
241 $glob_imports.$field.remove(&$lookup); 240 $glob_imports.$field.remove(&$lookup);
242 if let Some(fld) = $def.$field { 241 if let Some(fld) = $def.$field {
243 entry.insert(fld); 242 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/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index e51d89b43..3bb69d935 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::{
@@ -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 }
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..77ff21739 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -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,11 @@ 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 } 345 }
346 } 346 }
347 traits 347 traits
@@ -349,7 +349,7 @@ impl Resolver {
349 349
350 fn module_scope(&self) -> Option<(&DefMap, LocalModuleId)> { 350 fn module_scope(&self) -> Option<(&DefMap, LocalModuleId)> {
351 self.scopes.iter().rev().find_map(|scope| match scope { 351 self.scopes.iter().rev().find_map(|scope| match scope {
352 Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)), 352 Scope::ModuleScope(m) => Some((&*m.def_map, m.module_id)),
353 353
354 _ => None, 354 _ => None,
355 }) 355 })
@@ -413,21 +413,21 @@ impl Scope {
413 // def: m.module.into(), 413 // def: m.module.into(),
414 // }), 414 // }),
415 // ); 415 // );
416 m.crate_def_map[m.module_id].scope.entries().for_each(|(name, def)| { 416 m.def_map[m.module_id].scope.entries().for_each(|(name, def)| {
417 f(name.clone(), ScopeDef::PerNs(def)); 417 f(name.clone(), ScopeDef::PerNs(def));
418 }); 418 });
419 m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { 419 m.def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| {
420 let scope = PerNs::macros(macro_, Visibility::Public); 420 let scope = PerNs::macros(macro_, Visibility::Public);
421 seen.insert((name.clone(), scope)); 421 seen.insert((name.clone(), scope));
422 f(name.clone(), ScopeDef::PerNs(scope)); 422 f(name.clone(), ScopeDef::PerNs(scope));
423 }); 423 });
424 m.crate_def_map.extern_prelude().for_each(|(name, &def)| { 424 m.def_map.extern_prelude().for_each(|(name, &def)| {
425 f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); 425 f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public)));
426 }); 426 });
427 BUILTIN_SCOPE.iter().for_each(|(name, &def)| { 427 BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
428 f(name.clone(), ScopeDef::PerNs(def)); 428 f(name.clone(), ScopeDef::PerNs(def));
429 }); 429 });
430 if let Some(prelude) = m.crate_def_map.prelude() { 430 if let Some(prelude) = m.def_map.prelude() {
431 let prelude_def_map = prelude.def_map(db); 431 let prelude_def_map = prelude.def_map(db);
432 prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { 432 prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| {
433 let seen_tuple = (name.clone(), def); 433 let seen_tuple = (name.clone(), def);
@@ -513,8 +513,8 @@ impl Resolver {
513 self.push_scope(Scope::ImplDefScope(impl_def)) 513 self.push_scope(Scope::ImplDefScope(impl_def))
514 } 514 }
515 515
516 fn push_module_scope(self, crate_def_map: Arc<DefMap>, module_id: LocalModuleId) -> Resolver { 516 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 })) 517 self.push_scope(Scope::ModuleScope(ModuleItemMap { def_map, module_id }))
518 } 518 }
519 519
520 fn push_expr_scope( 520 fn push_expr_scope(
@@ -534,7 +534,7 @@ impl ModuleItemMap {
534 path: &ModPath, 534 path: &ModPath,
535 ) -> Option<ResolveValueResult> { 535 ) -> Option<ResolveValueResult> {
536 let (module_def, idx) = 536 let (module_def, idx) =
537 self.crate_def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other); 537 self.def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other);
538 match idx { 538 match idx {
539 None => { 539 None => {
540 let value = to_value_ns(module_def)?; 540 let value = to_value_ns(module_def)?;
@@ -564,7 +564,7 @@ impl ModuleItemMap {
564 path: &ModPath, 564 path: &ModPath,
565 ) -> Option<(TypeNs, Option<usize>)> { 565 ) -> Option<(TypeNs, Option<usize>)> {
566 let (module_def, idx) = 566 let (module_def, idx) =
567 self.crate_def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other); 567 self.def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other);
568 let res = to_type_ns(module_def)?; 568 let res = to_type_ns(module_def)?;
569 Some((res, idx)) 569 Some((res, idx))
570 } 570 }
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index d1302d749..6131ebee8 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" }
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index 6773ddea3..e230f9765 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,7 +92,7 @@ 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
@@ -625,7 +624,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
625 fn validate_static(&mut self, static_id: StaticId) { 624 fn validate_static(&mut self, static_id: StaticId) {
626 let data = self.db.static_data(static_id); 625 let data = self.db.static_data(static_id);
627 if data.is_extern { 626 if data.is_extern {
628 mark::hit!(extern_static_incorrect_case_ignored); 627 cov_mark::hit!(extern_static_incorrect_case_ignored);
629 return; 628 return;
630 } 629 }
631 630
@@ -673,8 +672,6 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
673 672
674#[cfg(test)] 673#[cfg(test)]
675mod tests { 674mod tests {
676 use test_utils::mark;
677
678 use crate::diagnostics::tests::check_diagnostics; 675 use crate::diagnostics::tests::check_diagnostics;
679 676
680 #[test] 677 #[test]
@@ -889,8 +886,8 @@ fn main() {
889 886
890 #[test] 887 #[test]
891 fn ignores_extern_items() { 888 fn ignores_extern_items() {
892 mark::check!(extern_func_incorrect_case_ignored); 889 cov_mark::check!(extern_func_incorrect_case_ignored);
893 mark::check!(extern_static_incorrect_case_ignored); 890 cov_mark::check!(extern_static_incorrect_case_ignored);
894 check_diagnostics( 891 check_diagnostics(
895 r#" 892 r#"
896extern { 893extern {
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 ec2c13154..262177ffb 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -12,7 +12,6 @@ use hir_def::{
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,
@@ -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,
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index 987793e2e..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;
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/lower.rs b/crates/hir_ty/src/lower.rs
index 5fe5b8ad1..b90fdc382 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);
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index dfcf346fb..24db33c49 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -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) {
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..528092082 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> {
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index f6aaaeda4..f7c5efaf3 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"
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index a9454cfa3..5d1cc2052 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -11,7 +11,6 @@ use ide_db::{
11use itertools::Itertools; 11use itertools::Itertools;
12use stdx::format_to; 12use stdx::format_to;
13use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 13use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
14use test_utils::mark;
15 14
16use crate::{ 15use crate::{
17 display::{macro_label, ShortLabel, TryToNav}, 16 display::{macro_label, ShortLabel, TryToNav},
@@ -193,8 +192,8 @@ fn runnable_action(
193 ModuleDef::Function(func) => { 192 ModuleDef::Function(func) => {
194 let src = func.source(sema.db)?; 193 let src = func.source(sema.db)?;
195 if src.file_id != file_id.into() { 194 if src.file_id != file_id.into() {
196 mark::hit!(hover_macro_generated_struct_fn_doc_comment); 195 cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment);
197 mark::hit!(hover_macro_generated_struct_fn_doc_attr); 196 cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr);
198 return None; 197 return None;
199 } 198 }
200 199
@@ -2101,7 +2100,7 @@ pub fn fo$0o() {}
2101 2100
2102 #[test] 2101 #[test]
2103 fn test_hover_macro_generated_struct_fn_doc_comment() { 2102 fn test_hover_macro_generated_struct_fn_doc_comment() {
2104 mark::check!(hover_macro_generated_struct_fn_doc_comment); 2103 cov_mark::check!(hover_macro_generated_struct_fn_doc_comment);
2105 2104
2106 check( 2105 check(
2107 r#" 2106 r#"
@@ -2139,7 +2138,7 @@ fn foo() { let bar = Bar; bar.fo$0o(); }
2139 2138
2140 #[test] 2139 #[test]
2141 fn test_hover_macro_generated_struct_fn_doc_attr() { 2140 fn test_hover_macro_generated_struct_fn_doc_attr() {
2142 mark::check!(hover_macro_generated_struct_fn_doc_attr); 2141 cov_mark::check!(hover_macro_generated_struct_fn_doc_attr);
2143 2142
2144 check( 2143 check(
2145 r#" 2144 r#"
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/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 65f60891e..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 }
@@ -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/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..3bf0099a9 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"
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..1422224ac 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -99,8 +99,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
99 format!("Import `{}`", &import), 99 format!("Import `{}`", &import),
100 range, 100 range,
101 |builder| { 101 |builder| {
102 let rewriter = 102 let rewriter = insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use);
103 insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use.merge);
104 builder.rewrite(rewriter); 103 builder.rewrite(rewriter);
105 }, 104 },
106 ); 105 );
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 8779d8bd1..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
@@ -197,14 +196,14 @@ fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<Con
197 if let Some(kind) = expr_err_kind(&expr, ctx) { 196 if let Some(kind) = expr_err_kind(&expr, ctx) {
198 Some(FlowKind::TryReturn { expr, kind }) 197 Some(FlowKind::TryReturn { expr, kind })
199 } else { 198 } else {
200 mark::hit!(external_control_flow_try_and_return_non_err); 199 cov_mark::hit!(external_control_flow_try_and_return_non_err);
201 return None; 200 return None;
202 } 201 }
203 } 202 }
204 None => return None, 203 None => return None,
205 }, 204 },
206 (Some(_), _, _, _) => { 205 (Some(_), _, _, _) => {
207 mark::hit!(external_control_flow_try_and_bc); 206 cov_mark::hit!(external_control_flow_try_and_bc);
208 return None; 207 return None;
209 } 208 }
210 (None, Some(r), None, None) => match r.expr() { 209 (None, Some(r), None, None) => match r.expr() {
@@ -212,11 +211,11 @@ fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<Con
212 None => Some(FlowKind::Return), 211 None => Some(FlowKind::Return),
213 }, 212 },
214 (None, Some(_), _, _) => { 213 (None, Some(_), _, _) => {
215 mark::hit!(external_control_flow_return_and_bc); 214 cov_mark::hit!(external_control_flow_return_and_bc);
216 return None; 215 return None;
217 } 216 }
218 (None, None, Some(_), Some(_)) => { 217 (None, None, Some(_), Some(_)) => {
219 mark::hit!(external_control_flow_break_and_continue); 218 cov_mark::hit!(external_control_flow_break_and_continue);
220 return None; 219 return None;
221 } 220 }
222 (None, None, Some(b), None) => match b.expr() { 221 (None, None, Some(b), None) => match b.expr() {
@@ -1837,7 +1836,7 @@ fn $0fun_name(n: u32) -> u32 {
1837 1836
1838 #[test] 1837 #[test]
1839 fn in_comment_is_not_applicable() { 1838 fn in_comment_is_not_applicable() {
1840 mark::check!(extract_function_in_comment_is_not_applicable); 1839 cov_mark::check!(extract_function_in_comment_is_not_applicable);
1841 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; }");
1842 } 1841 }
1843 1842
@@ -2822,7 +2821,7 @@ fn $0fun_name(n: i32) -> Result<i32, i64> {
2822 2821
2823 #[test] 2822 #[test]
2824 fn break_and_continue() { 2823 fn break_and_continue() {
2825 mark::check!(external_control_flow_break_and_continue); 2824 cov_mark::check!(external_control_flow_break_and_continue);
2826 check_assist_not_applicable( 2825 check_assist_not_applicable(
2827 extract_function, 2826 extract_function,
2828 r##" 2827 r##"
@@ -2842,7 +2841,7 @@ fn foo() {
2842 2841
2843 #[test] 2842 #[test]
2844 fn return_and_break() { 2843 fn return_and_break() {
2845 mark::check!(external_control_flow_return_and_bc); 2844 cov_mark::check!(external_control_flow_return_and_bc);
2846 check_assist_not_applicable( 2845 check_assist_not_applicable(
2847 extract_function, 2846 extract_function,
2848 r##" 2847 r##"
@@ -3341,7 +3340,7 @@ fn $0fun_name() -> Result<i32, i64> {
3341 3340
3342 #[test] 3341 #[test]
3343 fn try_and_break() { 3342 fn try_and_break() {
3344 mark::check!(external_control_flow_try_and_bc); 3343 cov_mark::check!(external_control_flow_try_and_bc);
3345 check_assist_not_applicable( 3344 check_assist_not_applicable(
3346 extract_function, 3345 extract_function,
3347 r##" 3346 r##"
@@ -3363,7 +3362,7 @@ fn foo() -> Option<()> {
3363 3362
3364 #[test] 3363 #[test]
3365 fn try_and_return_ok() { 3364 fn try_and_return_ok() {
3366 mark::check!(external_control_flow_try_and_return_non_err); 3365 cov_mark::check!(external_control_flow_try_and_return_non_err);
3367 check_assist_not_applicable( 3366 check_assist_not_applicable(
3368 extract_function, 3367 extract_function,
3369 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/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 377ed4f2f..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)) => {
@@ -160,7 +159,7 @@ fn is_equivalent(
160 if prefix0.op_kind() == Some(ast::PrefixOp::Deref) 159 if prefix0.op_kind() == Some(ast::PrefixOp::Deref)
161 && prefix1.op_kind() == Some(ast::PrefixOp::Deref) => 160 && prefix1.op_kind() == Some(ast::PrefixOp::Deref) =>
162 { 161 {
163 mark::hit!(test_pull_assignment_up_deref); 162 cov_mark::hit!(test_pull_assignment_up_deref);
164 if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) { 163 if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) {
165 is_equivalent(sema, &prefix0, &prefix1) 164 is_equivalent(sema, &prefix0, &prefix1)
166 } else { 165 } else {
@@ -263,7 +262,7 @@ fn foo() {
263 262
264 #[test] 263 #[test]
265 fn test_pull_assignment_up_chained_if() { 264 fn test_pull_assignment_up_chained_if() {
266 mark::check!(test_pull_assignment_up_chained_if); 265 cov_mark::check!(test_pull_assignment_up_chained_if);
267 check_assist( 266 check_assist(
268 pull_assignment_up, 267 pull_assignment_up,
269 r#" 268 r#"
@@ -379,7 +378,7 @@ fn foo() {
379 378
380 #[test] 379 #[test]
381 fn test_pull_assignment_up_field_assignment() { 380 fn test_pull_assignment_up_field_assignment() {
382 mark::check!(test_pull_assignment_up_field_assignment); 381 cov_mark::check!(test_pull_assignment_up_field_assignment);
383 check_assist( 382 check_assist(
384 pull_assignment_up, 383 pull_assignment_up,
385 r#" 384 r#"
@@ -411,7 +410,7 @@ fn foo() {
411 410
412 #[test] 411 #[test]
413 fn test_pull_assignment_up_deref() { 412 fn test_pull_assignment_up_deref() {
414 mark::check!(test_pull_assignment_up_deref); 413 cov_mark::check!(test_pull_assignment_up_deref);
415 check_assist( 414 check_assist(
416 pull_assignment_up, 415 pull_assignment_up,
417 r#" 416 r#"
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs
index b0b0d31b4..d3e34e540 100644
--- a/crates/ide_assists/src/handlers/qualify_path.rs
+++ b/crates/ide_assists/src/handlers/qualify_path.rs
@@ -1,6 +1,6 @@
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::{import_assets::ImportCandidate, mod_path_to_ast};
5use ide_db::RootDatabase; 5use ide_db::RootDatabase;
6use syntax::{ 6use syntax::{
@@ -8,7 +8,6 @@ use syntax::{
8 ast::{make, ArgListOwner}, 8 ast::{make, ArgListOwner},
9 AstNode, 9 AstNode,
10}; 10};
11use test_utils::mark;
12 11
13use crate::{ 12use crate::{
14 assist_context::{AssistContext, Assists}, 13 assist_context::{AssistContext, Assists},
@@ -47,25 +46,25 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
47 let qualify_candidate = match candidate { 46 let qualify_candidate = match candidate {
48 ImportCandidate::Path(candidate) => { 47 ImportCandidate::Path(candidate) => {
49 if candidate.qualifier.is_some() { 48 if candidate.qualifier.is_some() {
50 mark::hit!(qualify_path_qualifier_start); 49 cov_mark::hit!(qualify_path_qualifier_start);
51 let path = ast::Path::cast(syntax_under_caret)?; 50 let path = ast::Path::cast(syntax_under_caret)?;
52 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); 51 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
53 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) 52 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
54 } else { 53 } else {
55 mark::hit!(qualify_path_unqualified_name); 54 cov_mark::hit!(qualify_path_unqualified_name);
56 let path = ast::Path::cast(syntax_under_caret)?; 55 let path = ast::Path::cast(syntax_under_caret)?;
57 let generics = path.segment()?.generic_arg_list(); 56 let generics = path.segment()?.generic_arg_list();
58 QualifyCandidate::UnqualifiedName(generics) 57 QualifyCandidate::UnqualifiedName(generics)
59 } 58 }
60 } 59 }
61 ImportCandidate::TraitAssocItem(_) => { 60 ImportCandidate::TraitAssocItem(_) => {
62 mark::hit!(qualify_path_trait_assoc_item); 61 cov_mark::hit!(qualify_path_trait_assoc_item);
63 let path = ast::Path::cast(syntax_under_caret)?; 62 let path = ast::Path::cast(syntax_under_caret)?;
64 let (qualifier, segment) = (path.qualifier()?, path.segment()?); 63 let (qualifier, segment) = (path.qualifier()?, path.segment()?);
65 QualifyCandidate::TraitAssocItem(qualifier, segment) 64 QualifyCandidate::TraitAssocItem(qualifier, segment)
66 } 65 }
67 ImportCandidate::TraitMethod(_) => { 66 ImportCandidate::TraitMethod(_) => {
68 mark::hit!(qualify_path_trait_method); 67 cov_mark::hit!(qualify_path_trait_method);
69 let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?; 68 let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
70 QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) 69 QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
71 } 70 }
@@ -160,7 +159,9 @@ fn find_trait_method(
160) -> Option<hir::Function> { 159) -> Option<hir::Function> {
161 if let Some(hir::AssocItem::Function(method)) = 160 if let Some(hir::AssocItem::Function(method)) =
162 trait_.items(db).into_iter().find(|item: &hir::AssocItem| { 161 trait_.items(db).into_iter().find(|item: &hir::AssocItem| {
163 item.name(db).map(|name| name == trait_method_name.as_name()).unwrap_or(false) 162 item.name(db)
163 .map(|name| name.to_string() == trait_method_name.to_string())
164 .unwrap_or(false)
164 }) 165 })
165 { 166 {
166 Some(method) 167 Some(method)
@@ -210,7 +211,7 @@ mod tests {
210 211
211 #[test] 212 #[test]
212 fn applicable_when_found_an_import_partial() { 213 fn applicable_when_found_an_import_partial() {
213 mark::check!(qualify_path_unqualified_name); 214 cov_mark::check!(qualify_path_unqualified_name);
214 check_assist( 215 check_assist(
215 qualify_path, 216 qualify_path,
216 r" 217 r"
@@ -502,7 +503,7 @@ fn main() {
502 503
503 #[test] 504 #[test]
504 fn associated_struct_const() { 505 fn associated_struct_const() {
505 mark::check!(qualify_path_qualifier_start); 506 cov_mark::check!(qualify_path_qualifier_start);
506 check_assist( 507 check_assist(
507 qualify_path, 508 qualify_path,
508 r" 509 r"
@@ -603,7 +604,7 @@ fn main() {
603 604
604 #[test] 605 #[test]
605 fn associated_trait_const() { 606 fn associated_trait_const() {
606 mark::check!(qualify_path_trait_assoc_item); 607 cov_mark::check!(qualify_path_trait_assoc_item);
607 check_assist( 608 check_assist(
608 qualify_path, 609 qualify_path,
609 r" 610 r"
@@ -673,7 +674,7 @@ fn main() {
673 674
674 #[test] 675 #[test]
675 fn trait_method() { 676 fn trait_method() {
676 mark::check!(qualify_path_trait_method); 677 cov_mark::check!(qualify_path_trait_method);
677 check_assist( 678 check_assist(
678 qualify_path, 679 qualify_path,
679 r" 680 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_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..84aa40736 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"
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..f34764b61 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -55,7 +55,6 @@ use ide_db::helpers::{
55}; 55};
56use rustc_hash::FxHashSet; 56use rustc_hash::FxHashSet;
57use syntax::{AstNode, SyntaxNode, T}; 57use syntax::{AstNode, SyntaxNode, T};
58use test_utils::mark;
59 58
60use crate::{ 59use crate::{
61 context::CompletionContext, 60 context::CompletionContext,
@@ -174,7 +173,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
174 if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_)) 173 if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_))
175 && fuzzy_name_length < 2 174 && fuzzy_name_length < 2
176 { 175 {
177 mark::hit!(ignore_short_input_for_path); 176 cov_mark::hit!(ignore_short_input_for_path);
178 None 177 None
179 } else { 178 } else {
180 assets_for_path 179 assets_for_path
@@ -186,7 +185,7 @@ fn compute_fuzzy_completion_order_key(
186 proposed_mod_path: &ModPath, 185 proposed_mod_path: &ModPath,
187 user_input_lowercased: &str, 186 user_input_lowercased: &str,
188) -> usize { 187) -> usize {
189 mark::hit!(certain_fuzzy_order_test); 188 cov_mark::hit!(certain_fuzzy_order_test);
190 let proposed_import_name = match proposed_mod_path.segments().last() { 189 let proposed_import_name = match proposed_mod_path.segments().last() {
191 Some(name) => name.to_string().to_lowercase(), 190 Some(name) => name.to_string().to_lowercase(),
192 None => return usize::MAX, 191 None => return usize::MAX,
@@ -200,7 +199,6 @@ fn compute_fuzzy_completion_order_key(
200#[cfg(test)] 199#[cfg(test)]
201mod tests { 200mod tests {
202 use expect_test::{expect, Expect}; 201 use expect_test::{expect, Expect};
203 use test_utils::mark;
204 202
205 use crate::{ 203 use crate::{
206 item::CompletionKind, 204 item::CompletionKind,
@@ -295,7 +293,7 @@ fn main() {
295 293
296 #[test] 294 #[test]
297 fn short_paths_are_ignored() { 295 fn short_paths_are_ignored() {
298 mark::check!(ignore_short_input_for_path); 296 cov_mark::check!(ignore_short_input_for_path);
299 297
300 check( 298 check(
301 r#" 299 r#"
@@ -319,7 +317,7 @@ fn main() {
319 317
320 #[test] 318 #[test]
321 fn fuzzy_completions_come_in_specific_order() { 319 fn fuzzy_completions_come_in_specific_order() {
322 mark::check!(certain_fuzzy_order_test); 320 cov_mark::check!(certain_fuzzy_order_test);
323 check( 321 check(
324 r#" 322 r#"
325//- /lib.rs crate:dep 323//- /lib.rs crate:dep
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/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..9b2435c4b 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -5,7 +5,7 @@ use std::fmt;
5use hir::{Documentation, ModPath, Mutability}; 5use hir::{Documentation, ModPath, Mutability};
6use ide_db::{ 6use ide_db::{
7 helpers::{ 7 helpers::{
8 insert_use::{self, ImportScope, MergeBehavior}, 8 insert_use::{self, ImportScope, InsertUseConfig},
9 mod_path_to_ast, SnippetCap, 9 mod_path_to_ast, SnippetCap,
10 }, 10 },
11 SymbolKind, 11 SymbolKind,
@@ -280,14 +280,11 @@ pub struct ImportEdit {
280impl ImportEdit { 280impl ImportEdit {
281 /// Attempts to insert the import to the given scope, producing a text edit. 281 /// 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. 282 /// 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> { 283 pub fn to_text_edit(&self, cfg: InsertUseConfig) -> Option<TextEdit> {
284 let _p = profile::span("ImportEdit::to_text_edit"); 284 let _p = profile::span("ImportEdit::to_text_edit");
285 285
286 let rewriter = insert_use::insert_use( 286 let rewriter =
287 &self.import_scope, 287 insert_use::insert_use(&self.import_scope, mod_path_to_ast(&self.import_path), cfg);
288 mod_path_to_ast(&self.import_path),
289 merge_behavior,
290 );
291 let old_ast = rewriter.rewrite_root()?; 288 let old_ast = rewriter.rewrite_root()?;
292 let mut import_insert = TextEdit::builder(); 289 let mut import_insert = TextEdit::builder();
293 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert); 290 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 76f31de9e..b0b809791 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -156,7 +156,7 @@ pub fn resolve_completion_edits(
156 .find(|mod_path| mod_path.to_string() == full_import_path)?; 156 .find(|mod_path| mod_path.to_string() == full_import_path)?;
157 157
158 ImportEdit { import_path, import_scope, import_for_trait_assoc_item } 158 ImportEdit { import_path, import_scope, import_for_trait_assoc_item }
159 .to_text_edit(config.insert_use.merge) 159 .to_text_edit(config.insert_use)
160 .map(|edit| vec![edit]) 160 .map(|edit| vec![edit])
161} 161}
162 162
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index eddaaa6f3..dcfac23c5 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -15,7 +15,6 @@ use hir::{
15}; 15};
16use ide_db::{helpers::SnippetCap, RootDatabase, SymbolKind}; 16use ide_db::{helpers::SnippetCap, RootDatabase, SymbolKind};
17use syntax::TextRange; 17use syntax::TextRange;
18use test_utils::mark;
19 18
20use crate::{ 19use crate::{
21 item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 20 item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
@@ -115,11 +114,11 @@ impl<'a> RenderContext<'a> {
115 114
116 fn active_name_and_type(&self) -> Option<(String, Type)> { 115 fn active_name_and_type(&self) -> Option<(String, Type)> {
117 if let Some(record_field) = &self.completion.record_field_syntax { 116 if let Some(record_field) = &self.completion.record_field_syntax {
118 mark::hit!(record_field_type_match); 117 cov_mark::hit!(record_field_type_match);
119 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; 118 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()))) 119 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 { 120 } else if let Some(active_parameter) = &self.completion.active_parameter {
122 mark::hit!(active_param_type_match); 121 cov_mark::hit!(active_param_type_match);
123 Some((active_parameter.name.clone(), active_parameter.ty.clone())) 122 Some((active_parameter.name.clone(), active_parameter.ty.clone()))
124 } else { 123 } else {
125 None 124 None
@@ -269,7 +268,7 @@ impl<'a> Render<'a> {
269 _ => false, 268 _ => false,
270 }; 269 };
271 if has_non_default_type_params { 270 if has_non_default_type_params {
272 mark::hit!(inserts_angle_brackets_for_generics); 271 cov_mark::hit!(inserts_angle_brackets_for_generics);
273 item = item 272 item = item
274 .lookup_by(local_name.clone()) 273 .lookup_by(local_name.clone())
275 .label(format!("{}<…>", local_name)) 274 .label(format!("{}<…>", local_name))
@@ -358,7 +357,6 @@ mod tests {
358 use std::cmp::Reverse; 357 use std::cmp::Reverse;
359 358
360 use expect_test::{expect, Expect}; 359 use expect_test::{expect, Expect};
361 use test_utils::mark;
362 360
363 use crate::{ 361 use crate::{
364 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, 362 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
@@ -734,7 +732,7 @@ fn foo(s: S) { s.$0 }
734 732
735 #[test] 733 #[test]
736 fn no_call_parens_if_fn_ptr_needed() { 734 fn no_call_parens_if_fn_ptr_needed() {
737 mark::check!(no_call_parens_if_fn_ptr_needed); 735 cov_mark::check!(no_call_parens_if_fn_ptr_needed);
738 check_edit( 736 check_edit(
739 "foo", 737 "foo",
740 r#" 738 r#"
@@ -758,7 +756,7 @@ fn main() -> ManualVtable {
758 756
759 #[test] 757 #[test]
760 fn no_parens_in_use_item() { 758 fn no_parens_in_use_item() {
761 mark::check!(no_parens_in_use_item); 759 cov_mark::check!(no_parens_in_use_item);
762 check_edit( 760 check_edit(
763 "foo", 761 "foo",
764 r#" 762 r#"
@@ -802,7 +800,7 @@ fn f(foo: &Foo) { foo.foo(); }
802 800
803 #[test] 801 #[test]
804 fn inserts_angle_brackets_for_generics() { 802 fn inserts_angle_brackets_for_generics() {
805 mark::check!(inserts_angle_brackets_for_generics); 803 cov_mark::check!(inserts_angle_brackets_for_generics);
806 check_edit( 804 check_edit(
807 "Vec", 805 "Vec",
808 r#" 806 r#"
@@ -851,7 +849,7 @@ fn foo(xs: Vec<i128>)
851 849
852 #[test] 850 #[test]
853 fn active_param_score() { 851 fn active_param_score() {
854 mark::check!(active_param_type_match); 852 cov_mark::check!(active_param_type_match);
855 check_scores( 853 check_scores(
856 r#" 854 r#"
857struct S { foo: i64, bar: u32, baz: u32 } 855struct S { foo: i64, bar: u32, baz: u32 }
@@ -868,7 +866,7 @@ fn foo(s: S) { test(s.$0) }
868 866
869 #[test] 867 #[test]
870 fn record_field_scores() { 868 fn record_field_scores() {
871 mark::check!(record_field_type_match); 869 cov_mark::check!(record_field_type_match);
872 check_scores( 870 check_scores(
873 r#" 871 r#"
874struct A { foo: i64, bar: u32, baz: u32 } 872struct A { foo: i64, bar: u32, baz: u32 }
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..1f855c621 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 }
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/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_ssr/Cargo.toml b/crates/ide_ssr/Cargo.toml
index edbc1846b..315691f40 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
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..c7d5e39fa 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"
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 08acd4ac2..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#"
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 14c853c77..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
@@ -185,28 +193,22 @@ pub mod token_stream {
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 let subtree = subtree_replace_token_ids_with_unspecified(subtree); 195 let subtree = subtree_replace_token_ids_with_unspecified(subtree);
188 Ok(TokenStream { subtree }) 196 Ok(TokenStream::with_subtree(subtree))
189 } 197 }
190 } 198 }
191 199
192 impl ToString for TokenStream { 200 impl ToString for TokenStream {
193 fn to_string(&self) -> String { 201 fn to_string(&self) -> String {
194 let tt = self.subtree.clone().into(); 202 return tokentrees_to_text(&self.token_trees[..]);
195 to_text(&tt)
196 }
197 }
198 203
199 fn to_text(tkn: &tt::TokenTree) -> String { 204 fn tokentrees_to_text(tkns: &[tt::TokenTree]) -> String {
200 match tkn { 205 tkns.iter()
201 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.text.clone().into(),
202 tt::TokenTree::Leaf(tt::Leaf::Literal(literal)) => literal.text.clone().into(),
203 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => format!("{}", punct.char),
204 tt::TokenTree::Subtree(subtree) => {
205 let content = subtree
206 .token_trees
207 .iter()
208 .fold((String::new(), true), |(last, last_to_joint), tkn| { 206 .fold((String::new(), true), |(last, last_to_joint), tkn| {
209 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 });
210 let mut is_joint = false; 212 let mut is_joint = false;
211 if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn { 213 if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn {
212 if punct.spacing == tt::Spacing::Joint { 214 if punct.spacing == tt::Spacing::Joint {
@@ -215,15 +217,25 @@ pub mod token_stream {
215 } 217 }
216 (s, is_joint) 218 (s, is_joint)
217 }) 219 })
218 .0; 220 .0
219 221 }
220 let (open, close) = match subtree.delimiter.map(|it| it.kind) { 222
221 None => ("", ""), 223 fn tokentree_to_text(tkn: &tt::TokenTree) -> String {
222 Some(tt::DelimiterKind::Brace) => ("{", "}"), 224 match tkn {
223 Some(tt::DelimiterKind::Parenthesis) => ("(", ")"), 225 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.text.clone().into(),
224 Some(tt::DelimiterKind::Bracket) => ("[", "]"), 226 tt::TokenTree::Leaf(tt::Leaf::Literal(literal)) => literal.text.clone().into(),
225 }; 227 tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => format!("{}", punct.char),
226 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 }
227 } 239 }
228 } 240 }
229 } 241 }
@@ -433,10 +445,7 @@ fn spacing_to_external(spacing: Spacing) -> bridge::Spacing {
433 445
434impl server::Group for Rustc { 446impl server::Group for Rustc {
435 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 {
436 Self::Group { 448 Self::Group { delimiter: delim_to_internal(delimiter), token_trees: stream.token_trees }
437 delimiter: delim_to_internal(delimiter),
438 token_trees: stream.subtree.token_trees,
439 }
440 } 449 }
441 fn delimiter(&mut self, group: &Self::Group) -> bridge::Delimiter { 450 fn delimiter(&mut self, group: &Self::Group) -> bridge::Delimiter {
442 delim_to_external(group.delimiter) 451 delim_to_external(group.delimiter)
@@ -444,9 +453,7 @@ impl server::Group for Rustc {
444 453
445 // NOTE: Return value of do not include delimiter 454 // NOTE: Return value of do not include delimiter
446 fn stream(&mut self, group: &Self::Group) -> Self::TokenStream { 455 fn stream(&mut self, group: &Self::Group) -> Self::TokenStream {
447 TokenStream { 456 TokenStream { token_trees: group.token_trees.clone() }
448 subtree: tt::Subtree { delimiter: None, token_trees: group.token_trees.clone() },
449 }
450 } 457 }
451 458
452 fn span(&mut self, group: &Self::Group) -> Self::Span { 459 fn span(&mut self, group: &Self::Group) -> Self::Span {
@@ -755,28 +762,48 @@ mod tests {
755 #[test] 762 #[test]
756 fn test_rustc_server_to_string() { 763 fn test_rustc_server_to_string() {
757 let s = TokenStream { 764 let s = TokenStream {
758 subtree: tt::Subtree { 765 token_trees: vec![
759 delimiter: None, 766 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
760 token_trees: vec![ 767 text: "struct".into(),
761 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { 768 id: tt::TokenId::unspecified(),
762 text: "struct".into(), 769 })),
763 id: tt::TokenId::unspecified(), 770 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
764 })), 771 text: "T".into(),
765 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { 772 id: tt::TokenId::unspecified(),
766 text: "T".into(), 773 })),
774 tt::TokenTree::Subtree(tt::Subtree {
775 delimiter: Some(tt::Delimiter {
767 id: tt::TokenId::unspecified(), 776 id: tt::TokenId::unspecified(),
768 })), 777 kind: tt::DelimiterKind::Brace,
769 tt::TokenTree::Subtree(tt::Subtree {
770 delimiter: Some(tt::Delimiter {
771 id: tt::TokenId::unspecified(),
772 kind: tt::DelimiterKind::Brace,
773 }),
774 token_trees: vec![],
775 }), 778 }),
776 ], 779 token_trees: vec![],
777 }, 780 }),
781 ],
778 }; 782 };
779 783
780 assert_eq!(s.to_string(), "struct T {}"); 784 assert_eq!(s.to_string(), "struct T {}");
781 } 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 }
782} 809}
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 367136702..078c83f75 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -35,7 +35,8 @@ config_data! {
35 assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"", 35 assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"",
36 /// The path structure for newly inserted paths to use. 36 /// The path structure for newly inserted paths to use.
37 assist_importPrefix: ImportPrefixDef = "\"plain\"", 37 assist_importPrefix: ImportPrefixDef = "\"plain\"",
38 38 /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
39 assist_importGroup: bool = "true",
39 /// Show function name and docs in parameter hints. 40 /// Show function name and docs in parameter hints.
40 callInfo_full: bool = "true", 41 callInfo_full: bool = "true",
41 42
@@ -48,7 +49,7 @@ config_data! {
48 cargo_features: Vec<String> = "[]", 49 cargo_features: Vec<String> = "[]",
49 /// Run build scripts (`build.rs`) for more precise code analysis. 50 /// Run build scripts (`build.rs`) for more precise code analysis.
50 cargo_runBuildScripts | 51 cargo_runBuildScripts |
51 cargo_loadOutDirsFromCheck: bool = "false", 52 cargo_loadOutDirsFromCheck: bool = "true",
52 /// Do not activate the `default` feature. 53 /// Do not activate the `default` feature.
53 cargo_noDefaultFeatures: bool = "false", 54 cargo_noDefaultFeatures: bool = "false",
54 /// Compilation target (target triple). 55 /// Compilation target (target triple).
@@ -180,7 +181,8 @@ config_data! {
180 runnables_cargoExtraArgs: Vec<String> = "[]", 181 runnables_cargoExtraArgs: Vec<String> = "[]",
181 182
182 /// Path to the rust compiler sources, for usage in rustc_private projects, or "discover" 183 /// Path to the rust compiler sources, for usage in rustc_private projects, or "discover"
183 /// to try to automatically find it. 184 /// to try to automatically find it. Any project which uses rust-analyzer with the rustcPrivate
185 /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
184 rustcSource : Option<String> = "null", 186 rustcSource : Option<String> = "null",
185 187
186 /// Additional arguments to `rustfmt`. 188 /// Additional arguments to `rustfmt`.
@@ -574,6 +576,7 @@ impl Config {
574 ImportPrefixDef::ByCrate => PrefixKind::ByCrate, 576 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
575 ImportPrefixDef::BySelf => PrefixKind::BySelf, 577 ImportPrefixDef::BySelf => PrefixKind::BySelf,
576 }, 578 },
579 group: self.data.assist_importGroup,
577 } 580 }
578 } 581 }
579 pub fn completion(&self) -> CompletionConfig { 582 pub fn completion(&self) -> CompletionConfig {
@@ -856,12 +859,12 @@ fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String {
856mod tests { 859mod tests {
857 use std::fs; 860 use std::fs;
858 861
859 use test_utils::project_dir; 862 use test_utils::{ensure_file_contents, project_root};
860 863
861 use super::*; 864 use super::*;
862 865
863 #[test] 866 #[test]
864 fn schema_in_sync_with_package_json() { 867 fn generate_package_json_config() {
865 let s = Config::json_schema(); 868 let s = Config::json_schema();
866 let schema = format!("{:#}", s); 869 let schema = format!("{:#}", s);
867 let mut schema = schema 870 let mut schema = schema
@@ -874,7 +877,7 @@ mod tests {
874 .to_string(); 877 .to_string();
875 schema.push_str(",\n"); 878 schema.push_str(",\n");
876 879
877 let package_json_path = project_dir().join("editors/code/package.json"); 880 let package_json_path = project_root().join("editors/code/package.json");
878 let mut package_json = fs::read_to_string(&package_json_path).unwrap(); 881 let mut package_json = fs::read_to_string(&package_json_path).unwrap();
879 882
880 let start_marker = " \"$generated-start\": false,\n"; 883 let start_marker = " \"$generated-start\": false,\n";
@@ -882,19 +885,18 @@ mod tests {
882 885
883 let start = package_json.find(start_marker).unwrap() + start_marker.len(); 886 let start = package_json.find(start_marker).unwrap() + start_marker.len();
884 let end = package_json.find(end_marker).unwrap(); 887 let end = package_json.find(end_marker).unwrap();
888
885 let p = remove_ws(&package_json[start..end]); 889 let p = remove_ws(&package_json[start..end]);
886 let s = remove_ws(&schema); 890 let s = remove_ws(&schema);
887
888 if !p.contains(&s) { 891 if !p.contains(&s) {
889 package_json.replace_range(start..end, &schema); 892 package_json.replace_range(start..end, &schema);
890 fs::write(&package_json_path, &mut package_json).unwrap(); 893 ensure_file_contents(&package_json_path, &package_json)
891 panic!("new config, updating package.json")
892 } 894 }
893 } 895 }
894 896
895 #[test] 897 #[test]
896 fn schema_in_sync_with_docs() { 898 fn generate_config_documentation() {
897 let docs_path = project_dir().join("docs/user/generated_config.adoc"); 899 let docs_path = project_root().join("docs/user/generated_config.adoc");
898 let current = fs::read_to_string(&docs_path).unwrap(); 900 let current = fs::read_to_string(&docs_path).unwrap();
899 let expected = ConfigData::manual(); 901 let expected = ConfigData::manual();
900 902
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index c1ca88df6..4235eb6dd 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -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 )
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..c39095def 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" }
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/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/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/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 1dbf2a611..5243bcbf6 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -2,6 +2,8 @@
2 The strategy to use when inserting new imports or merging imports. 2 The strategy to use when inserting new imports or merging imports.
3[[rust-analyzer.assist.importPrefix]]rust-analyzer.assist.importPrefix (default: `"plain"`):: 3[[rust-analyzer.assist.importPrefix]]rust-analyzer.assist.importPrefix (default: `"plain"`)::
4 The path structure for newly inserted paths to use. 4 The path structure for newly inserted paths to use.
5[[rust-analyzer.assist.importGroup]]rust-analyzer.assist.importGroup (default: `true`)::
6 Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
5[[rust-analyzer.callInfo.full]]rust-analyzer.callInfo.full (default: `true`):: 7[[rust-analyzer.callInfo.full]]rust-analyzer.callInfo.full (default: `true`)::
6 Show function name and docs in parameter hints. 8 Show function name and docs in parameter hints.
7[[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`):: 9[[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`)::
@@ -10,7 +12,7 @@
10 Activate all available features (`--all-features`). 12 Activate all available features (`--all-features`).
11[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: 13[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`)::
12 List of features to activate. 14 List of features to activate.
13[[rust-analyzer.cargo.runBuildScripts]]rust-analyzer.cargo.runBuildScripts (default: `false`):: 15[[rust-analyzer.cargo.runBuildScripts]]rust-analyzer.cargo.runBuildScripts (default: `true`)::
14 Run build scripts (`build.rs`) for more precise code analysis. 16 Run build scripts (`build.rs`) for more precise code analysis.
15[[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`):: 17[[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`)::
16 Do not activate the `default` feature. 18 Do not activate the `default` feature.
@@ -105,7 +107,7 @@
105[[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`):: 107[[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`. 108 Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be `--release`.
107[[rust-analyzer.rustcSource]]rust-analyzer.rustcSource (default: `null`):: 109[[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. 110 Path to the rust compiler sources, for usage in rustc_private projects, or "discover" to try to automatically find it. Any project which uses rust-analyzer with the rustcPrivate crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
109[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: 111[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`)::
110 Additional arguments to `rustfmt`. 112 Additional arguments to `rustfmt`.
111[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: 113[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`)::
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 1987364bc..856f1c94e 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,6 +386,11 @@
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,
@@ -410,7 +416,7 @@
410 }, 416 },
411 "rust-analyzer.cargo.runBuildScripts": { 417 "rust-analyzer.cargo.runBuildScripts": {
412 "markdownDescription": "Run build scripts (`build.rs`) for more precise code analysis.", 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": {
@@ -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 rust compiler sources, for usage in rustc_private projects, or \"discover\" to try to automatically find it. Any project which uses rust-analyzer with the rustcPrivate crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.",
711 "default": null, 717 "default": null,
712 "type": [ 718 "type": [
713 "null", 719 "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 56eda5b1e..48d1ad45e 100644
--- a/xtask/src/flags.rs
+++ b/xtask/src/flags.rs
@@ -2,7 +2,9 @@
2 2
3use crate::install::{ClientOpt, Malloc, ServerOpt}; 3use crate::install::{ClientOpt, Malloc, ServerOpt};
4 4
5xflags::args_parser! { 5xflags::xflags! {
6 src "./src/flags.rs"
7
6 /// Run custom build command. 8 /// Run custom build command.
7 cmd xtask { 9 cmd xtask {
8 default cmd help { 10 default cmd help {
@@ -25,10 +27,6 @@ xflags::args_parser! {
25 optional --jemalloc 27 optional --jemalloc
26 } 28 }
27 29
28 cmd codegen {
29 optional --features
30 }
31
32 cmd lint {} 30 cmd lint {}
33 cmd fuzz-tests {} 31 cmd fuzz-tests {}
34 cmd pre-cache {} 32 cmd pre-cache {}
@@ -55,7 +53,7 @@ xflags::args_parser! {
55 53
56// generated start 54// generated start
57// The following code is generated by `xflags` macro. 55// The following code is generated by `xflags` macro.
58// Run `env XFLAGS_DUMP= cargo build` to regenerate. 56// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate.
59#[derive(Debug)] 57#[derive(Debug)]
60pub struct Xtask { 58pub struct Xtask {
61 pub subcommand: XtaskCmd, 59 pub subcommand: XtaskCmd,
@@ -65,7 +63,6 @@ pub struct Xtask {
65pub enum XtaskCmd { 63pub enum XtaskCmd {
66 Help(Help), 64 Help(Help),
67 Install(Install), 65 Install(Install),
68 Codegen(Codegen),
69 Lint(Lint), 66 Lint(Lint),
70 FuzzTests(FuzzTests), 67 FuzzTests(FuzzTests),
71 PreCache(PreCache), 68 PreCache(PreCache),
@@ -91,18 +88,13 @@ pub struct Install {
91} 88}
92 89
93#[derive(Debug)] 90#[derive(Debug)]
94pub struct Codegen { 91pub struct Lint;
95 pub features: bool,
96}
97
98#[derive(Debug)]
99pub struct Lint {}
100 92
101#[derive(Debug)] 93#[derive(Debug)]
102pub struct FuzzTests {} 94pub struct FuzzTests;
103 95
104#[derive(Debug)] 96#[derive(Debug)]
105pub struct PreCache {} 97pub struct PreCache;
106 98
107#[derive(Debug)] 99#[derive(Debug)]
108pub struct Release { 100pub struct Release {
@@ -131,11 +123,10 @@ pub struct Bb {
131} 123}
132 124
133impl Xtask { 125impl Xtask {
134 pub const HELP: &'static str = Self::_HELP; 126 pub const HELP: &'static str = Self::HELP_;
135 127
136 pub fn from_env() -> xflags::Result<Self> { 128 pub fn from_env() -> xflags::Result<Self> {
137 let mut p = xflags::rt::Parser::new_from_env(); 129 Self::from_env_()
138 Self::_parse(&mut p)
139 } 130 }
140} 131}
141// generated end 132// generated end
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 3c4332f75..35cc7c108 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -28,7 +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::{codegen::Mode, dist::DistCmd}; 31use crate::dist::DistCmd;
32 32
33fn main() -> Result<()> { 33fn main() -> Result<()> {
34 let _d = pushd(project_root())?; 34 let _d = pushd(project_root())?;
@@ -40,7 +40,6 @@ fn main() -> Result<()> {
40 return Ok(()); 40 return Ok(());
41 } 41 }
42 flags::XtaskCmd::Install(cmd) => cmd.run(), 42 flags::XtaskCmd::Install(cmd) => cmd.run(),
43 flags::XtaskCmd::Codegen(cmd) => cmd.run(),
44 flags::XtaskCmd::Lint(_) => run_clippy(), 43 flags::XtaskCmd::Lint(_) => run_clippy(),
45 flags::XtaskCmd::FuzzTests(_) => run_fuzzer(), 44 flags::XtaskCmd::FuzzTests(_) => run_fuzzer(),
46 flags::XtaskCmd::PreCache(cmd) => cmd.run(), 45 flags::XtaskCmd::PreCache(cmd) => cmd.run(),
@@ -85,18 +84,6 @@ fn rust_files_in(path: &Path) -> impl Iterator<Item = PathBuf> {
85 files_in(path, "rs") 84 files_in(path, "rs")
86} 85}
87 86
88fn run_rustfmt(mode: Mode) -> Result<()> {
89 let _dir = pushd(project_root())?;
90 let _e = pushenv("RUSTUP_TOOLCHAIN", "stable");
91 ensure_rustfmt()?;
92 let check = match mode {
93 Mode::Overwrite => &[][..],
94 Mode::Verify => &["--", "--check"],
95 };
96 cmd!("cargo fmt {check...}").run()?;
97 Ok(())
98}
99
100fn ensure_rustfmt() -> Result<()> { 87fn ensure_rustfmt() -> Result<()> {
101 let out = cmd!("rustfmt --version").read()?; 88 let out = cmd!("rustfmt --version").read()?;
102 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]