aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Cargo.lock83
-rw-r--r--crates/ra_assists/src/assist_context.rs39
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_function.rs2
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs103
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs2
-rw-r--r--crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs320
-rw-r--r--crates/ra_assists/src/handlers/fix_visibility.rs4
-rw-r--r--crates/ra_assists/src/handlers/introduce_named_lifetime.rs19
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs37
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs164
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_assists/src/tests/generated.rs15
-rw-r--r--crates/ra_db/src/input.rs20
-rw-r--r--crates/ra_db/src/lib.rs79
-rw-r--r--crates/ra_flycheck/src/lib.rs24
-rw-r--r--crates/ra_hir/src/code_model.rs26
-rw-r--r--crates/ra_hir/src/db.rs14
-rw-r--r--crates/ra_hir/src/semantics.rs3
-rw-r--r--crates/ra_hir/src/source_analyzer.rs3
-rw-r--r--crates/ra_hir_def/Cargo.toml3
-rw-r--r--crates/ra_hir_def/src/attr.rs8
-rw-r--r--crates/ra_hir_def/src/body.rs2
-rw-r--r--crates/ra_hir_def/src/data.rs8
-rw-r--r--crates/ra_hir_def/src/db.rs18
-rw-r--r--crates/ra_hir_def/src/docs.rs50
-rw-r--r--crates/ra_hir_def/src/find_path.rs247
-rw-r--r--crates/ra_hir_def/src/import_map.rs679
-rw-r--r--crates/ra_hir_def/src/item_scope.rs23
-rw-r--r--crates/ra_hir_def/src/lib.rs12
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs39
-rw-r--r--crates/ra_hir_def/src/nameres/mod_resolution.rs2
-rw-r--r--crates/ra_hir_def/src/path.rs35
-rw-r--r--crates/ra_hir_def/src/per_ns.rs10
-rw-r--r--crates/ra_hir_def/src/test_db.rs23
-rw-r--r--crates/ra_hir_expand/Cargo.toml1
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs31
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs38
-rw-r--r--crates/ra_hir_expand/src/eager.rs32
-rw-r--r--crates/ra_hir_expand/src/lib.rs11
-rw-r--r--crates/ra_hir_expand/src/name.rs1
-rw-r--r--crates/ra_hir_expand/src/test_db.rs20
-rw-r--r--crates/ra_hir_ty/Cargo.toml4
-rw-r--r--crates/ra_hir_ty/src/db.rs20
-rw-r--r--crates/ra_hir_ty/src/display.rs39
-rw-r--r--crates/ra_hir_ty/src/expr.rs10
-rw-r--r--crates/ra_hir_ty/src/infer.rs52
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs28
-rw-r--r--crates/ra_hir_ty/src/infer/path.rs3
-rw-r--r--crates/ra_hir_ty/src/lib.rs104
-rw-r--r--crates/ra_hir_ty/src/lower.rs214
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs12
-rw-r--r--crates/ra_hir_ty/src/primitive.rs54
-rw-r--r--crates/ra_hir_ty/src/test_db.rs22
-rw-r--r--crates/ra_hir_ty/src/tests/display_source_code.rs4
-rw-r--r--crates/ra_hir_ty/src/tests/method_resolution.rs54
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs112
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs174
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs52
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/interner.rs2
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/mapping.rs74
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/tls.rs5
-rw-r--r--crates/ra_ide/src/completion.rs78
-rw-r--r--crates/ra_ide/src/diagnostics.rs75
-rw-r--r--crates/ra_ide/src/display/function_signature.rs13
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs66
-rw-r--r--crates/ra_ide/src/goto_definition.rs26
-rw-r--r--crates/ra_ide/src/hover.rs411
-rw-r--r--crates/ra_ide/src/inlay_hints.rs7
-rw-r--r--crates/ra_ide/src/lib.rs43
-rw-r--r--crates/ra_ide/src/references/rename.rs6
-rw-r--r--crates/ra_ide/src/runnables.rs342
-rw-r--r--crates/ra_ide/src/snapshots/highlight_doctest.html71
-rw-r--r--crates/ra_ide/src/snapshots/highlight_injection.html2
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html5
-rw-r--r--crates/ra_ide/src/snapshots/highlight_unsafe.html49
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html6
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html2
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs148
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs2
-rw-r--r--crates/ra_ide/src/syntax_highlighting/injection.rs168
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs8
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs123
-rw-r--r--crates/ra_ide/src/typing.rs44
-rw-r--r--crates/ra_ide_db/src/change.rs11
-rw-r--r--crates/ra_ide_db/src/defs.rs59
-rw-r--r--crates/ra_ide_db/src/imports_locator.rs46
-rw-r--r--crates/ra_ide_db/src/lib.rs37
-rw-r--r--crates/ra_ide_db/src/source_change.rs23
-rw-r--r--crates/ra_ide_db/src/symbol_index.rs53
-rw-r--r--crates/ra_parser/src/grammar.rs7
-rw-r--r--crates/ra_parser/src/grammar/items.rs20
-rw-r--r--crates/ra_parser/src/grammar/paths.rs2
-rw-r--r--crates/ra_parser/src/grammar/patterns.rs8
-rw-r--r--crates/ra_parser/src/grammar/type_params.rs6
-rw-r--r--crates/ra_parser/src/grammar/types.rs16
-rw-r--r--crates/ra_proc_macro_srv/Cargo.toml1
-rw-r--r--crates/ra_proc_macro_srv/src/tests/utils.rs3
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs2
-rw-r--r--crates/ra_project_model/src/json_project.rs55
-rw-r--r--crates/ra_project_model/src/lib.rs111
-rw-r--r--crates/ra_syntax/src/ast.rs8
-rw-r--r--crates/ra_syntax/src/ast/edit.rs13
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs276
-rw-r--r--crates/ra_syntax/src/ast/generated/tokens.rs8
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs52
-rw-r--r--crates/ra_syntax/src/ast/traits.rs13
-rw-r--r--crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast74
-rw-r--r--crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast15
-rw-r--r--crates/ra_syntax/test_data/parser/err/0043_default_const.rast40
-rw-r--r--crates/ra_syntax/test_data/parser/err/0043_default_const.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast240
-rw-r--r--crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs9
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast34
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast117
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs2
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast292
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast40
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast18
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast38
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0066_default_const.rast44
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0066_default_const.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast392
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs30
-rw-r--r--crates/rust-analyzer/Cargo.toml4
-rw-r--r--crates/rust-analyzer/src/bin/main.rs41
-rw-r--r--crates/rust-analyzer/src/caps.rs3
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs89
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs64
-rw-r--r--crates/rust-analyzer/src/config.rs84
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap1
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap3
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs4
-rw-r--r--crates/rust-analyzer/src/from_proto.rs8
-rw-r--r--crates/rust-analyzer/src/global_state.rs (renamed from crates/rust-analyzer/src/world.rs)72
-rw-r--r--crates/rust-analyzer/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs80
-rw-r--r--crates/rust-analyzer/src/main_loop.rs150
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs802
-rw-r--r--crates/rust-analyzer/src/to_proto.rs125
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs198
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs31
-rw-r--r--crates/stdx/src/lib.rs5
-rw-r--r--crates/test_utils/Cargo.toml3
-rw-r--r--crates/test_utils/src/lib.rs6
-rw-r--r--docs/dev/README.md209
-rw-r--r--docs/dev/lsp-extensions.md122
-rw-r--r--docs/user/generated_assists.adoc1015
-rw-r--r--docs/user/generated_features.adoc10
-rw-r--r--docs/user/manual.adoc51
-rw-r--r--editors/code/package.json64
-rw-r--r--editors/code/src/client.ts106
-rw-r--r--editors/code/src/commands.ts93
-rw-r--r--editors/code/src/config.ts13
-rw-r--r--editors/code/src/debug.ts121
-rw-r--r--editors/code/src/lsp_ext.ts34
-rw-r--r--editors/code/src/main.ts1
-rw-r--r--editors/code/src/run.ts96
-rw-r--r--editors/code/src/tasks.ts7
-rw-r--r--editors/code/src/toolchain.ts (renamed from editors/code/src/cargo.ts)117
-rw-r--r--editors/code/src/util.ts18
-rw-r--r--editors/code/tests/unit/launch_config.test.ts14
-rw-r--r--xtask/src/ast_src.rs2
-rw-r--r--xtask/src/codegen.rs6
-rw-r--r--xtask/src/codegen/gen_assists_docs.rs11
-rw-r--r--xtask/src/codegen/gen_syntax.rs6
-rw-r--r--xtask/src/lib.rs55
-rw-r--r--xtask/src/main.rs8
-rw-r--r--xtask/src/release.rs67
-rw-r--r--xtask/tests/tidy.rs25
175 files changed, 7387 insertions, 3882 deletions
diff --git a/.gitignore b/.gitignore
index dab51647d..aef0fac33 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,5 @@ crates/*/target
8*.iml 8*.iml
9.vscode/settings.json 9.vscode/settings.json
10*.html 10*.html
11generated_assists.adoc
12generated_features.adoc
diff --git a/Cargo.lock b/Cargo.lock
index 9981a2e33..308e36836 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -113,8 +113,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
113 113
114[[package]] 114[[package]]
115name = "chalk-derive" 115name = "chalk-derive"
116version = "0.10.1-dev" 116version = "0.11.0"
117source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c" 117source = "registry+https://github.com/rust-lang/crates.io-index"
118checksum = "5b9bd01eab87277d973183a1d2e56bace1c11f8242c52c20636fb7dddf343ac9"
118dependencies = [ 119dependencies = [
119 "proc-macro2", 120 "proc-macro2",
120 "quote", 121 "quote",
@@ -124,8 +125,9 @@ dependencies = [
124 125
125[[package]] 126[[package]]
126name = "chalk-engine" 127name = "chalk-engine"
127version = "0.10.1-dev" 128version = "0.11.0"
128source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c" 129source = "registry+https://github.com/rust-lang/crates.io-index"
130checksum = "6c7a637c3d17ed555aef16e16952a5d1e127bd55178cc30be22afeb92da90c7d"
129dependencies = [ 131dependencies = [
130 "chalk-derive", 132 "chalk-derive",
131 "chalk-ir", 133 "chalk-ir",
@@ -134,8 +136,9 @@ dependencies = [
134 136
135[[package]] 137[[package]]
136name = "chalk-ir" 138name = "chalk-ir"
137version = "0.10.1-dev" 139version = "0.11.0"
138source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c" 140source = "registry+https://github.com/rust-lang/crates.io-index"
141checksum = "595e5735ded16c3f3dc348f7b15bbb2521a0080b1863cac38ad5271589944670"
139dependencies = [ 142dependencies = [
140 "chalk-derive", 143 "chalk-derive",
141 "lazy_static", 144 "lazy_static",
@@ -143,8 +146,9 @@ dependencies = [
143 146
144[[package]] 147[[package]]
145name = "chalk-solve" 148name = "chalk-solve"
146version = "0.10.1-dev" 149version = "0.11.0"
147source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c" 150source = "registry+https://github.com/rust-lang/crates.io-index"
151checksum = "5d9d938139db425867a30cc0cfec0269406d8238d0571d829041eaa7a8455d11"
148dependencies = [ 152dependencies = [
149 "chalk-derive", 153 "chalk-derive",
150 "chalk-engine", 154 "chalk-engine",
@@ -243,12 +247,13 @@ dependencies = [
243 247
244[[package]] 248[[package]]
245name = "crossbeam-queue" 249name = "crossbeam-queue"
246version = "0.2.2" 250version = "0.2.3"
247source = "registry+https://github.com/rust-lang/crates.io-index" 251source = "registry+https://github.com/rust-lang/crates.io-index"
248checksum = "ab6bffe714b6bb07e42f201352c34f51fefd355ace793f9e638ebd52d23f98d2" 252checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
249dependencies = [ 253dependencies = [
250 "cfg-if", 254 "cfg-if",
251 "crossbeam-utils", 255 "crossbeam-utils",
256 "maybe-uninit",
252] 257]
253 258
254[[package]] 259[[package]]
@@ -462,18 +467,18 @@ dependencies = [
462 467
463[[package]] 468[[package]]
464name = "indexmap" 469name = "indexmap"
465version = "1.3.2" 470version = "1.4.0"
466source = "registry+https://github.com/rust-lang/crates.io-index" 471source = "registry+https://github.com/rust-lang/crates.io-index"
467checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" 472checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe"
468dependencies = [ 473dependencies = [
469 "autocfg", 474 "autocfg",
470] 475]
471 476
472[[package]] 477[[package]]
473name = "inotify" 478name = "inotify"
474version = "0.7.0" 479version = "0.7.1"
475source = "registry+https://github.com/rust-lang/crates.io-index" 480source = "registry+https://github.com/rust-lang/crates.io-index"
476checksum = "24e40d6fd5d64e2082e0c796495c8ef5ad667a96d03e5aaa0becfd9d47bcbfb8" 481checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f"
477dependencies = [ 482dependencies = [
478 "bitflags", 483 "bitflags",
479 "inotify-sys", 484 "inotify-sys",
@@ -561,9 +566,9 @@ dependencies = [
561 566
562[[package]] 567[[package]]
563name = "jod-thread" 568name = "jod-thread"
564version = "0.1.1" 569version = "0.1.2"
565source = "registry+https://github.com/rust-lang/crates.io-index" 570source = "registry+https://github.com/rust-lang/crates.io-index"
566checksum = "4022656272c3e564a7cdebcaaba6518d844b0d0c1836597196efb5bfeb98bb49" 571checksum = "8b23360e99b8717f20aaa4598f5a6541efbe30630039fbc7706cf954a87947ae"
567 572
568[[package]] 573[[package]]
569name = "kernel32-sys" 574name = "kernel32-sys"
@@ -809,9 +814,9 @@ dependencies = [
809 814
810[[package]] 815[[package]]
811name = "paste" 816name = "paste"
812version = "0.1.15" 817version = "0.1.16"
813source = "registry+https://github.com/rust-lang/crates.io-index" 818source = "registry+https://github.com/rust-lang/crates.io-index"
814checksum = "d53181dcd37421c08d3b69f887784956674d09c3f9a47a04fece2b130a5b346b" 819checksum = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec"
815dependencies = [ 820dependencies = [
816 "paste-impl", 821 "paste-impl",
817 "proc-macro-hack", 822 "proc-macro-hack",
@@ -819,9 +824,9 @@ dependencies = [
819 824
820[[package]] 825[[package]]
821name = "paste-impl" 826name = "paste-impl"
822version = "0.1.15" 827version = "0.1.16"
823source = "registry+https://github.com/rust-lang/crates.io-index" 828source = "registry+https://github.com/rust-lang/crates.io-index"
824checksum = "05ca490fa1c034a71412b4d1edcb904ec5a0981a4426c9eb2128c0fda7a68d17" 829checksum = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32"
825dependencies = [ 830dependencies = [
826 "proc-macro-hack", 831 "proc-macro-hack",
827 "proc-macro2", 832 "proc-macro2",
@@ -871,18 +876,18 @@ checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
871 876
872[[package]] 877[[package]]
873name = "proc-macro2" 878name = "proc-macro2"
874version = "1.0.17" 879version = "1.0.18"
875source = "registry+https://github.com/rust-lang/crates.io-index" 880source = "registry+https://github.com/rust-lang/crates.io-index"
876checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" 881checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
877dependencies = [ 882dependencies = [
878 "unicode-xid", 883 "unicode-xid",
879] 884]
880 885
881[[package]] 886[[package]]
882name = "quote" 887name = "quote"
883version = "1.0.6" 888version = "1.0.7"
884source = "registry+https://github.com/rust-lang/crates.io-index" 889source = "registry+https://github.com/rust-lang/crates.io-index"
885checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" 890checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
886dependencies = [ 891dependencies = [
887 "proc-macro2", 892 "proc-macro2",
888] 893]
@@ -977,7 +982,10 @@ dependencies = [
977 "anymap", 982 "anymap",
978 "drop_bomb", 983 "drop_bomb",
979 "either", 984 "either",
985 "fst",
986 "indexmap",
980 "insta", 987 "insta",
988 "itertools",
981 "log", 989 "log",
982 "once_cell", 990 "once_cell",
983 "ra_arena", 991 "ra_arena",
@@ -1006,6 +1014,7 @@ dependencies = [
1006 "ra_prof", 1014 "ra_prof",
1007 "ra_syntax", 1015 "ra_syntax",
1008 "ra_tt", 1016 "ra_tt",
1017 "rustc-hash",
1009 "test_utils", 1018 "test_utils",
1010] 1019]
1011 1020
@@ -1119,6 +1128,7 @@ dependencies = [
1119 "memmap", 1128 "memmap",
1120 "ra_mbe", 1129 "ra_mbe",
1121 "ra_proc_macro", 1130 "ra_proc_macro",
1131 "ra_toolchain",
1122 "ra_tt", 1132 "ra_tt",
1123 "serde_derive", 1133 "serde_derive",
1124 "test_utils", 1134 "test_utils",
@@ -1400,9 +1410,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
1400 1410
1401[[package]] 1411[[package]]
1402name = "ryu" 1412name = "ryu"
1403version = "1.0.4" 1413version = "1.0.5"
1404source = "registry+https://github.com/rust-lang/crates.io-index" 1414source = "registry+https://github.com/rust-lang/crates.io-index"
1405checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" 1415checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
1406 1416
1407[[package]] 1417[[package]]
1408name = "salsa" 1418name = "salsa"
@@ -1511,9 +1521,9 @@ dependencies = [
1511 1521
1512[[package]] 1522[[package]]
1513name = "serde_json" 1523name = "serde_json"
1514version = "1.0.53" 1524version = "1.0.55"
1515source = "registry+https://github.com/rust-lang/crates.io-index" 1525source = "registry+https://github.com/rust-lang/crates.io-index"
1516checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" 1526checksum = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226"
1517dependencies = [ 1527dependencies = [
1518 "itoa", 1528 "itoa",
1519 "ryu", 1529 "ryu",
@@ -1533,9 +1543,9 @@ dependencies = [
1533 1543
1534[[package]] 1544[[package]]
1535name = "serde_yaml" 1545name = "serde_yaml"
1536version = "0.8.12" 1546version = "0.8.13"
1537source = "registry+https://github.com/rust-lang/crates.io-index" 1547source = "registry+https://github.com/rust-lang/crates.io-index"
1538checksum = "16c7a592a1ec97c9c1c68d75b6e537dcbf60c7618e038e7841e00af1d9ccf0c4" 1548checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5"
1539dependencies = [ 1549dependencies = [
1540 "dtoa", 1550 "dtoa",
1541 "linked-hash-map", 1551 "linked-hash-map",
@@ -1576,9 +1586,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
1576 1586
1577[[package]] 1587[[package]]
1578name = "syn" 1588name = "syn"
1579version = "1.0.29" 1589version = "1.0.31"
1580source = "registry+https://github.com/rust-lang/crates.io-index" 1590source = "registry+https://github.com/rust-lang/crates.io-index"
1581checksum = "bb37da98a55b1d08529362d9cbb863be17556873df2585904ab9d2bc951291d0" 1591checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
1582dependencies = [ 1592dependencies = [
1583 "proc-macro2", 1593 "proc-macro2",
1584 "quote", 1594 "quote",
@@ -1587,9 +1597,9 @@ dependencies = [
1587 1597
1588[[package]] 1598[[package]]
1589name = "synstructure" 1599name = "synstructure"
1590version = "0.12.3" 1600version = "0.12.4"
1591source = "registry+https://github.com/rust-lang/crates.io-index" 1601source = "registry+https://github.com/rust-lang/crates.io-index"
1592checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" 1602checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
1593dependencies = [ 1603dependencies = [
1594 "proc-macro2", 1604 "proc-macro2",
1595 "quote", 1605 "quote",
@@ -1639,6 +1649,7 @@ dependencies = [
1639 "relative-path", 1649 "relative-path",
1640 "rustc-hash", 1650 "rustc-hash",
1641 "serde_json", 1651 "serde_json",
1652 "stdx",
1642 "text-size", 1653 "text-size",
1643] 1654]
1644 1655
@@ -1797,9 +1808,9 @@ dependencies = [
1797 1808
1798[[package]] 1809[[package]]
1799name = "yaml-rust" 1810name = "yaml-rust"
1800version = "0.4.3" 1811version = "0.4.4"
1801source = "registry+https://github.com/rust-lang/crates.io-index" 1812source = "registry+https://github.com/rust-lang/crates.io-index"
1802checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" 1813checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
1803dependencies = [ 1814dependencies = [
1804 "linked-hash-map", 1815 "linked-hash-map",
1805] 1816]
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index 5b1a4680b..edd8255f4 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -1,5 +1,7 @@
1//! See `AssistContext` 1//! See `AssistContext`
2 2
3use std::mem;
4
3use algo::find_covering_element; 5use algo::find_covering_element;
4use hir::Semantics; 6use hir::Semantics;
5use ra_db::{FileId, FileRange}; 7use ra_db::{FileId, FileRange};
@@ -170,13 +172,32 @@ impl Assists {
170 172
171pub(crate) struct AssistBuilder { 173pub(crate) struct AssistBuilder {
172 edit: TextEditBuilder, 174 edit: TextEditBuilder,
173 file: FileId, 175 file_id: FileId,
174 is_snippet: bool, 176 is_snippet: bool,
177 edits: Vec<SourceFileEdit>,
175} 178}
176 179
177impl AssistBuilder { 180impl AssistBuilder {
178 pub(crate) fn new(file: FileId) -> AssistBuilder { 181 pub(crate) fn new(file_id: FileId) -> AssistBuilder {
179 AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false } 182 AssistBuilder {
183 edit: TextEditBuilder::default(),
184 file_id,
185 is_snippet: false,
186 edits: Vec::new(),
187 }
188 }
189
190 pub(crate) fn edit_file(&mut self, file_id: FileId) {
191 self.file_id = file_id;
192 }
193
194 fn commit(&mut self) {
195 let edit = mem::take(&mut self.edit).finish();
196 if !edit.is_empty() {
197 let new_edit = SourceFileEdit { file_id: self.file_id, edit };
198 assert!(!self.edits.iter().any(|it| it.file_id == new_edit.file_id));
199 self.edits.push(new_edit);
200 }
180 } 201 }
181 202
182 /// Remove specified `range` of text. 203 /// Remove specified `range` of text.
@@ -234,21 +255,15 @@ impl AssistBuilder {
234 algo::diff(&node, &new).into_text_edit(&mut self.edit) 255 algo::diff(&node, &new).into_text_edit(&mut self.edit)
235 } 256 }
236 257
237 // FIXME: better API
238 pub(crate) fn set_file(&mut self, assist_file: FileId) {
239 self.file = assist_file;
240 }
241
242 // FIXME: kill this API 258 // FIXME: kill this API
243 /// Get access to the raw `TextEditBuilder`. 259 /// Get access to the raw `TextEditBuilder`.
244 pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { 260 pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder {
245 &mut self.edit 261 &mut self.edit
246 } 262 }
247 263
248 fn finish(self) -> SourceChange { 264 fn finish(mut self) -> SourceChange {
249 let edit = self.edit.finish(); 265 self.commit();
250 let source_file_edit = SourceFileEdit { file_id: self.file, edit }; 266 let mut res: SourceChange = mem::take(&mut self.edits).into();
251 let mut res: SourceChange = source_file_edit.into();
252 if self.is_snippet { 267 if self.is_snippet {
253 res.is_snippet = true; 268 res.is_snippet = true;
254 } 269 }
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index ab20c6649..90b06a625 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -195,7 +195,7 @@ struct Test<K, T = u8> {
195} 195}
196 196
197fn main() { 197fn main() {
198 let test<|> = Test { t: 23, k: 33 }; 198 let test<|> = Test { t: 23u8, k: 33 };
199}"#, 199}"#,
200 r#" 200 r#"
201struct Test<K, T = u8> { 201struct Test<K, T = u8> {
@@ -204,7 +204,7 @@ struct Test<K, T = u8> {
204} 204}
205 205
206fn main() { 206fn main() {
207 let test: Test<i32> = Test { t: 23, k: 33 }; 207 let test: Test<i32> = Test { t: 23u8, k: 33 };
208}"#, 208}"#,
209 ); 209 );
210 } 210 }
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs
index 24f931a85..1cfbd75aa 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/add_function.rs
@@ -64,7 +64,7 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
64 let target = call.syntax().text_range(); 64 let target = call.syntax().text_range();
65 acc.add(AssistId("add_function"), "Add function", target, |builder| { 65 acc.add(AssistId("add_function"), "Add function", target, |builder| {
66 let function_template = function_builder.render(); 66 let function_template = function_builder.render();
67 builder.set_file(function_template.file); 67 builder.edit_file(function_template.file);
68 let new_fn = function_template.to_string(ctx.config.snippet_cap); 68 let new_fn = function_template.to_string(ctx.config.snippet_cap);
69 match ctx.config.snippet_cap { 69 match ctx.config.snippet_cap {
70 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), 70 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn),
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index edf96d50e..5092bf336 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -130,7 +130,7 @@ impl AutoImportAssets {
130 fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { 130 fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> {
131 let _p = profile("auto_import::search_for_imports"); 131 let _p = profile("auto_import::search_for_imports");
132 let current_crate = self.module_with_name_to_import.krate(); 132 let current_crate = self.module_with_name_to_import.krate();
133 ImportsLocator::new(db) 133 ImportsLocator::new(db, current_crate)
134 .find_imports(&self.get_search_query()) 134 .find_imports(&self.get_search_query())
135 .into_iter() 135 .into_iter()
136 .filter_map(|candidate| match &self.import_candidate { 136 .filter_map(|candidate| match &self.import_candidate {
@@ -841,4 +841,105 @@ fn main() {
841 ", 841 ",
842 ) 842 )
843 } 843 }
844
845 #[test]
846 fn dep_import() {
847 check_assist(
848 auto_import,
849 r"
850 //- /lib.rs crate:dep
851 pub struct Struct;
852
853 //- /main.rs crate:main deps:dep
854 fn main() {
855 Struct<|>
856 }",
857 r"use dep::Struct;
858
859fn main() {
860 Struct
861}
862",
863 );
864 }
865
866 #[test]
867 fn whole_segment() {
868 // Tests that only imports whose last segment matches the identifier get suggested.
869 check_assist(
870 auto_import,
871 r"
872 //- /lib.rs crate:dep
873 pub mod fmt {
874 pub trait Display {}
875 }
876
877 pub fn panic_fmt() {}
878
879 //- /main.rs crate:main deps:dep
880 struct S;
881
882 impl f<|>mt::Display for S {}",
883 r"use dep::fmt;
884
885struct S;
886impl fmt::Display for S {}
887",
888 );
889 }
890
891 #[test]
892 fn macro_generated() {
893 // Tests that macro-generated items are suggested from external crates.
894 check_assist(
895 auto_import,
896 r"
897 //- /lib.rs crate:dep
898
899 macro_rules! mac {
900 () => {
901 pub struct Cheese;
902 };
903 }
904
905 mac!();
906
907 //- /main.rs crate:main deps:dep
908
909 fn main() {
910 Cheese<|>;
911 }",
912 r"use dep::Cheese;
913
914fn main() {
915 Cheese;
916}
917",
918 );
919 }
920
921 #[test]
922 fn casing() {
923 // Tests that differently cased names don't interfere and we only suggest the matching one.
924 check_assist(
925 auto_import,
926 r"
927 //- /lib.rs crate:dep
928
929 pub struct FMT;
930 pub struct fmt;
931
932 //- /main.rs crate:main deps:dep
933
934 fn main() {
935 FMT<|>;
936 }",
937 r"use dep::FMT;
938
939fn main() {
940 FMT;
941}
942",
943 );
944 }
844} 945}
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs
index 4cc75a7ce..dfade7432 100644
--- a/crates/ra_assists/src/handlers/early_return.rs
+++ b/crates/ra_assists/src/handlers/early_return.rs
@@ -154,7 +154,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
154 parent_block: &ast::BlockExpr, 154 parent_block: &ast::BlockExpr,
155 if_expr: &ast::IfExpr, 155 if_expr: &ast::IfExpr,
156 ) -> SyntaxNode { 156 ) -> SyntaxNode {
157 let then_block_items = then_block.dedent(IndentLevel::from(1)); 157 let then_block_items = then_block.dedent(IndentLevel(1));
158 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); 158 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
159 let end_of_then = 159 let end_of_then =
160 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { 160 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
new file mode 100644
index 000000000..44db7917a
--- /dev/null
+++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -0,0 +1,320 @@
1use hir::{EnumVariant, Module, ModuleDef, Name};
2use ra_db::FileId;
3use ra_fmt::leading_indent;
4use ra_ide_db::{defs::Definition, search::Reference, RootDatabase};
5use ra_syntax::{
6 algo::find_node_at_offset,
7 ast::{self, ArgListOwner, AstNode, NameOwner, VisibilityOwner},
8 SourceFile, SyntaxNode, TextRange, TextSize,
9};
10use rustc_hash::FxHashSet;
11
12use crate::{
13 assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId, Assists,
14};
15
16// Assist: extract_struct_from_enum_variant
17//
18// Extracts a struct from enum variant.
19//
20// ```
21// enum A { <|>One(u32, u32) }
22// ```
23// ->
24// ```
25// struct One(pub u32, pub u32);
26//
27// enum A { One(One) }
28// ```
29pub(crate) fn extract_struct_from_enum_variant(
30 acc: &mut Assists,
31 ctx: &AssistContext,
32) -> Option<()> {
33 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
34 let field_list = match variant.kind() {
35 ast::StructKind::Tuple(field_list) => field_list,
36 _ => return None,
37 };
38 let variant_name = variant.name()?.to_string();
39 let variant_hir = ctx.sema.to_def(&variant)?;
40 if existing_struct_def(ctx.db, &variant_name, &variant_hir) {
41 return None;
42 }
43 let enum_ast = variant.parent_enum();
44 let visibility = enum_ast.visibility();
45 let enum_hir = ctx.sema.to_def(&enum_ast)?;
46 let variant_hir_name = variant_hir.name(ctx.db);
47 let enum_module_def = ModuleDef::from(enum_hir);
48 let current_module = enum_hir.module(ctx.db);
49 let target = variant.syntax().text_range();
50 acc.add(
51 AssistId("extract_struct_from_enum_variant"),
52 "Extract struct from enum variant",
53 target,
54 |builder| {
55 let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir));
56 let res = definition.find_usages(&ctx.db, None);
57 let start_offset = variant.parent_enum().syntax().text_range().start();
58 let mut visited_modules_set = FxHashSet::default();
59 visited_modules_set.insert(current_module);
60 for reference in res {
61 let source_file = ctx.sema.parse(reference.file_range.file_id);
62 update_reference(
63 ctx,
64 builder,
65 reference,
66 &source_file,
67 &enum_module_def,
68 &variant_hir_name,
69 &mut visited_modules_set,
70 );
71 }
72 extract_struct_def(
73 builder,
74 enum_ast.syntax(),
75 &variant_name,
76 &field_list.to_string(),
77 start_offset,
78 ctx.frange.file_id,
79 &visibility,
80 );
81 let list_range = field_list.syntax().text_range();
82 update_variant(builder, &variant_name, ctx.frange.file_id, list_range);
83 },
84 )
85}
86
87fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVariant) -> bool {
88 variant
89 .parent_enum(db)
90 .module(db)
91 .scope(db, None)
92 .into_iter()
93 .any(|(name, _)| name.to_string() == variant_name.to_string())
94}
95
96fn insert_import(
97 ctx: &AssistContext,
98 builder: &mut AssistBuilder,
99 path: &ast::PathExpr,
100 module: &Module,
101 enum_module_def: &ModuleDef,
102 variant_hir_name: &Name,
103) -> Option<()> {
104 let db = ctx.db;
105 let mod_path = module.find_use_path(db, enum_module_def.clone());
106 if let Some(mut mod_path) = mod_path {
107 mod_path.segments.pop();
108 mod_path.segments.push(variant_hir_name.clone());
109 insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder());
110 }
111 Some(())
112}
113
114fn extract_struct_def(
115 builder: &mut AssistBuilder,
116 enum_ast: &SyntaxNode,
117 variant_name: &str,
118 variant_list: &str,
119 start_offset: TextSize,
120 file_id: FileId,
121 visibility: &Option<ast::Visibility>,
122) -> Option<()> {
123 let visibility_string = if let Some(visibility) = visibility {
124 format!("{} ", visibility.to_string())
125 } else {
126 "".to_string()
127 };
128 let indent = if let Some(indent) = leading_indent(enum_ast) {
129 indent.to_string()
130 } else {
131 "".to_string()
132 };
133 let struct_def = format!(
134 r#"{}struct {}{};
135
136{}"#,
137 visibility_string,
138 variant_name,
139 list_with_visibility(variant_list),
140 indent
141 );
142 builder.edit_file(file_id);
143 builder.insert(start_offset, struct_def);
144 Some(())
145}
146
147fn update_variant(
148 builder: &mut AssistBuilder,
149 variant_name: &str,
150 file_id: FileId,
151 list_range: TextRange,
152) -> Option<()> {
153 let inside_variant_range = TextRange::new(
154 list_range.start().checked_add(TextSize::from(1))?,
155 list_range.end().checked_sub(TextSize::from(1))?,
156 );
157 builder.edit_file(file_id);
158 builder.replace(inside_variant_range, variant_name);
159 Some(())
160}
161
162fn update_reference(
163 ctx: &AssistContext,
164 builder: &mut AssistBuilder,
165 reference: Reference,
166 source_file: &SourceFile,
167 enum_module_def: &ModuleDef,
168 variant_hir_name: &Name,
169 visited_modules_set: &mut FxHashSet<Module>,
170) -> Option<()> {
171 let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>(
172 source_file.syntax(),
173 reference.file_range.range.start(),
174 )?;
175 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
176 let list = call.arg_list()?;
177 let segment = path_expr.path()?.segment()?;
178 let module = ctx.sema.scope(&path_expr.syntax()).module()?;
179 let list_range = list.syntax().text_range();
180 let inside_list_range = TextRange::new(
181 list_range.start().checked_add(TextSize::from(1))?,
182 list_range.end().checked_sub(TextSize::from(1))?,
183 );
184 builder.edit_file(reference.file_range.file_id);
185 if !visited_modules_set.contains(&module) {
186 if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name)
187 .is_some()
188 {
189 visited_modules_set.insert(module);
190 }
191 }
192 builder.replace(inside_list_range, format!("{}{}", segment, list));
193 Some(())
194}
195
196fn list_with_visibility(list: &str) -> String {
197 list.split(',')
198 .map(|part| {
199 let index = if part.chars().next().unwrap() == '(' { 1usize } else { 0 };
200 let mut mod_part = part.trim().to_string();
201 mod_part.insert_str(index, "pub ");
202 mod_part
203 })
204 .collect::<Vec<String>>()
205 .join(", ")
206}
207
208#[cfg(test)]
209mod tests {
210
211 use crate::{
212 tests::{check_assist, check_assist_not_applicable},
213 utils::FamousDefs,
214 };
215
216 use super::*;
217
218 #[test]
219 fn test_extract_struct_several_fields() {
220 check_assist(
221 extract_struct_from_enum_variant,
222 "enum A { <|>One(u32, u32) }",
223 r#"struct One(pub u32, pub u32);
224
225enum A { One(One) }"#,
226 );
227 }
228
229 #[test]
230 fn test_extract_struct_one_field() {
231 check_assist(
232 extract_struct_from_enum_variant,
233 "enum A { <|>One(u32) }",
234 r#"struct One(pub u32);
235
236enum A { One(One) }"#,
237 );
238 }
239
240 #[test]
241 fn test_extract_struct_pub_visibility() {
242 check_assist(
243 extract_struct_from_enum_variant,
244 "pub enum A { <|>One(u32, u32) }",
245 r#"pub struct One(pub u32, pub u32);
246
247pub enum A { One(One) }"#,
248 );
249 }
250
251 #[test]
252 fn test_extract_struct_with_complex_imports() {
253 check_assist(
254 extract_struct_from_enum_variant,
255 r#"mod my_mod {
256 fn another_fn() {
257 let m = my_other_mod::MyEnum::MyField(1, 1);
258 }
259
260 pub mod my_other_mod {
261 fn another_fn() {
262 let m = MyEnum::MyField(1, 1);
263 }
264
265 pub enum MyEnum {
266 <|>MyField(u8, u8),
267 }
268 }
269}
270
271fn another_fn() {
272 let m = my_mod::my_other_mod::MyEnum::MyField(1, 1);
273}"#,
274 r#"use my_mod::my_other_mod::MyField;
275
276mod my_mod {
277 use my_other_mod::MyField;
278
279 fn another_fn() {
280 let m = my_other_mod::MyEnum::MyField(MyField(1, 1));
281 }
282
283 pub mod my_other_mod {
284 fn another_fn() {
285 let m = MyEnum::MyField(MyField(1, 1));
286 }
287
288 pub struct MyField(pub u8, pub u8);
289
290 pub enum MyEnum {
291 MyField(MyField),
292 }
293 }
294}
295
296fn another_fn() {
297 let m = my_mod::my_other_mod::MyEnum::MyField(MyField(1, 1));
298}"#,
299 );
300 }
301
302 fn check_not_applicable(ra_fixture: &str) {
303 let fixture =
304 format!("//- main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
305 check_assist_not_applicable(extract_struct_from_enum_variant, &fixture)
306 }
307
308 #[test]
309 fn test_extract_enum_not_applicable_for_element_with_no_fields() {
310 check_not_applicable("enum A { <|>One }");
311 }
312
313 #[test]
314 fn test_extract_enum_not_applicable_if_struct_exists() {
315 check_not_applicable(
316 r#"struct One;
317 enum A { <|>One(u8) }"#,
318 );
319 }
320}
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs
index 9ec42f568..531b3560f 100644
--- a/crates/ra_assists/src/handlers/fix_visibility.rs
+++ b/crates/ra_assists/src/handlers/fix_visibility.rs
@@ -63,7 +63,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
63 }; 63 };
64 64
65 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { 65 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| {
66 builder.set_file(target_file); 66 builder.edit_file(target_file);
67 match ctx.config.snippet_cap { 67 match ctx.config.snippet_cap {
68 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), 68 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
69 None => builder.insert(offset, format!("{} ", missing_visibility)), 69 None => builder.insert(offset, format!("{} ", missing_visibility)),
@@ -106,7 +106,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
106 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); 106 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
107 107
108 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { 108 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| {
109 builder.set_file(target_file); 109 builder.edit_file(target_file);
110 match ctx.config.snippet_cap { 110 match ctx.config.snippet_cap {
111 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), 111 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
112 None => builder.insert(offset, format!("{} ", missing_visibility)), 112 None => builder.insert(offset, format!("{} ", missing_visibility)),
diff --git a/crates/ra_assists/src/handlers/introduce_named_lifetime.rs b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs
index beb5b7366..28fcbc9ba 100644
--- a/crates/ra_assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs
@@ -41,8 +41,6 @@ pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -
41 if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) { 41 if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) {
42 generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range()) 42 generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range())
43 } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) { 43 } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) {
44 // only allow naming the last anonymous lifetime
45 lifetime_token.next_token().filter(|tok| tok.kind() == SyntaxKind::R_ANGLE)?;
46 generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range()) 44 generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range())
47 } else { 45 } else {
48 None 46 None
@@ -191,6 +189,23 @@ mod tests {
191 } 189 }
192 190
193 #[test] 191 #[test]
192 fn test_impl_with_other_type_param() {
193 check_assist(
194 introduce_named_lifetime,
195 "impl<I> fmt::Display for SepByBuilder<'_<|>, I>
196 where
197 I: Iterator,
198 I::Item: fmt::Display,
199 {",
200 "impl<I, 'a> fmt::Display for SepByBuilder<'a, I>
201 where
202 I: Iterator,
203 I::Item: fmt::Display,
204 {",
205 )
206 }
207
208 #[test]
194 fn test_example_case_cursor_before_tick() { 209 fn test_example_case_cursor_before_tick() {
195 check_assist( 210 check_assist(
196 introduce_named_lifetime, 211 introduce_named_lifetime,
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
index e016f51c3..dfcd787de 100644
--- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
@@ -51,6 +51,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
51 acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| { 51 acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| {
52 let match_expr = { 52 let match_expr = {
53 let then_arm = { 53 let then_arm = {
54 let then_block = then_block.reset_indent().indent(IndentLevel(1));
54 let then_expr = unwrap_trivial_block(then_block); 55 let then_expr = unwrap_trivial_block(then_block);
55 make::match_arm(vec![pat.clone()], then_expr) 56 make::match_arm(vec![pat.clone()], then_expr)
56 }; 57 };
@@ -64,8 +65,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
64 let else_expr = unwrap_trivial_block(else_block); 65 let else_expr = unwrap_trivial_block(else_block);
65 make::match_arm(vec![pattern], else_expr) 66 make::match_arm(vec![pattern], else_expr)
66 }; 67 };
67 make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) 68 let match_expr = make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]));
68 .indent(IndentLevel::from_node(if_expr.syntax())) 69 match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
69 }; 70 };
70 71
71 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); 72 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
@@ -213,4 +214,36 @@ fn foo(x: Result<i32, ()>) {
213 "#, 214 "#,
214 ); 215 );
215 } 216 }
217
218 #[test]
219 fn nested_indent() {
220 check_assist(
221 replace_if_let_with_match,
222 r#"
223fn main() {
224 if true {
225 <|>if let Ok(rel_path) = path.strip_prefix(root_path) {
226 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
227 Some((*id, rel_path))
228 } else {
229 None
230 }
231 }
232}
233"#,
234 r#"
235fn main() {
236 if true {
237 match path.strip_prefix(root_path) {
238 Ok(rel_path) => {
239 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
240 Some((*id, rel_path))
241 }
242 _ => None,
243 }
244 }
245}
246"#,
247 )
248 }
216} 249}
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs
index 8440c7d0f..1fb13f481 100644
--- a/crates/ra_assists/src/handlers/unwrap_block.rs
+++ b/crates/ra_assists/src/handlers/unwrap_block.rs
@@ -1,7 +1,10 @@
1use ra_fmt::unwrap_trivial_block; 1use ra_fmt::unwrap_trivial_block;
2use ra_syntax::{ 2use ra_syntax::{
3 ast::{self, ElseBranch, Expr, LoopBodyOwner}, 3 ast::{
4 match_ast, AstNode, TextRange, T, 4 self,
5 edit::{AstNodeEdit, IndentLevel},
6 },
7 AstNode, TextRange, T,
5}; 8};
6 9
7use crate::{AssistContext, AssistId, Assists}; 10use crate::{AssistContext, AssistId, Assists};
@@ -24,94 +27,73 @@ use crate::{AssistContext, AssistId, Assists};
24// } 27// }
25// ``` 28// ```
26pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 29pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
27 let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
28 let block = ast::BlockExpr::cast(l_curly_token.parent())?;
29 let parent = block.syntax().parent()?;
30 let assist_id = AssistId("unwrap_block"); 30 let assist_id = AssistId("unwrap_block");
31 let assist_label = "Unwrap block"; 31 let assist_label = "Unwrap block";
32 32
33 let (expr, expr_to_unwrap) = match_ast! { 33 let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
34 match parent { 34 let mut block = ast::BlockExpr::cast(l_curly_token.parent())?;
35 ast::ForExpr(for_expr) => { 35 let mut parent = block.syntax().parent()?;
36 let block_expr = for_expr.loop_body()?; 36 if ast::MatchArm::can_cast(parent.kind()) {
37 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; 37 parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
38 (ast::Expr::ForExpr(for_expr), expr_to_unwrap) 38 }
39 },
40 ast::WhileExpr(while_expr) => {
41 let block_expr = while_expr.loop_body()?;
42 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
43 (ast::Expr::WhileExpr(while_expr), expr_to_unwrap)
44 },
45 ast::LoopExpr(loop_expr) => {
46 let block_expr = loop_expr.loop_body()?;
47 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
48 (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)
49 },
50 ast::IfExpr(if_expr) => {
51 let mut resp = None;
52
53 let then_branch = if_expr.then_branch()?;
54 if then_branch.l_curly_token()?.text_range().contains_range(ctx.frange.range) {
55 if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
56 // For `else if` blocks
57 let ancestor_then_branch = ancestor.then_branch()?;
58 let l_curly_token = then_branch.l_curly_token()?;
59
60 let target = then_branch.syntax().text_range();
61 return acc.add(assist_id, assist_label, target, |edit| {
62 let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
63 let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end());
64
65 edit.delete(range_to_del_rest);
66 edit.delete(range_to_del_else_if);
67 edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{']));
68 });
69 } else {
70 resp = Some((ast::Expr::IfExpr(if_expr.clone()), Expr::BlockExpr(then_branch)));
71 }
72 } else if let Some(else_branch) = if_expr.else_branch() {
73 match else_branch {
74 ElseBranch::Block(else_block) => {
75 let l_curly_token = else_block.l_curly_token()?;
76 if l_curly_token.text_range().contains_range(ctx.frange.range) {
77 let target = else_block.syntax().text_range();
78 return acc.add(assist_id, assist_label, target, |edit| {
79 let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
80
81 edit.delete(range_to_del);
82 edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{']));
83 });
84 }
85 },
86 ElseBranch::IfExpr(_) => {},
87 }
88 }
89 39
90 resp? 40 let parent = ast::Expr::cast(parent)?;
91 }, 41
92 _ => return None, 42 match parent.clone() {
43 ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (),
44 ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)),
45 ast::Expr::IfExpr(if_expr) => {
46 let then_branch = if_expr.then_branch()?;
47 if then_branch == block {
48 if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
49 // For `else if` blocks
50 let ancestor_then_branch = ancestor.then_branch()?;
51
52 let target = then_branch.syntax().text_range();
53 return acc.add(assist_id, assist_label, target, |edit| {
54 let range_to_del_else_if = TextRange::new(
55 ancestor_then_branch.syntax().text_range().end(),
56 l_curly_token.text_range().start(),
57 );
58 let range_to_del_rest = TextRange::new(
59 then_branch.syntax().text_range().end(),
60 if_expr.syntax().text_range().end(),
61 );
62
63 edit.delete(range_to_del_rest);
64 edit.delete(range_to_del_else_if);
65 edit.replace(
66 target,
67 update_expr_string(then_branch.to_string(), &[' ', '{']),
68 );
69 });
70 }
71 } else {
72 let target = block.syntax().text_range();
73 return acc.add(assist_id, assist_label, target, |edit| {
74 let range_to_del = TextRange::new(
75 then_branch.syntax().text_range().end(),
76 l_curly_token.text_range().start(),
77 );
78
79 edit.delete(range_to_del);
80 edit.replace(target, update_expr_string(block.to_string(), &[' ', '{']));
81 });
82 }
93 } 83 }
84 _ => return None,
94 }; 85 };
95 86
96 let target = expr_to_unwrap.syntax().text_range(); 87 let unwrapped = unwrap_trivial_block(block);
97 acc.add(assist_id, assist_label, target, |edit| { 88 let target = unwrapped.syntax().text_range();
98 edit.replace( 89 acc.add(assist_id, assist_label, target, |builder| {
99 expr.syntax().text_range(), 90 builder.replace(
100 update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), 91 parent.syntax().text_range(),
92 update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']),
101 ); 93 );
102 }) 94 })
103} 95}
104 96
105fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::Expr> {
106 let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range);
107
108 if cursor_in_range {
109 Some(unwrap_trivial_block(block))
110 } else {
111 None
112 }
113}
114
115fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String { 97fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String {
116 let expr_string = expr_str.trim_start_matches(trim_start_pat); 98 let expr_string = expr_str.trim_start_matches(trim_start_pat);
117 let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); 99 let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
@@ -490,6 +472,30 @@ mod tests {
490 } 472 }
491 473
492 #[test] 474 #[test]
475 fn unwrap_match_arm() {
476 check_assist(
477 unwrap_block,
478 r#"
479fn main() {
480 match rel_path {
481 Ok(rel_path) => {<|>
482 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
483 Some((*id, rel_path))
484 }
485 Err(_) => None,
486 }
487}
488"#,
489 r#"
490fn main() {
491 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
492 Some((*id, rel_path))
493}
494"#,
495 );
496 }
497
498 #[test]
493 fn simple_if_in_while_bad_cursor_position() { 499 fn simple_if_in_while_bad_cursor_position() {
494 check_assist_not_applicable( 500 check_assist_not_applicable(
495 unwrap_block, 501 unwrap_block,
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index fb5d59a87..185428bd5 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -115,6 +115,7 @@ mod handlers {
115 mod change_return_type_to_result; 115 mod change_return_type_to_result;
116 mod change_visibility; 116 mod change_visibility;
117 mod early_return; 117 mod early_return;
118 mod extract_struct_from_enum_variant;
118 mod fill_match_arms; 119 mod fill_match_arms;
119 mod fix_visibility; 120 mod fix_visibility;
120 mod flip_binexpr; 121 mod flip_binexpr;
@@ -155,6 +156,7 @@ mod handlers {
155 change_return_type_to_result::change_return_type_to_result, 156 change_return_type_to_result::change_return_type_to_result,
156 change_visibility::change_visibility, 157 change_visibility::change_visibility,
157 early_return::convert_to_guarded_return, 158 early_return::convert_to_guarded_return,
159 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
158 fill_match_arms::fill_match_arms, 160 fill_match_arms::fill_match_arms,
159 fix_visibility::fix_visibility, 161 fix_visibility::fix_visibility,
160 flip_binexpr::flip_binexpr, 162 flip_binexpr::flip_binexpr,
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs
index d17504529..40a223727 100644
--- a/crates/ra_assists/src/tests/generated.rs
+++ b/crates/ra_assists/src/tests/generated.rs
@@ -338,6 +338,21 @@ fn main() {
338} 338}
339 339
340#[test] 340#[test]
341fn doctest_extract_struct_from_enum_variant() {
342 check_doc_test(
343 "extract_struct_from_enum_variant",
344 r#####"
345enum A { <|>One(u32, u32) }
346"#####,
347 r#####"
348struct One(pub u32, pub u32);
349
350enum A { One(One) }
351"#####,
352 )
353}
354
355#[test]
341fn doctest_fill_match_arms() { 356fn doctest_fill_match_arms() {
342 check_doc_test( 357 check_doc_test(
343 "fill_match_arms", 358 "fill_match_arms",
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 4d2d3b48a..bf26048f2 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -15,12 +15,10 @@ use std::{
15 15
16use ra_cfg::CfgOptions; 16use ra_cfg::CfgOptions;
17use ra_syntax::SmolStr; 17use ra_syntax::SmolStr;
18use rustc_hash::FxHashMap; 18use ra_tt::TokenExpander;
19use rustc_hash::FxHashSet; 19use rustc_hash::{FxHashMap, FxHashSet};
20 20
21use crate::{RelativePath, RelativePathBuf}; 21use crate::{RelativePath, RelativePathBuf};
22use fmt::Display;
23use ra_tt::TokenExpander;
24 22
25/// `FileId` is an integer which uniquely identifies a file. File paths are 23/// `FileId` is an integer which uniquely identifies a file. File paths are
26/// messy and system-dependent, so most of the code should work directly with 24/// messy and system-dependent, so most of the code should work directly with
@@ -111,7 +109,7 @@ impl CrateName {
111 } 109 }
112} 110}
113 111
114impl Display for CrateName { 112impl fmt::Display for CrateName {
115 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 113 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116 write!(f, "{}", self.0) 114 write!(f, "{}", self.0)
117 } 115 }
@@ -337,15 +335,11 @@ impl Env {
337} 335}
338 336
339impl ExternSource { 337impl ExternSource {
340 pub fn extern_path(&self, path: impl AsRef<Path>) -> Option<(ExternSourceId, RelativePathBuf)> { 338 pub fn extern_path(&self, path: &Path) -> Option<(ExternSourceId, RelativePathBuf)> {
341 let path = path.as_ref();
342 self.extern_paths.iter().find_map(|(root_path, id)| { 339 self.extern_paths.iter().find_map(|(root_path, id)| {
343 if let Ok(rel_path) = path.strip_prefix(root_path) { 340 let rel_path = path.strip_prefix(root_path).ok()?;
344 let rel_path = RelativePathBuf::from_path(rel_path).ok()?; 341 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
345 Some((*id, rel_path)) 342 Some((*id, rel_path))
346 } else {
347 None
348 }
349 }) 343 })
350 } 344 }
351 345
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index fd4280de2..80ddb6058 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -7,12 +7,13 @@ use std::{panic, sync::Arc};
7 7
8use ra_prof::profile; 8use ra_prof::profile;
9use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; 9use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize};
10use rustc_hash::FxHashSet;
10 11
11pub use crate::{ 12pub use crate::{
12 cancellation::Canceled, 13 cancellation::Canceled,
13 input::{ 14 input::{
14 CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId, 15 CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource,
15 FileId, ProcMacroId, SourceRoot, SourceRootId, 16 ExternSourceId, FileId, ProcMacroId, SourceRoot, SourceRootId,
16 }, 17 },
17}; 18};
18pub use relative_path::{RelativePath, RelativePathBuf}; 19pub use relative_path::{RelativePath, RelativePathBuf};
@@ -89,15 +90,13 @@ pub const DEFAULT_LRU_CAP: usize = 128;
89pub trait FileLoader { 90pub trait FileLoader {
90 /// Text of the file. 91 /// Text of the file.
91 fn file_text(&self, file_id: FileId) -> Arc<String>; 92 fn file_text(&self, file_id: FileId) -> Arc<String>;
92 fn resolve_relative_path(&self, anchor: FileId, relative_path: &RelativePath) 93 /// Note that we intentionally accept a `&str` and not a `&Path` here. This
93 -> Option<FileId>; 94 /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such,
94 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>; 95 /// so the input is guaranteed to be utf-8 string. We might introduce
95 96 /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we
96 fn resolve_extern_path( 97 /// get by with a `&str` for the time being.
97 &self, 98 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>;
98 extern_id: ExternSourceId, 99 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>;
99 relative_path: &RelativePath,
100 ) -> Option<FileId>;
101} 100}
102 101
103/// Database which stores all significant input facts: source code and project 102/// Database which stores all significant input facts: source code and project
@@ -135,16 +134,21 @@ pub trait SourceDatabaseExt: SourceDatabase {
135 #[salsa::input] 134 #[salsa::input]
136 fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; 135 fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
137 136
138 fn source_root_crates(&self, id: SourceRootId) -> Arc<Vec<CrateId>>; 137 fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>;
139} 138}
140 139
141fn source_root_crates( 140fn source_root_crates(
142 db: &(impl SourceDatabaseExt + SourceDatabase), 141 db: &(impl SourceDatabaseExt + SourceDatabase),
143 id: SourceRootId, 142 id: SourceRootId,
144) -> Arc<Vec<CrateId>> { 143) -> Arc<FxHashSet<CrateId>> {
145 let root = db.source_root(id);
146 let graph = db.crate_graph(); 144 let graph = db.crate_graph();
147 let res = root.walk().filter_map(|it| graph.crate_id_for_crate_root(it)).collect::<Vec<_>>(); 145 let res = graph
146 .iter()
147 .filter(|&krate| {
148 let root_file = graph[krate].root_file_id;
149 db.file_source_root(root_file) == id
150 })
151 .collect::<FxHashSet<_>>();
148 Arc::new(res) 152 Arc::new(res)
149} 153}
150 154
@@ -155,33 +159,30 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
155 fn file_text(&self, file_id: FileId) -> Arc<String> { 159 fn file_text(&self, file_id: FileId) -> Arc<String> {
156 SourceDatabaseExt::file_text(self.0, file_id) 160 SourceDatabaseExt::file_text(self.0, file_id)
157 } 161 }
158 fn resolve_relative_path( 162 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
159 &self, 163 // FIXME: this *somehow* should be platform agnostic...
160 anchor: FileId, 164 if std::path::Path::new(path).is_absolute() {
161 relative_path: &RelativePath, 165 let krate = *self.relevant_crates(anchor).iter().next()?;
162 ) -> Option<FileId> { 166 let (extern_source_id, relative_file) =
163 let path = { 167 self.0.crate_graph()[krate].extern_source.extern_path(path.as_ref())?;
164 let mut path = self.0.file_relative_path(anchor); 168
165 assert!(path.pop()); 169 let source_root = self.0.source_root(SourceRootId(extern_source_id.0));
166 path.push(relative_path); 170 source_root.file_by_relative_path(&relative_file)
167 path.normalize() 171 } else {
168 }; 172 let rel_path = {
169 let source_root = self.0.file_source_root(anchor); 173 let mut rel_path = self.0.file_relative_path(anchor);
170 let source_root = self.0.source_root(source_root); 174 assert!(rel_path.pop());
171 source_root.file_by_relative_path(&path) 175 rel_path.push(path);
176 rel_path.normalize()
177 };
178 let source_root = self.0.file_source_root(anchor);
179 let source_root = self.0.source_root(source_root);
180 source_root.file_by_relative_path(&rel_path)
181 }
172 } 182 }
173 183
174 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 184 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
175 let source_root = self.0.file_source_root(file_id); 185 let source_root = self.0.file_source_root(file_id);
176 self.0.source_root_crates(source_root) 186 self.0.source_root_crates(source_root)
177 } 187 }
178
179 fn resolve_extern_path(
180 &self,
181 extern_id: ExternSourceId,
182 relative_path: &RelativePath,
183 ) -> Option<FileId> {
184 let source_root = self.0.source_root(SourceRootId(extern_id.0));
185 source_root.file_by_relative_path(&relative_path)
186 }
187} 188}
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs
index 041e38a9f..6c4170529 100644
--- a/crates/ra_flycheck/src/lib.rs
+++ b/crates/ra_flycheck/src/lib.rs
@@ -18,8 +18,17 @@ pub use cargo_metadata::diagnostic::{
18 18
19#[derive(Clone, Debug, PartialEq, Eq)] 19#[derive(Clone, Debug, PartialEq, Eq)]
20pub enum FlycheckConfig { 20pub enum FlycheckConfig {
21 CargoCommand { command: String, all_targets: bool, all_features: bool, extra_args: Vec<String> }, 21 CargoCommand {
22 CustomCommand { command: String, args: Vec<String> }, 22 command: String,
23 all_targets: bool,
24 all_features: bool,
25 features: Vec<String>,
26 extra_args: Vec<String>,
27 },
28 CustomCommand {
29 command: String,
30 args: Vec<String>,
31 },
23} 32}
24 33
25/// Flycheck wraps the shared state and communication machinery used for 34/// Flycheck wraps the shared state and communication machinery used for
@@ -188,7 +197,13 @@ impl FlycheckThread {
188 self.check_process = None; 197 self.check_process = None;
189 198
190 let mut cmd = match &self.config { 199 let mut cmd = match &self.config {
191 FlycheckConfig::CargoCommand { command, all_targets, all_features, extra_args } => { 200 FlycheckConfig::CargoCommand {
201 command,
202 all_targets,
203 all_features,
204 extra_args,
205 features,
206 } => {
192 let mut cmd = Command::new(ra_toolchain::cargo()); 207 let mut cmd = Command::new(ra_toolchain::cargo());
193 cmd.arg(command); 208 cmd.arg(command);
194 cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) 209 cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
@@ -198,6 +213,9 @@ impl FlycheckThread {
198 } 213 }
199 if *all_features { 214 if *all_features {
200 cmd.arg("--all-features"); 215 cmd.arg("--all-features");
216 } else if !features.is_empty() {
217 cmd.arg("--features");
218 cmd.arg(features.join(" "));
201 } 219 }
202 cmd.args(extra_args); 220 cmd.args(extra_args);
203 cmd 221 cmd
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index e40aeffbc..1a9f6cc76 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -9,6 +9,7 @@ use hir_def::{
9 builtin_type::BuiltinType, 9 builtin_type::BuiltinType,
10 docs::Documentation, 10 docs::Documentation,
11 expr::{BindingAnnotation, Pat, PatId}, 11 expr::{BindingAnnotation, Pat, PatId},
12 import_map,
12 per_ns::PerNs, 13 per_ns::PerNs,
13 resolver::{HasResolver, Resolver}, 14 resolver::{HasResolver, Resolver},
14 type_ref::{Mutability, TypeRef}, 15 type_ref::{Mutability, TypeRef},
@@ -98,6 +99,23 @@ impl Crate {
98 db.crate_graph()[self.id].display_name.as_ref().cloned() 99 db.crate_graph()[self.id].display_name.as_ref().cloned()
99 } 100 }
100 101
102 pub fn query_external_importables(
103 self,
104 db: &dyn DefDatabase,
105 query: &str,
106 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
107 import_map::search_dependencies(
108 db,
109 self.into(),
110 import_map::Query::new(query).anchor_end().case_sensitive().limit(40),
111 )
112 .into_iter()
113 .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
101 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { 119 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
102 db.crate_graph().iter().map(|id| Crate { id }).collect() 120 db.crate_graph().iter().map(|id| Crate { id }).collect()
103 } 121 }
@@ -637,6 +655,10 @@ impl Function {
637 db.function_data(self.id).params.clone() 655 db.function_data(self.id).params.clone()
638 } 656 }
639 657
658 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
659 db.function_data(self.id).is_unsafe
660 }
661
640 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 662 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
641 let _p = profile("Function::diagnostics"); 663 let _p = profile("Function::diagnostics");
642 let infer = db.infer(self.id.into()); 664 let infer = db.infer(self.id.into());
@@ -1190,6 +1212,10 @@ impl Type {
1190 ) 1212 )
1191 } 1213 }
1192 1214
1215 pub fn is_raw_ptr(&self) -> bool {
1216 matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }))
1217 }
1218
1193 pub fn contains_unknown(&self) -> bool { 1219 pub fn contains_unknown(&self) -> bool {
1194 return go(&self.ty.value); 1220 return go(&self.ty.value);
1195 1221
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index ec931b34f..b6b665de1 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -3,11 +3,11 @@
3pub use hir_def::db::{ 3pub use hir_def::db::{
4 AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery, 4 AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery,
5 CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery, 5 CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery,
6 ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, InternConstQuery, 6 ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
7 InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery, 7 InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
8 InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery, 8 InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
9 LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, StructDataQuery, 9 InternUnionQuery, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery,
10 TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, 10 StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
11}; 11};
12pub use hir_expand::db::{ 12pub use hir_expand::db::{
13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, 13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery,
@@ -18,8 +18,8 @@ pub use hir_ty::db::{
18 GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, 18 GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase,
19 HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsForTraitQuery, 19 HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsForTraitQuery,
20 ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery, 20 ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery,
21 InternTypeCtorQuery, InternTypeParamIdQuery, StructDatumQuery, TraitDatumQuery, 21 InternTypeCtorQuery, InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery,
22 TraitSolveQuery, TyQuery, ValueTyQuery, 22 TraitDatumQuery, TraitSolveQuery, TyQuery, ValueTyQuery,
23}; 23};
24 24
25#[test] 25#[test]
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 7c1f79f27..a232a5856 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -122,8 +122,9 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
122 let macro_call = 122 let macro_call =
123 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); 123 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call);
124 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); 124 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None);
125 let krate = sa.resolver.krate()?;
125 let macro_call_id = macro_call 126 let macro_call_id = macro_call
126 .as_call_id(self.db, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; 127 .as_call_id(self.db, krate, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?;
127 hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) 128 hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map)
128 } 129 }
129 130
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 4b509f07c..7c6bbea13 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -307,7 +307,8 @@ impl SourceAnalyzer {
307 db: &dyn HirDatabase, 307 db: &dyn HirDatabase,
308 macro_call: InFile<&ast::MacroCall>, 308 macro_call: InFile<&ast::MacroCall>,
309 ) -> Option<HirFileId> { 309 ) -> Option<HirFileId> {
310 let macro_call_id = macro_call.as_call_id(db.upcast(), |path| { 310 let krate = self.resolver.krate()?;
311 let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| {
311 self.resolver.resolve_path_as_macro(db.upcast(), &path) 312 self.resolver.resolve_path_as_macro(db.upcast(), &path)
312 })?; 313 })?;
313 Some(macro_call_id.as_file()) 314 Some(macro_call_id.as_file())
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index b85358308..ef1f65ee0 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -14,6 +14,9 @@ rustc-hash = "1.1.0"
14either = "1.5.3" 14either = "1.5.3"
15anymap = "0.12.1" 15anymap = "0.12.1"
16drop_bomb = "0.1.4" 16drop_bomb = "0.1.4"
17fst = { version = "0.4", default-features = false }
18itertools = "0.9.0"
19indexmap = "1.4.0"
17 20
18stdx = { path = "../stdx" } 21stdx = { path = "../stdx" }
19 22
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 8b6c0bede..2eeba0572 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -87,12 +87,18 @@ impl Attrs {
87 } 87 }
88 88
89 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { 89 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
90 let docs = ast::CommentIter::from_syntax_node(owner.syntax()).doc_comment_text().map(
91 |docs_text| Attr {
92 input: Some(AttrInput::Literal(SmolStr::new(docs_text))),
93 path: ModPath::from(hir_expand::name!(doc)),
94 },
95 );
90 let mut attrs = owner.attrs().peekable(); 96 let mut attrs = owner.attrs().peekable();
91 let entries = if attrs.peek().is_none() { 97 let entries = if attrs.peek().is_none() {
92 // Avoid heap allocation 98 // Avoid heap allocation
93 None 99 None
94 } else { 100 } else {
95 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect()) 101 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect())
96 }; 102 };
97 Attrs { entries } 103 Attrs { entries }
98 } 104 }
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 273036cee..4f2350915 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -97,7 +97,7 @@ impl Expander {
97 97
98 let macro_call = InFile::new(self.current_file_id, &macro_call); 98 let macro_call = InFile::new(self.current_file_id, &macro_call);
99 99
100 if let Some(call_id) = macro_call.as_call_id(db, |path| { 100 if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| {
101 if let Some(local_scope) = local_scope { 101 if let Some(local_scope) = local_scope {
102 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { 102 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
103 return Some(def); 103 return Some(def);
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index e2130d931..53599e74a 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -34,6 +34,7 @@ pub struct FunctionData {
34 /// True if the first param is `self`. This is relevant to decide whether this 34 /// True if the first param is `self`. This is relevant to decide whether this
35 /// can be called as a method. 35 /// can be called as a method.
36 pub has_self_param: bool, 36 pub has_self_param: bool,
37 pub is_unsafe: bool,
37 pub visibility: RawVisibility, 38 pub visibility: RawVisibility,
38} 39}
39 40
@@ -85,17 +86,20 @@ impl FunctionData {
85 ret_type 86 ret_type
86 }; 87 };
87 88
89 let is_unsafe = src.value.unsafe_token().is_some();
90
88 let vis_default = RawVisibility::default_for_container(loc.container); 91 let vis_default = RawVisibility::default_for_container(loc.container);
89 let visibility = 92 let visibility =
90 RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility())); 93 RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility()));
91 94
92 let sig = FunctionData { name, params, ret_type, has_self_param, visibility, attrs }; 95 let sig =
96 FunctionData { name, params, ret_type, has_self_param, is_unsafe, visibility, attrs };
93 Arc::new(sig) 97 Arc::new(sig)
94 } 98 }
95} 99}
96 100
97fn desugar_future_path(orig: TypeRef) -> Path { 101fn desugar_future_path(orig: TypeRef) -> Path {
98 let path = path![std::future::Future]; 102 let path = path![core::future::Future];
99 let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); 103 let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
100 let mut last = GenericArgs::empty(); 104 let mut last = GenericArgs::empty();
101 last.bindings.push(AssociatedTypeBinding { 105 last.bindings.push(AssociatedTypeBinding {
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
index 945a0025e..10cc26480 100644
--- a/crates/ra_hir_def/src/db.rs
+++ b/crates/ra_hir_def/src/db.rs
@@ -1,7 +1,7 @@
1//! Defines database & queries for name resolution. 1//! Defines database & queries for name resolution.
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use hir_expand::{db::AstDatabase, name::Name, HirFileId}; 4use hir_expand::{db::AstDatabase, HirFileId};
5use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; 5use ra_db::{salsa, CrateId, SourceDatabase, Upcast};
6use ra_prof::profile; 6use ra_prof::profile;
7use ra_syntax::SmolStr; 7use ra_syntax::SmolStr;
@@ -12,13 +12,10 @@ use crate::{
12 body::{scope::ExprScopes, Body, BodySourceMap}, 12 body::{scope::ExprScopes, Body, BodySourceMap},
13 data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, 13 data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData},
14 docs::Documentation, 14 docs::Documentation,
15 find_path,
16 generics::GenericParams, 15 generics::GenericParams,
17 item_scope::ItemInNs, 16 import_map::ImportMap,
18 lang_item::{LangItemTarget, LangItems}, 17 lang_item::{LangItemTarget, LangItems},
19 nameres::{raw::RawItems, CrateDefMap}, 18 nameres::{raw::RawItems, CrateDefMap},
20 path::ModPath,
21 visibility::Visibility,
22 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, 19 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
23 GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, 20 GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
24 TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, 21 TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
@@ -113,15 +110,8 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
113 #[salsa::invoke(Documentation::documentation_query)] 110 #[salsa::invoke(Documentation::documentation_query)]
114 fn documentation(&self, def: AttrDefId) -> Option<Documentation>; 111 fn documentation(&self, def: AttrDefId) -> Option<Documentation>;
115 112
116 #[salsa::invoke(find_path::importable_locations_of_query)] 113 #[salsa::invoke(ImportMap::import_map_query)]
117 fn importable_locations_of( 114 fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
118 &self,
119 item: ItemInNs,
120 krate: CrateId,
121 ) -> Arc<[(ModuleId, Name, Visibility)]>;
122
123 #[salsa::invoke(find_path::find_path_inner_query)]
124 fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath>;
125} 115}
126 116
127fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 117fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs
index b221ae1ce..2630b3d89 100644
--- a/crates/ra_hir_def/src/docs.rs
+++ b/crates/ra_hir_def/src/docs.rs
@@ -29,6 +29,13 @@ impl Documentation {
29 Documentation(s.into()) 29 Documentation(s.into())
30 } 30 }
31 31
32 pub fn from_ast<N>(node: &N) -> Option<Documentation>
33 where
34 N: ast::DocCommentsOwner + ast::AttrsOwner,
35 {
36 docs_from_ast(node)
37 }
38
32 pub fn as_str(&self) -> &str { 39 pub fn as_str(&self) -> &str {
33 &*self.0 40 &*self.0
34 } 41 }
@@ -70,6 +77,45 @@ impl Documentation {
70 } 77 }
71} 78}
72 79
73pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> { 80pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation>
74 node.doc_comment_text().map(|it| Documentation::new(&it)) 81where
82 N: ast::DocCommentsOwner + ast::AttrsOwner,
83{
84 let doc_comment_text = node.doc_comment_text();
85 let doc_attr_text = expand_doc_attrs(node);
86 let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
87 docs.map(|it| Documentation::new(&it))
88}
89
90fn merge_doc_comments_and_attrs(
91 doc_comment_text: Option<String>,
92 doc_attr_text: Option<String>,
93) -> Option<String> {
94 match (doc_comment_text, doc_attr_text) {
95 (Some(mut comment_text), Some(attr_text)) => {
96 comment_text.push_str("\n\n");
97 comment_text.push_str(&attr_text);
98 Some(comment_text)
99 }
100 (Some(comment_text), None) => Some(comment_text),
101 (None, Some(attr_text)) => Some(attr_text),
102 (None, None) => None,
103 }
104}
105
106fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> {
107 let mut docs = String::new();
108 for attr in owner.attrs() {
109 if let Some(("doc", value)) =
110 attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str()))
111 {
112 docs.push_str(value);
113 docs.push_str("\n\n");
114 }
115 }
116 if docs.is_empty() {
117 None
118 } else {
119 Some(docs.trim_end_matches("\n\n").to_owned())
120 }
75} 121}
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index 4db798473..06701a830 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -1,9 +1,8 @@
1//! An algorithm to find a path to refer to a certain item. 1//! An algorithm to find a path to refer to a certain item.
2 2
3use std::sync::Arc;
4
5use hir_expand::name::{known, AsName, Name}; 3use hir_expand::name::{known, AsName, Name};
6use ra_prof::profile; 4use ra_prof::profile;
5use rustc_hash::FxHashSet;
7use test_utils::mark; 6use test_utils::mark;
8 7
9use crate::{ 8use crate::{
@@ -11,7 +10,7 @@ use crate::{
11 item_scope::ItemInNs, 10 item_scope::ItemInNs,
12 path::{ModPath, PathKind}, 11 path::{ModPath, PathKind},
13 visibility::Visibility, 12 visibility::Visibility,
14 CrateId, ModuleDefId, ModuleId, 13 ModuleDefId, ModuleId,
15}; 14};
16 15
17// FIXME: handle local items 16// FIXME: handle local items
@@ -20,7 +19,7 @@ use crate::{
20/// *from where* you're referring to the item, hence the `from` parameter. 19/// *from where* you're referring to the item, hence the `from` parameter.
21pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 20pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
22 let _p = profile("find_path"); 21 let _p = profile("find_path");
23 db.find_path_inner(item, from, MAX_PATH_LEN) 22 find_path_inner(db, item, from, MAX_PATH_LEN)
24} 23}
25 24
26const MAX_PATH_LEN: usize = 15; 25const MAX_PATH_LEN: usize = 15;
@@ -36,20 +35,9 @@ impl ModPath {
36 let first_segment = self.segments.first(); 35 let first_segment = self.segments.first();
37 first_segment == Some(&known::alloc) || first_segment == Some(&known::core) 36 first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
38 } 37 }
39
40 fn len(&self) -> usize {
41 self.segments.len()
42 + match self.kind {
43 PathKind::Plain => 0,
44 PathKind::Super(i) => i as usize,
45 PathKind::Crate => 1,
46 PathKind::Abs => 0,
47 PathKind::DollarCrate(_) => 1,
48 }
49 }
50} 38}
51 39
52pub(crate) fn find_path_inner_query( 40fn find_path_inner(
53 db: &dyn DefDatabase, 41 db: &dyn DefDatabase,
54 item: ItemInNs, 42 item: ItemInNs,
55 from: ModuleId, 43 from: ModuleId,
@@ -133,31 +121,67 @@ pub(crate) fn find_path_inner_query(
133 } 121 }
134 122
135 // - otherwise, look for modules containing (reexporting) it and import it from one of those 123 // - otherwise, look for modules containing (reexporting) it and import it from one of those
124
136 let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; 125 let crate_root = ModuleId { local_id: def_map.root, krate: from.krate };
137 let crate_attrs = db.attrs(crate_root.into()); 126 let crate_attrs = db.attrs(crate_root.into());
138 let prefer_no_std = crate_attrs.by_key("no_std").exists(); 127 let prefer_no_std = crate_attrs.by_key("no_std").exists();
139 let importable_locations = find_importable_locations(db, item, from);
140 let mut best_path = None; 128 let mut best_path = None;
141 let mut best_path_len = max_len; 129 let mut best_path_len = max_len;
142 for (module_id, name) in importable_locations {
143 let mut path = match db.find_path_inner(
144 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
145 from,
146 best_path_len - 1,
147 ) {
148 None => continue,
149 Some(path) => path,
150 };
151 path.segments.push(name);
152 130
153 let new_path = if let Some(best_path) = best_path { 131 if item.krate(db) == Some(from.krate) {
154 select_best_path(best_path, path, prefer_no_std) 132 // Item was defined in the same crate that wants to import it. It cannot be found in any
155 } else { 133 // dependency in this case.
156 path 134
157 }; 135 let local_imports = find_local_import_locations(db, item, from);
158 best_path_len = new_path.len(); 136 for (module_id, name) in local_imports {
159 best_path = Some(new_path); 137 if let Some(mut path) = find_path_inner(
138 db,
139 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
140 from,
141 best_path_len - 1,
142 ) {
143 path.segments.push(name);
144
145 let new_path = if let Some(best_path) = best_path {
146 select_best_path(best_path, path, prefer_no_std)
147 } else {
148 path
149 };
150 best_path_len = new_path.len();
151 best_path = Some(new_path);
152 }
153 }
154 } else {
155 // Item was defined in some upstream crate. This means that it must be exported from one,
156 // too (unless we can't name it at all). It could *also* be (re)exported by the same crate
157 // that wants to import it here, but we always prefer to use the external path here.
158
159 let crate_graph = db.crate_graph();
160 let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
161 let import_map = db.import_map(dep.crate_id);
162 import_map.import_info_for(item).and_then(|info| {
163 // Determine best path for containing module and append last segment from `info`.
164 let mut path = find_path_inner(
165 db,
166 ItemInNs::Types(ModuleDefId::ModuleId(info.container)),
167 from,
168 best_path_len - 1,
169 )?;
170 path.segments.push(info.path.segments.last().unwrap().clone());
171 Some(path)
172 })
173 });
174
175 for path in extern_paths {
176 let new_path = if let Some(best_path) = best_path {
177 select_best_path(best_path, path, prefer_no_std)
178 } else {
179 path
180 };
181 best_path = Some(new_path);
182 }
160 } 183 }
184
161 best_path 185 best_path
162} 186}
163 187
@@ -185,69 +209,86 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
185 } 209 }
186} 210}
187 211
188fn find_importable_locations( 212/// Finds locations in `from.krate` from which `item` can be imported by `from`.
213fn find_local_import_locations(
189 db: &dyn DefDatabase, 214 db: &dyn DefDatabase,
190 item: ItemInNs, 215 item: ItemInNs,
191 from: ModuleId, 216 from: ModuleId,
192) -> Vec<(ModuleId, Name)> { 217) -> Vec<(ModuleId, Name)> {
193 let crate_graph = db.crate_graph(); 218 let _p = profile("find_local_import_locations");
194 let mut result = Vec::new(); 219
195 // We only look in the crate from which we are importing, and the direct 220 // `from` can import anything below `from` with visibility of at least `from`, and anything
196 // dependencies. We cannot refer to names from transitive dependencies 221 // above `from` with any visibility. That means we do not need to descend into private siblings
197 // directly (only through reexports in direct dependencies). 222 // of `from` (and similar).
198 for krate in Some(from.krate) 223
199 .into_iter() 224 let def_map = db.crate_def_map(from.krate);
200 .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) 225
201 { 226 // Compute the initial worklist. We start with all direct child modules of `from` as well as all
202 result.extend( 227 // of its (recursive) parent modules.
203 db.importable_locations_of(item, krate) 228 let data = &def_map.modules[from.local_id];
204 .iter() 229 let mut worklist = data
205 .filter(|(_, _, vis)| vis.is_visible_from(db, from)) 230 .children
206 .map(|(m, n, _)| (*m, n.clone())), 231 .values()
207 ); 232 .map(|child| ModuleId { krate: from.krate, local_id: *child })
208 } 233 .collect::<Vec<_>>();
209 result 234 let mut parent = data.parent;
210} 235 while let Some(p) = parent {
236 worklist.push(ModuleId { krate: from.krate, local_id: p });
237 parent = def_map.modules[p].parent;
238 }
239
240 let mut seen: FxHashSet<_> = FxHashSet::default();
241
242 let mut locations = Vec::new();
243 while let Some(module) = worklist.pop() {
244 if !seen.insert(module) {
245 continue; // already processed this module
246 }
247
248 let ext_def_map;
249 let data = if module.krate == from.krate {
250 &def_map[module.local_id]
251 } else {
252 // The crate might reexport a module defined in another crate.
253 ext_def_map = db.crate_def_map(module.krate);
254 &ext_def_map[module.local_id]
255 };
211 256
212/// Collects all locations from which we might import the item in a particular
213/// crate. These include the original definition of the item, and any
214/// non-private `use`s.
215///
216/// Note that the crate doesn't need to be the one in which the item is defined;
217/// it might be re-exported in other crates.
218pub(crate) fn importable_locations_of_query(
219 db: &dyn DefDatabase,
220 item: ItemInNs,
221 krate: CrateId,
222) -> Arc<[(ModuleId, Name, Visibility)]> {
223 let _p = profile("importable_locations_of_query");
224 let def_map = db.crate_def_map(krate);
225 let mut result = Vec::new();
226 for (local_id, data) in def_map.modules.iter() {
227 if let Some((name, vis)) = data.scope.name_of(item) { 257 if let Some((name, vis)) = data.scope.name_of(item) {
228 let is_private = if let Visibility::Module(private_to) = vis { 258 if vis.is_visible_from(db, from) {
229 private_to.local_id == local_id 259 let is_private = if let Visibility::Module(private_to) = vis {
230 } else { 260 private_to.local_id == module.local_id
231 false 261 } else {
232 }; 262 false
233 let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { 263 };
234 data.scope.declarations().any(|it| it == module_def_id) 264 let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
235 } else { 265 data.scope.declarations().any(|it| it == module_def_id)
236 false 266 } else {
237 }; 267 false
238 if is_private && !is_original_def { 268 };
269
239 // Ignore private imports. these could be used if we are 270 // Ignore private imports. these could be used if we are
240 // in a submodule of this module, but that's usually not 271 // in a submodule of this module, but that's usually not
241 // what the user wants; and if this module can import 272 // what the user wants; and if this module can import
242 // the item and we're a submodule of it, so can we. 273 // the item and we're a submodule of it, so can we.
243 // Also this keeps the cached data smaller. 274 // Also this keeps the cached data smaller.
244 continue; 275 if !is_private || is_original_def {
276 locations.push((module, name.clone()));
277 }
278 }
279 }
280
281 // Descend into all modules visible from `from`.
282 for (_, per_ns) in data.scope.entries() {
283 if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() {
284 if vis.is_visible_from(db, from) {
285 worklist.push(module);
286 }
245 } 287 }
246 result.push((ModuleId { krate, local_id }, name.clone(), vis));
247 } 288 }
248 } 289 }
249 290
250 Arc::from(result) 291 locations
251} 292}
252 293
253#[cfg(test)] 294#[cfg(test)]
@@ -264,8 +305,8 @@ mod tests {
264 /// `code` needs to contain a cursor marker; checks that `find_path` for the 305 /// `code` needs to contain a cursor marker; checks that `find_path` for the
265 /// item the `path` refers to returns that same path when called from the 306 /// item the `path` refers to returns that same path when called from the
266 /// module the cursor is in. 307 /// module the cursor is in.
267 fn check_found_path(code: &str, path: &str) { 308 fn check_found_path(ra_fixture: &str, path: &str) {
268 let (db, pos) = TestDB::with_position(code); 309 let (db, pos) = TestDB::with_position(ra_fixture);
269 let module = db.module_for_file(pos.file_id); 310 let module = db.module_for_file(pos.file_id);
270 let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); 311 let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
271 let ast_path = parsed_path_file 312 let ast_path = parsed_path_file
@@ -396,6 +437,44 @@ mod tests {
396 } 437 }
397 438
398 #[test] 439 #[test]
440 fn partially_imported() {
441 // Tests that short paths are used even for external items, when parts of the path are
442 // already in scope.
443 check_found_path(
444 r#"
445 //- /main.rs crate:main deps:ra_syntax
446
447 use ra_syntax::ast;
448 <|>
449
450 //- /lib.rs crate:ra_syntax
451 pub mod ast {
452 pub enum ModuleItem {
453 A, B, C,
454 }
455 }
456 "#,
457 "ast::ModuleItem",
458 );
459
460 check_found_path(
461 r#"
462 //- /main.rs crate:main deps:ra_syntax
463
464 <|>
465
466 //- /lib.rs crate:ra_syntax
467 pub mod ast {
468 pub enum ModuleItem {
469 A, B, C,
470 }
471 }
472 "#,
473 "ra_syntax::ast::ModuleItem",
474 );
475 }
476
477 #[test]
399 fn same_crate_reexport() { 478 fn same_crate_reexport() {
400 let code = r#" 479 let code = r#"
401 //- /main.rs 480 //- /main.rs
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs
new file mode 100644
index 000000000..68e20d06b
--- /dev/null
+++ b/crates/ra_hir_def/src/import_map.rs
@@ -0,0 +1,679 @@
1//! A map of all publicly exported items in a crate.
2
3use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc};
4
5use fst::{self, Streamer};
6use indexmap::{map::Entry, IndexMap};
7use ra_db::CrateId;
8use rustc_hash::FxHasher;
9
10use crate::{
11 db::DefDatabase,
12 item_scope::ItemInNs,
13 path::{ModPath, PathKind},
14 visibility::Visibility,
15 ModuleDefId, ModuleId,
16};
17
18type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
19
20/// Item import details stored in the `ImportMap`.
21#[derive(Debug, Clone, Eq, PartialEq)]
22pub struct ImportInfo {
23 /// A path that can be used to import the item, relative to the crate's root.
24 pub path: ModPath,
25 /// The module containing this item.
26 pub container: ModuleId,
27}
28
29/// A map from publicly exported items to the path needed to import/name them from a downstream
30/// crate.
31///
32/// Reexports of items are taken into account, ie. if something is exported under multiple
33/// names, the one with the shortest import path will be used.
34///
35/// Note that all paths are relative to the containing crate's root, so the crate name still needs
36/// to be prepended to the `ModPath` before the path is valid.
37pub struct ImportMap {
38 map: FxIndexMap<ItemInNs, ImportInfo>,
39
40 /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
41 /// values returned by running `fst`.
42 ///
43 /// Since a path can refer to multiple items due to namespacing, we store all items with the
44 /// same path right after each other. This allows us to find all items after the FST gives us
45 /// the index of the first one.
46 importables: Vec<ItemInNs>,
47 fst: fst::Map<Vec<u8>>,
48}
49
50impl ImportMap {
51 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
52 let _p = ra_prof::profile("import_map_query");
53 let def_map = db.crate_def_map(krate);
54 let mut import_map = FxIndexMap::with_capacity_and_hasher(64, Default::default());
55
56 // We look only into modules that are public(ly reexported), starting with the crate root.
57 let empty = ModPath { kind: PathKind::Plain, segments: vec![] };
58 let root = ModuleId { krate, local_id: def_map.root };
59 let mut worklist = vec![(root, empty)];
60 while let Some((module, mod_path)) = worklist.pop() {
61 let ext_def_map;
62 let mod_data = if module.krate == krate {
63 &def_map[module.local_id]
64 } else {
65 // The crate might reexport a module defined in another crate.
66 ext_def_map = db.crate_def_map(module.krate);
67 &ext_def_map[module.local_id]
68 };
69
70 let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
71 let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public);
72 if per_ns.is_none() {
73 None
74 } else {
75 Some((name, per_ns))
76 }
77 });
78
79 for (name, per_ns) in visible_items {
80 let mk_path = || {
81 let mut path = mod_path.clone();
82 path.segments.push(name.clone());
83 path
84 };
85
86 for item in per_ns.iter_items() {
87 let path = mk_path();
88 match import_map.entry(item) {
89 Entry::Vacant(entry) => {
90 entry.insert(ImportInfo { path, container: module });
91 }
92 Entry::Occupied(mut entry) => {
93 // If the new path is shorter, prefer that one.
94 if path.len() < entry.get().path.len() {
95 *entry.get_mut() = ImportInfo { path, container: module };
96 } else {
97 continue;
98 }
99 }
100 }
101
102 // If we've just added a path to a module, descend into it. We might traverse
103 // modules multiple times, but only if the new path to it is shorter than the
104 // first (else we `continue` above).
105 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
106 worklist.push((mod_id, mk_path()));
107 }
108 }
109 }
110 }
111
112 let mut importables = import_map.iter().collect::<Vec<_>>();
113
114 importables.sort_by(cmp);
115
116 // Build the FST, taking care not to insert duplicate values.
117
118 let mut builder = fst::MapBuilder::memory();
119 let mut last_batch_start = 0;
120
121 for idx in 0..importables.len() {
122 if let Some(next_item) = importables.get(idx + 1) {
123 if cmp(&importables[last_batch_start], next_item) == Ordering::Equal {
124 continue;
125 }
126 }
127
128 let start = last_batch_start;
129 last_batch_start = idx + 1;
130
131 let key = fst_path(&importables[start].1.path);
132
133 builder.insert(key, start as u64).unwrap();
134 }
135
136 let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
137 let importables = importables.iter().map(|(item, _)| **item).collect();
138
139 Arc::new(Self { map: import_map, fst, importables })
140 }
141
142 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
143 pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> {
144 Some(&self.map.get(&item)?.path)
145 }
146
147 pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
148 self.map.get(&item)
149 }
150}
151
152impl PartialEq for ImportMap {
153 fn eq(&self, other: &Self) -> bool {
154 // `fst` and `importables` are built from `map`, so we don't need to compare them.
155 self.map == other.map
156 }
157}
158
159impl Eq for ImportMap {}
160
161impl fmt::Debug for ImportMap {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 let mut importable_paths: Vec<_> = self
164 .map
165 .iter()
166 .map(|(item, info)| {
167 let ns = match item {
168 ItemInNs::Types(_) => "t",
169 ItemInNs::Values(_) => "v",
170 ItemInNs::Macros(_) => "m",
171 };
172 format!("- {} ({})", info.path, ns)
173 })
174 .collect();
175
176 importable_paths.sort();
177 f.write_str(&importable_paths.join("\n"))
178 }
179}
180
181fn fst_path(path: &ModPath) -> String {
182 let mut s = path.to_string();
183 s.make_ascii_lowercase();
184 s
185}
186
187fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering {
188 let lhs_str = fst_path(&lhs.path);
189 let rhs_str = fst_path(&rhs.path);
190 lhs_str.cmp(&rhs_str)
191}
192
193#[derive(Debug)]
194pub struct Query {
195 query: String,
196 lowercased: String,
197 anchor_end: bool,
198 case_sensitive: bool,
199 limit: usize,
200}
201
202impl Query {
203 pub fn new(query: &str) -> Self {
204 Self {
205 lowercased: query.to_lowercase(),
206 query: query.to_string(),
207 anchor_end: false,
208 case_sensitive: false,
209 limit: usize::max_value(),
210 }
211 }
212
213 /// Only returns items whose paths end with the (case-insensitive) query string as their last
214 /// segment.
215 pub fn anchor_end(self) -> Self {
216 Self { anchor_end: true, ..self }
217 }
218
219 /// Limits the returned number of items to `limit`.
220 pub fn limit(self, limit: usize) -> Self {
221 Self { limit, ..self }
222 }
223
224 /// Respect casing of the query string when matching.
225 pub fn case_sensitive(self) -> Self {
226 Self { case_sensitive: true, ..self }
227 }
228}
229
230/// Searches dependencies of `krate` for an importable path matching `query`.
231///
232/// This returns a list of items that could be imported from dependencies of `krate`.
233pub fn search_dependencies<'a>(
234 db: &'a dyn DefDatabase,
235 krate: CrateId,
236 query: Query,
237) -> Vec<ItemInNs> {
238 let _p = ra_prof::profile("search_dependencies").detail(|| format!("{:?}", query));
239
240 let graph = db.crate_graph();
241 let import_maps: Vec<_> =
242 graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
243
244 let automaton = fst::automaton::Subsequence::new(&query.lowercased);
245
246 let mut op = fst::map::OpBuilder::new();
247 for map in &import_maps {
248 op = op.add(map.fst.search(&automaton));
249 }
250
251 let mut stream = op.union();
252 let mut res = Vec::new();
253 while let Some((_, indexed_values)) = stream.next() {
254 for indexed_value in indexed_values {
255 let import_map = &import_maps[indexed_value.index];
256 let importables = &import_map.importables[indexed_value.value as usize..];
257
258 // Path shared by the importable items in this group.
259 let path = &import_map.map[&importables[0]].path;
260
261 if query.anchor_end {
262 // Last segment must match query.
263 let last = path.segments.last().unwrap().to_string();
264 if last.to_lowercase() != query.lowercased {
265 continue;
266 }
267 }
268
269 // Add the items from this `ModPath` group. Those are all subsequent items in
270 // `importables` whose paths match `path`.
271 let iter = importables.iter().copied().take_while(|item| {
272 let item_path = &import_map.map[item].path;
273 fst_path(item_path) == fst_path(path)
274 });
275
276 if query.case_sensitive {
277 // FIXME: This does not do a subsequence match.
278 res.extend(iter.filter(|item| {
279 let item_path = &import_map.map[item].path;
280 item_path.to_string().contains(&query.query)
281 }));
282 } else {
283 res.extend(iter);
284 }
285
286 if res.len() >= query.limit {
287 res.truncate(query.limit);
288 return res;
289 }
290 }
291 }
292
293 res
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299 use crate::test_db::TestDB;
300 use insta::assert_snapshot;
301 use itertools::Itertools;
302 use ra_db::fixture::WithFixture;
303 use ra_db::{SourceDatabase, Upcast};
304
305 fn import_map(ra_fixture: &str) -> String {
306 let db = TestDB::with_files(ra_fixture);
307 let crate_graph = db.crate_graph();
308
309 let s = crate_graph
310 .iter()
311 .filter_map(|krate| {
312 let cdata = &crate_graph[krate];
313 let name = cdata.display_name.as_ref()?;
314
315 let map = db.import_map(krate);
316
317 Some(format!("{}:\n{:?}", name, map))
318 })
319 .join("\n");
320 s
321 }
322
323 fn search_dependencies_of(ra_fixture: &str, krate_name: &str, query: Query) -> String {
324 let db = TestDB::with_files(ra_fixture);
325 let crate_graph = db.crate_graph();
326 let krate = crate_graph
327 .iter()
328 .find(|krate| {
329 crate_graph[*krate].display_name.as_ref().map(|n| n.to_string())
330 == Some(krate_name.to_string())
331 })
332 .unwrap();
333
334 search_dependencies(db.upcast(), krate, query)
335 .into_iter()
336 .filter_map(|item| {
337 let mark = match item {
338 ItemInNs::Types(_) => "t",
339 ItemInNs::Values(_) => "v",
340 ItemInNs::Macros(_) => "m",
341 };
342 item.krate(db.upcast()).map(|krate| {
343 let map = db.import_map(krate);
344 let path = map.path_of(item).unwrap();
345 format!(
346 "{}::{} ({})",
347 crate_graph[krate].display_name.as_ref().unwrap(),
348 path,
349 mark
350 )
351 })
352 })
353 .join("\n")
354 }
355
356 #[test]
357 fn smoke() {
358 let map = import_map(
359 r"
360 //- /main.rs crate:main deps:lib
361
362 mod private {
363 pub use lib::Pub;
364 pub struct InPrivateModule;
365 }
366
367 pub mod publ1 {
368 use lib::Pub;
369 }
370
371 pub mod real_pub {
372 pub use lib::Pub;
373 }
374 pub mod real_pu2 { // same path length as above
375 pub use lib::Pub;
376 }
377
378 //- /lib.rs crate:lib
379 pub struct Pub {}
380 pub struct Pub2; // t + v
381 struct Priv;
382 ",
383 );
384
385 assert_snapshot!(map, @r###"
386 main:
387 - publ1 (t)
388 - real_pu2 (t)
389 - real_pub (t)
390 - real_pub::Pub (t)
391 lib:
392 - Pub (t)
393 - Pub2 (t)
394 - Pub2 (v)
395 "###);
396 }
397
398 #[test]
399 fn prefers_shortest_path() {
400 let map = import_map(
401 r"
402 //- /main.rs crate:main
403
404 pub mod sub {
405 pub mod subsub {
406 pub struct Def {}
407 }
408
409 pub use super::sub::subsub::Def;
410 }
411 ",
412 );
413
414 assert_snapshot!(map, @r###"
415 main:
416 - sub (t)
417 - sub::Def (t)
418 - sub::subsub (t)
419 "###);
420 }
421
422 #[test]
423 fn type_reexport_cross_crate() {
424 // Reexports need to be visible from a crate, even if the original crate exports the item
425 // at a shorter path.
426 let map = import_map(
427 r"
428 //- /main.rs crate:main deps:lib
429 pub mod m {
430 pub use lib::S;
431 }
432 //- /lib.rs crate:lib
433 pub struct S;
434 ",
435 );
436
437 assert_snapshot!(map, @r###"
438 main:
439 - m (t)
440 - m::S (t)
441 - m::S (v)
442 lib:
443 - S (t)
444 - S (v)
445 "###);
446 }
447
448 #[test]
449 fn macro_reexport() {
450 let map = import_map(
451 r"
452 //- /main.rs crate:main deps:lib
453 pub mod m {
454 pub use lib::pub_macro;
455 }
456 //- /lib.rs crate:lib
457 #[macro_export]
458 macro_rules! pub_macro {
459 () => {};
460 }
461 ",
462 );
463
464 assert_snapshot!(map, @r###"
465 main:
466 - m (t)
467 - m::pub_macro (m)
468 lib:
469 - pub_macro (m)
470 "###);
471 }
472
473 #[test]
474 fn module_reexport() {
475 // Reexporting modules from a dependency adds all contents to the import map.
476 let map = import_map(
477 r"
478 //- /main.rs crate:main deps:lib
479 pub use lib::module as reexported_module;
480 //- /lib.rs crate:lib
481 pub mod module {
482 pub struct S;
483 }
484 ",
485 );
486
487 assert_snapshot!(map, @r###"
488 main:
489 - reexported_module (t)
490 - reexported_module::S (t)
491 - reexported_module::S (v)
492 lib:
493 - module (t)
494 - module::S (t)
495 - module::S (v)
496 "###);
497 }
498
499 #[test]
500 fn cyclic_module_reexport() {
501 // A cyclic reexport does not hang.
502 let map = import_map(
503 r"
504 //- /lib.rs crate:lib
505 pub mod module {
506 pub struct S;
507 pub use super::sub::*;
508 }
509
510 pub mod sub {
511 pub use super::module;
512 }
513 ",
514 );
515
516 assert_snapshot!(map, @r###"
517 lib:
518 - module (t)
519 - module::S (t)
520 - module::S (v)
521 - sub (t)
522 "###);
523 }
524
525 #[test]
526 fn private_macro() {
527 let map = import_map(
528 r"
529 //- /lib.rs crate:lib
530 macro_rules! private_macro {
531 () => {};
532 }
533 ",
534 );
535
536 assert_snapshot!(map, @r###"
537 lib:
538 "###);
539 }
540
541 #[test]
542 fn namespacing() {
543 let map = import_map(
544 r"
545 //- /lib.rs crate:lib
546 pub struct Thing; // t + v
547 #[macro_export]
548 macro_rules! Thing { // m
549 () => {};
550 }
551 ",
552 );
553
554 assert_snapshot!(map, @r###"
555 lib:
556 - Thing (m)
557 - Thing (t)
558 - Thing (v)
559 "###);
560
561 let map = import_map(
562 r"
563 //- /lib.rs crate:lib
564 pub mod Thing {} // t
565 #[macro_export]
566 macro_rules! Thing { // m
567 () => {};
568 }
569 ",
570 );
571
572 assert_snapshot!(map, @r###"
573 lib:
574 - Thing (m)
575 - Thing (t)
576 "###);
577 }
578
579 #[test]
580 fn search() {
581 let ra_fixture = r#"
582 //- /main.rs crate:main deps:dep
583 //- /dep.rs crate:dep deps:tdep
584 use tdep::fmt as fmt_dep;
585 pub mod fmt {
586 pub trait Display {
587 fn fmt();
588 }
589 }
590 #[macro_export]
591 macro_rules! Fmt {
592 () => {};
593 }
594 pub struct Fmt;
595
596 pub fn format() {}
597 pub fn no() {}
598
599 //- /tdep.rs crate:tdep
600 pub mod fmt {
601 pub struct NotImportableFromMain;
602 }
603 "#;
604
605 let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt"));
606 assert_snapshot!(res, @r###"
607 dep::fmt (t)
608 dep::Fmt (t)
609 dep::Fmt (v)
610 dep::Fmt (m)
611 dep::fmt::Display (t)
612 dep::format (v)
613 "###);
614
615 let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end());
616 assert_snapshot!(res, @r###"
617 dep::fmt (t)
618 dep::Fmt (t)
619 dep::Fmt (v)
620 dep::Fmt (m)
621 "###);
622 }
623
624 #[test]
625 fn search_casing() {
626 let ra_fixture = r#"
627 //- /main.rs crate:main deps:dep
628 //- /dep.rs crate:dep
629
630 pub struct fmt;
631 pub struct FMT;
632 "#;
633
634 let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT"));
635
636 assert_snapshot!(res, @r###"
637 dep::fmt (t)
638 dep::fmt (v)
639 dep::FMT (t)
640 dep::FMT (v)
641 "###);
642
643 let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT").case_sensitive());
644
645 assert_snapshot!(res, @r###"
646 dep::FMT (t)
647 dep::FMT (v)
648 "###);
649 }
650
651 #[test]
652 fn search_limit() {
653 let res = search_dependencies_of(
654 r#"
655 //- /main.rs crate:main deps:dep
656 //- /dep.rs crate:dep
657 pub mod fmt {
658 pub trait Display {
659 fn fmt();
660 }
661 }
662 #[macro_export]
663 macro_rules! Fmt {
664 () => {};
665 }
666 pub struct Fmt;
667
668 pub fn format() {}
669 pub fn no() {}
670 "#,
671 "main",
672 Query::new("").limit(2),
673 );
674 assert_snapshot!(res, @r###"
675 dep::fmt (t)
676 dep::Fmt (t)
677 "###);
678 }
679}
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs
index fc15948ad..b03ba939a 100644
--- a/crates/ra_hir_def/src/item_scope.rs
+++ b/crates/ra_hir_def/src/item_scope.rs
@@ -3,11 +3,12 @@
3 3
4use hir_expand::name::Name; 4use hir_expand::name::Name;
5use once_cell::sync::Lazy; 5use once_cell::sync::Lazy;
6use ra_db::CrateId;
6use rustc_hash::FxHashMap; 7use rustc_hash::FxHashMap;
7 8
8use crate::{ 9use crate::{
9 per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, 10 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId,
10 TraitId, 11 Lookup, MacroDefId, ModuleDefId, TraitId,
11}; 12};
12 13
13#[derive(Debug, Default, PartialEq, Eq)] 14#[derive(Debug, Default, PartialEq, Eq)]
@@ -203,4 +204,22 @@ impl ItemInNs {
203 ItemInNs::Macros(_) => None, 204 ItemInNs::Macros(_) => None,
204 } 205 }
205 } 206 }
207
208 /// Returns the crate defining this item (or `None` if `self` is built-in).
209 pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> {
210 Some(match self {
211 ItemInNs::Types(did) | ItemInNs::Values(did) => match did {
212 ModuleDefId::ModuleId(id) => id.krate,
213 ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate,
214 ModuleDefId::AdtId(id) => id.module(db).krate,
215 ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate,
216 ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate,
217 ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate,
218 ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate,
219 ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate,
220 ModuleDefId::BuiltinType(_) => return None,
221 },
222 ItemInNs::Macros(id) => return id.krate,
223 })
224 }
206} 225}
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index 5325a2760..edc59e5a8 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -43,6 +43,7 @@ pub mod child_by_source;
43 43
44pub mod visibility; 44pub mod visibility;
45pub mod find_path; 45pub mod find_path;
46pub mod import_map;
46 47
47#[cfg(test)] 48#[cfg(test)]
48mod test_db; 49mod test_db;
@@ -416,6 +417,7 @@ pub trait AsMacroCall {
416 fn as_call_id( 417 fn as_call_id(
417 &self, 418 &self,
418 db: &dyn db::DefDatabase, 419 db: &dyn db::DefDatabase,
420 krate: CrateId,
419 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 421 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
420 ) -> Option<MacroCallId>; 422 ) -> Option<MacroCallId>;
421} 423}
@@ -424,13 +426,14 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
424 fn as_call_id( 426 fn as_call_id(
425 &self, 427 &self,
426 db: &dyn db::DefDatabase, 428 db: &dyn db::DefDatabase,
429 krate: CrateId,
427 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 430 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
428 ) -> Option<MacroCallId> { 431 ) -> Option<MacroCallId> {
429 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); 432 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
430 let h = Hygiene::new(db.upcast(), self.file_id); 433 let h = Hygiene::new(db.upcast(), self.file_id);
431 let path = path::ModPath::from_src(self.value.path()?, &h)?; 434 let path = path::ModPath::from_src(self.value.path()?, &h)?;
432 435
433 AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, resolver) 436 AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, krate, resolver)
434 } 437 }
435} 438}
436 439
@@ -451,6 +454,7 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
451 fn as_call_id( 454 fn as_call_id(
452 &self, 455 &self,
453 db: &dyn db::DefDatabase, 456 db: &dyn db::DefDatabase,
457 krate: CrateId,
454 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 458 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
455 ) -> Option<MacroCallId> { 459 ) -> Option<MacroCallId> {
456 let def: MacroDefId = resolver(self.path.clone())?; 460 let def: MacroDefId = resolver(self.path.clone())?;
@@ -460,13 +464,13 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
460 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); 464 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id);
461 465
462 Some( 466 Some(
463 expand_eager_macro(db.upcast(), macro_call, def, &|path: ast::Path| { 467 expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| {
464 resolver(path::ModPath::from_src(path, &hygiene)?) 468 resolver(path::ModPath::from_src(path, &hygiene)?)
465 })? 469 })?
466 .into(), 470 .into(),
467 ) 471 )
468 } else { 472 } else {
469 Some(def.as_lazy_macro(db.upcast(), MacroCallKind::FnLike(self.ast_id)).into()) 473 Some(def.as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike(self.ast_id)).into())
470 } 474 }
471 } 475 }
472} 476}
@@ -475,12 +479,14 @@ impl AsMacroCall for AstIdWithPath<ast::ModuleItem> {
475 fn as_call_id( 479 fn as_call_id(
476 &self, 480 &self,
477 db: &dyn db::DefDatabase, 481 db: &dyn db::DefDatabase,
482 krate: CrateId,
478 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 483 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
479 ) -> Option<MacroCallId> { 484 ) -> Option<MacroCallId> {
480 let def = resolver(self.path.clone())?; 485 let def = resolver(self.path.clone())?;
481 Some( 486 Some(
482 def.as_lazy_macro( 487 def.as_lazy_macro(
483 db.upcast(), 488 db.upcast(),
489 krate,
484 MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()), 490 MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()),
485 ) 491 )
486 .into(), 492 .into(),
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 353a31ad4..976e5e585 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -571,16 +571,18 @@ impl DefCollector<'_> {
571 return false; 571 return false;
572 } 572 }
573 573
574 if let Some(call_id) = directive.ast_id.as_call_id(self.db, |path| { 574 if let Some(call_id) =
575 let resolved_res = self.def_map.resolve_path_fp_with_macro( 575 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| {
576 self.db, 576 let resolved_res = self.def_map.resolve_path_fp_with_macro(
577 ResolveMode::Other, 577 self.db,
578 directive.module_id, 578 ResolveMode::Other,
579 &path, 579 directive.module_id,
580 BuiltinShadowMode::Module, 580 &path,
581 ); 581 BuiltinShadowMode::Module,
582 resolved_res.resolved_def.take_macros() 582 );
583 }) { 583 resolved_res.resolved_def.take_macros()
584 })
585 {
584 resolved.push((directive.module_id, call_id, directive.depth)); 586 resolved.push((directive.module_id, call_id, directive.depth));
585 res = ReachedFixedPoint::No; 587 res = ReachedFixedPoint::No;
586 return false;