aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikhail Rakhmanov <[email protected]>2020-06-13 07:42:15 +0100
committerMikhail Rakhmanov <[email protected]>2020-06-13 07:42:15 +0100
commit16bbf4ab7f132e6e5e5318dccdef9a5d71afdd7f (patch)
tree4b79fa8c046be56b02427ba843e70cdf3ac05767
parenteeb8b9e236796da8734ba81a49164864497f7226 (diff)
parentb56ad148db0c69eb279c225f45d324b4e80e7367 (diff)
Merge branch 'master' into keyword_completion
# Conflicts: # docs/user/generated_features.adoc
-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; 588 return false;
@@ -589,9 +591,10 @@ impl DefCollector<'_> {
589 true 591 true
590 }); 592 });
591 attribute_macros.retain(|directive| { 593 attribute_macros.retain(|directive| {
592 if let Some(call_id) = directive 594 if let Some(call_id) =
593 .ast_id 595 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| {
594 .as_call_id(self.db, |path| self.resolve_attribute_macro(&directive, &path)) 596 self.resolve_attribute_macro(&directive, &path)
597 })
595 { 598 {
596 resolved.push((directive.module_id, call_id, 0)); 599 resolved.push((directive.module_id, call_id, 0));
597 res = ReachedFixedPoint::No; 600 res = ReachedFixedPoint::No;
@@ -957,11 +960,13 @@ impl ModCollector<'_, '_> {
957 } 960 }
958 961
959 // Case 2: try to resolve in legacy scope and expand macro_rules 962 // Case 2: try to resolve in legacy scope and expand macro_rules
960 if let Some(macro_call_id) = ast_id.as_call_id(self.def_collector.db, |path| { 963 if let Some(macro_call_id) =
961 path.as_ident().and_then(|name| { 964 ast_id.as_call_id(self.def_collector.db, self.def_collector.def_map.krate, |path| {
962 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) 965 path.as_ident().and_then(|name| {
966 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
967 })
963 }) 968 })
964 }) { 969 {
965 self.def_collector.unexpanded_macros.push(MacroDirective { 970 self.def_collector.unexpanded_macros.push(MacroDirective {
966 module_id: self.module_id, 971 module_id: self.module_id,
967 ast_id, 972 ast_id,
diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs
index 386c5cade..cede4a6fc 100644
--- a/crates/ra_hir_def/src/nameres/mod_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs
@@ -61,7 +61,7 @@ impl ModDir {
61 }; 61 };
62 62
63 for candidate in candidate_files.iter() { 63 for candidate in candidate_files.iter() {
64 if let Some(file_id) = db.resolve_relative_path(file_id, candidate) { 64 if let Some(file_id) = db.resolve_path(file_id, candidate.as_str()) {
65 let mut root_non_dir_owner = false; 65 let mut root_non_dir_owner = false;
66 let mut mod_path = RelativePathBuf::new(); 66 let mut mod_path = RelativePathBuf::new();
67 if !(candidate.ends_with("mod.rs") || attr_path.is_some()) { 67 if !(candidate.ends_with("mod.rs") || attr_path.is_some()) {
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index e84efe2ab..ba16442bd 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -76,6 +76,19 @@ impl ModPath {
76 } 76 }
77 } 77 }
78 78
79 /// Returns the number of segments in the path (counting special segments like `$crate` and
80 /// `super`).
81 pub fn len(&self) -> usize {
82 self.segments.len()
83 + match self.kind {
84 PathKind::Plain => 0,
85 PathKind::Super(i) => i as usize,
86 PathKind::Crate => 1,
87 PathKind::Abs => 0,
88 PathKind::DollarCrate(_) => 1,
89 }
90 }
91
79 pub fn is_ident(&self) -> bool { 92 pub fn is_ident(&self) -> bool {
80 self.kind == PathKind::Plain && self.segments.len() == 1 93 self.kind == PathKind::Plain && self.segments.len() == 1
81 } 94 }
@@ -273,7 +286,7 @@ impl From<Name> for ModPath {
273impl Display for ModPath { 286impl Display for ModPath {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 let mut first_segment = true; 288 let mut first_segment = true;
276 let mut add_segment = |s| { 289 let mut add_segment = |s| -> fmt::Result {
277 if !first_segment { 290 if !first_segment {
278 f.write_str("::")?; 291 f.write_str("::")?;
279 } 292 }
@@ -310,16 +323,16 @@ pub use hir_expand::name as __name;
310 323
311#[macro_export] 324#[macro_export]
312macro_rules! __known_path { 325macro_rules! __known_path {
313 (std::iter::IntoIterator) => {}; 326 (core::iter::IntoIterator) => {};
314 (std::result::Result) => {}; 327 (core::result::Result) => {};
315 (std::ops::Range) => {}; 328 (core::ops::Range) => {};
316 (std::ops::RangeFrom) => {}; 329 (core::ops::RangeFrom) => {};
317 (std::ops::RangeFull) => {}; 330 (core::ops::RangeFull) => {};
318 (std::ops::RangeTo) => {}; 331 (core::ops::RangeTo) => {};
319 (std::ops::RangeToInclusive) => {}; 332 (core::ops::RangeToInclusive) => {};
320 (std::ops::RangeInclusive) => {}; 333 (core::ops::RangeInclusive) => {};
321 (std::future::Future) => {}; 334 (core::future::Future) => {};
322 (std::ops::Try) => {}; 335 (core::ops::Try) => {};
323 ($path:path) => { 336 ($path:path) => {
324 compile_error!("Please register your known path in the path module") 337 compile_error!("Please register your known path in the path module")
325 }; 338 };
diff --git a/crates/ra_hir_def/src/per_ns.rs b/crates/ra_hir_def/src/per_ns.rs
index 6e435c8c1..74665c588 100644
--- a/crates/ra_hir_def/src/per_ns.rs
+++ b/crates/ra_hir_def/src/per_ns.rs
@@ -5,7 +5,7 @@
5 5
6use hir_expand::MacroDefId; 6use hir_expand::MacroDefId;
7 7
8use crate::{visibility::Visibility, ModuleDefId}; 8use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId};
9 9
10#[derive(Debug, Copy, Clone, PartialEq, Eq)] 10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
11pub struct PerNs { 11pub struct PerNs {
@@ -84,4 +84,12 @@ impl PerNs {
84 macros: self.macros.or(other.macros), 84 macros: self.macros.or(other.macros),
85 } 85 }
86 } 86 }
87
88 pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> {
89 self.types
90 .map(|it| ItemInNs::Types(it.0))
91 .into_iter()
92 .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter())
93 .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter())
94 }
87} 95}
diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs
index eb83dee79..4581d8745 100644
--- a/crates/ra_hir_def/src/test_db.rs
+++ b/crates/ra_hir_def/src/test_db.rs
@@ -6,9 +6,8 @@ use std::{
6}; 6};
7 7
8use hir_expand::db::AstDatabase; 8use hir_expand::db::AstDatabase;
9use ra_db::{ 9use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
10 salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath, Upcast, 10use rustc_hash::FxHashSet;
11};
12 11
13use crate::db::DefDatabase; 12use crate::db::DefDatabase;
14 13
@@ -58,24 +57,12 @@ impl FileLoader for TestDB {
58 fn file_text(&self, file_id: FileId) -> Arc<String> { 57 fn file_text(&self, file_id: FileId) -> Arc<String> {
59 FileLoaderDelegate(self).file_text(file_id) 58 FileLoaderDelegate(self).file_text(file_id)
60 } 59 }
61 fn resolve_relative_path( 60 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
62 &self, 61 FileLoaderDelegate(self).resolve_path(anchor, path)
63 anchor: FileId,
64 relative_path: &RelativePath,
65 ) -> Option<FileId> {
66 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
67 } 62 }
68 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 63 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
69 FileLoaderDelegate(self).relevant_crates(file_id) 64 FileLoaderDelegate(self).relevant_crates(file_id)
70 } 65 }
71
72 fn resolve_extern_path(
73 &self,
74 extern_id: ExternSourceId,
75 relative_path: &RelativePath,
76 ) -> Option<FileId> {
77 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
78 }
79} 66}
80 67
81impl TestDB { 68impl TestDB {
diff --git a/crates/ra_hir_expand/Cargo.toml b/crates/ra_hir_expand/Cargo.toml
index 2cd522766..e5c9f3e99 100644
--- a/crates/ra_hir_expand/Cargo.toml
+++ b/crates/ra_hir_expand/Cargo.toml
@@ -10,6 +10,7 @@ doctest = false
10[dependencies] 10[dependencies]
11log = "0.4.8" 11log = "0.4.8"
12either = "1.5.3" 12either = "1.5.3"
13rustc-hash = "1.0.0"
13 14
14ra_arena = { path = "../ra_arena" } 15ra_arena = { path = "../ra_arena" }
15ra_db = { path = "../ra_db" } 16ra_db = { path = "../ra_db" }
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
index 1dc9cac66..26b667b55 100644
--- a/crates/ra_hir_expand/src/builtin_derive.rs
+++ b/crates/ra_hir_expand/src/builtin_derive.rs
@@ -8,8 +8,7 @@ use ra_syntax::{
8 match_ast, 8 match_ast,
9}; 9};
10 10
11use crate::db::AstDatabase; 11use crate::{db::AstDatabase, name, quote, LazyMacroId, MacroDefId, MacroDefKind};
12use crate::{name, quote, LazyMacroId, MacroCallId, MacroDefId, MacroDefKind};
13 12
14macro_rules! register_builtin { 13macro_rules! register_builtin {
15 ( $($trait:ident => $expand:ident),* ) => { 14 ( $($trait:ident => $expand:ident),* ) => {
@@ -156,23 +155,13 @@ fn expand_simple_derive(
156fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree { 155fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree {
157 // FIXME: make hygiene works for builtin derive macro 156 // FIXME: make hygiene works for builtin derive macro
158 // such that $crate can be used here. 157 // such that $crate can be used here.
159
160 let m: MacroCallId = id.into();
161 let file_id = m.as_file().original_file(db);
162 let cg = db.crate_graph(); 158 let cg = db.crate_graph();
163 let krates = db.relevant_crates(file_id); 159 let krate = db.lookup_intern_macro(id).krate;
164 let krate = match krates.get(0) {
165 Some(krate) => krate,
166 None => {
167 let tt = quote! { core };
168 return tt.token_trees[0].clone();
169 }
170 };
171 160
172 // XXX 161 // XXX
173 // All crates except core itself should have a dependency on core, 162 // All crates except core itself should have a dependency on core,
174 // We detect `core` by seeing whether it doesn't have such a dependency. 163 // We detect `core` by seeing whether it doesn't have such a dependency.
175 let tt = if cg[*krate].dependencies.iter().any(|dep| dep.name == "core") { 164 let tt = if cg[krate].dependencies.iter().any(|dep| dep.name == "core") {
176 quote! { core } 165 quote! { core }
177 } else { 166 } else {
178 quote! { crate } 167 quote! { crate }
@@ -264,10 +253,12 @@ fn partial_ord_expand(
264 253
265#[cfg(test)] 254#[cfg(test)]
266mod tests { 255mod tests {
267 use super::*;
268 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
269 use name::{known, Name}; 256 use name::{known, Name};
270 use ra_db::{fixture::WithFixture, SourceDatabase}; 257 use ra_db::{fixture::WithFixture, CrateId, SourceDatabase};
258
259 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
260
261 use super::*;
271 262
272 fn expand_builtin_derive(s: &str, name: Name) -> String { 263 fn expand_builtin_derive(s: &str, name: Name) -> String {
273 let def = find_builtin_derive(&name).unwrap(); 264 let def = find_builtin_derive(&name).unwrap();
@@ -291,7 +282,11 @@ mod tests {
291 282
292 let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0])); 283 let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]));
293 284
294 let loc = MacroCallLoc { def, kind: MacroCallKind::Attr(attr_id, name.to_string()) }; 285 let loc = MacroCallLoc {
286 def,
287 krate: CrateId(0),
288 kind: MacroCallKind::Attr(attr_id, name.to_string()),
289 };
295 290
296 let id: MacroCallId = db.intern_macro(loc).into(); 291 let id: MacroCallId = db.intern_macro(loc).into();
297 let parsed = db.parse_or_expand(id.as_file()).unwrap(); 292 let parsed = db.parse_or_expand(id.as_file()).unwrap();
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index 3bce8f673..b50eb347c 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -1,15 +1,14 @@
1//! Builtin macro 1//! Builtin macro
2use crate::db::AstDatabase;
3use crate::{ 2use crate::{
4 ast::{self, AstToken, HasStringValue}, 3 db::AstDatabase, name, quote, AstId, CrateId, EagerMacroId, LazyMacroId, MacroCallId,
5 name, AstId, CrateId, MacroDefId, MacroDefKind, TextSize, 4 MacroDefId, MacroDefKind, TextSize,
6}; 5};
7 6
8use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId};
9use either::Either; 7use either::Either;
10use mbe::parse_to_token_tree; 8use mbe::parse_to_token_tree;
11use ra_db::{FileId, RelativePath}; 9use ra_db::FileId;
12use ra_parser::FragmentKind; 10use ra_parser::FragmentKind;
11use ra_syntax::ast::{self, AstToken, HasStringValue};
13 12
14macro_rules! register_builtin { 13macro_rules! register_builtin {
15 ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => { 14 ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
@@ -295,19 +294,13 @@ fn concat_expand(
295 294
296fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> { 295fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> {
297 let call_site = call_id.as_file().original_file(db); 296 let call_site = call_id.as_file().original_file(db);
298 297 let res = db.resolve_path(call_site, path)?;
299 // Handle trivial case 298 // Prevent include itself
300 if let Some(res) = db.resolve_relative_path(call_site, &RelativePath::new(&path)) { 299 if res == call_site {
301 // Prevent include itself 300 None
302 return if res == call_site { None } else { Some(res) }; 301 } else {
302 Some(res)
303 } 303 }
304
305 // Extern paths ?
306 let krate = *db.relevant_crates(call_site).get(0)?;
307 let (extern_source_id, relative_file) =
308 db.crate_graph()[krate].extern_source.extern_path(path)?;
309
310 db.resolve_extern_path(extern_source_id, &relative_file)
311} 304}
312 305
313fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> { 306fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> {
@@ -339,10 +332,7 @@ fn include_expand(
339} 332}
340 333
341fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> { 334fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> {
342 let call_id: MacroCallId = arg_id.into(); 335 let krate = db.lookup_intern_eager_expansion(arg_id).krate;
343 let original_file = call_id.as_file().original_file(db);
344
345 let krate = *db.relevant_crates(original_file).get(0)?;
346 db.crate_graph()[krate].env.get(key) 336 db.crate_graph()[krate].env.get(key)
347} 337}
348 338
@@ -401,6 +391,7 @@ mod tests {
401 391
402 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap(); 392 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap();
403 393
394 let krate = CrateId(0);
404 let file_id = match expander { 395 let file_id = match expander {
405 Either::Left(expander) => { 396 Either::Left(expander) => {
406 // the first one should be a macro_rules 397 // the first one should be a macro_rules
@@ -413,6 +404,7 @@ mod tests {
413 404
414 let loc = MacroCallLoc { 405 let loc = MacroCallLoc {
415 def, 406 def,
407 krate,
416 kind: MacroCallKind::FnLike(AstId::new( 408 kind: MacroCallKind::FnLike(AstId::new(
417 file_id.into(), 409 file_id.into(),
418 ast_id_map.ast_id(&macro_calls[1]), 410 ast_id_map.ast_id(&macro_calls[1]),
@@ -425,7 +417,7 @@ mod tests {
425 Either::Right(expander) => { 417 Either::Right(expander) => {
426 // the first one should be a macro_rules 418 // the first one should be a macro_rules
427 let def = MacroDefId { 419 let def = MacroDefId {
428 krate: Some(CrateId(0)), 420 krate: Some(krate),
429 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))), 421 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
430 kind: MacroDefKind::BuiltInEager(expander), 422 kind: MacroDefKind::BuiltInEager(expander),
431 local_inner: false, 423 local_inner: false,
@@ -439,6 +431,7 @@ mod tests {
439 def, 431 def,
440 fragment: FragmentKind::Expr, 432 fragment: FragmentKind::Expr,
441 subtree: Arc::new(parsed_args.clone()), 433 subtree: Arc::new(parsed_args.clone()),
434 krate,
442 file_id: file_id.into(), 435 file_id: file_id.into(),
443 } 436 }
444 }); 437 });
@@ -448,6 +441,7 @@ mod tests {
448 def, 441 def,
449 fragment, 442 fragment,
450 subtree: Arc::new(subtree), 443 subtree: Arc::new(subtree),
444 krate,
451 file_id: file_id.into(), 445 file_id: file_id.into(),
452 }; 446 };
453 447
diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs
index 932f47c30..302d2b3e0 100644
--- a/crates/ra_hir_expand/src/eager.rs
+++ b/crates/ra_hir_expand/src/eager.rs
@@ -25,12 +25,14 @@ use crate::{
25 EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 25 EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
26}; 26};
27 27
28use ra_db::CrateId;
28use ra_parser::FragmentKind; 29use ra_parser::FragmentKind;
29use ra_syntax::{algo::SyntaxRewriter, SyntaxNode}; 30use ra_syntax::{algo::SyntaxRewriter, SyntaxNode};
30use std::sync::Arc; 31use std::sync::Arc;
31 32
32pub fn expand_eager_macro( 33pub fn expand_eager_macro(
33 db: &dyn AstDatabase, 34 db: &dyn AstDatabase,
35 krate: CrateId,
34 macro_call: InFile<ast::MacroCall>, 36 macro_call: InFile<ast::MacroCall>,
35 def: MacroDefId, 37 def: MacroDefId,
36 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 38 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
@@ -47,6 +49,7 @@ pub fn expand_eager_macro(
47 def, 49 def,
48 fragment: FragmentKind::Expr, 50 fragment: FragmentKind::Expr,
49 subtree: Arc::new(parsed_args.clone()), 51 subtree: Arc::new(parsed_args.clone()),
52 krate,
50 file_id: macro_call.file_id, 53 file_id: macro_call.file_id,
51 } 54 }
52 }); 55 });
@@ -56,14 +59,20 @@ pub fn expand_eager_macro(
56 let result = eager_macro_recur( 59 let result = eager_macro_recur(
57 db, 60 db,
58 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()), 61 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()),
62 krate,
59 resolver, 63 resolver,
60 )?; 64 )?;
61 let subtree = to_subtree(&result)?; 65 let subtree = to_subtree(&result)?;
62 66
63 if let MacroDefKind::BuiltInEager(eager) = def.kind { 67 if let MacroDefKind::BuiltInEager(eager) = def.kind {
64 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?; 68 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?;
65 let eager = 69 let eager = EagerCallLoc {
66 EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id }; 70 def,
71 fragment,
72 subtree: Arc::new(subtree),
73 krate,
74 file_id: macro_call.file_id,
75 };
67 76
68 Some(db.intern_eager_expansion(eager)) 77 Some(db.intern_eager_expansion(eager))
69 } else { 78 } else {
@@ -81,11 +90,12 @@ fn lazy_expand(
81 db: &dyn AstDatabase, 90 db: &dyn AstDatabase,
82 def: &MacroDefId, 91 def: &MacroDefId,
83 macro_call: InFile<ast::MacroCall>, 92 macro_call: InFile<ast::MacroCall>,
93 krate: CrateId,
84) -> Option<InFile<SyntaxNode>> { 94) -> Option<InFile<SyntaxNode>> {
85 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); 95 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
86 96
87 let id: MacroCallId = 97 let id: MacroCallId =
88 def.as_lazy_macro(db, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); 98 def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
89 99
90 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)) 100 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node))
91} 101}
@@ -93,6 +103,7 @@ fn lazy_expand(
93fn eager_macro_recur( 103fn eager_macro_recur(
94 db: &dyn AstDatabase, 104 db: &dyn AstDatabase,
95 curr: InFile<SyntaxNode>, 105 curr: InFile<SyntaxNode>,
106 krate: CrateId,
96 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 107 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
97) -> Option<SyntaxNode> { 108) -> Option<SyntaxNode> {
98 let original = curr.value.clone(); 109 let original = curr.value.clone();
@@ -105,18 +116,23 @@ fn eager_macro_recur(
105 let def: MacroDefId = macro_resolver(child.path()?)?; 116 let def: MacroDefId = macro_resolver(child.path()?)?;
106 let insert = match def.kind { 117 let insert = match def.kind {
107 MacroDefKind::BuiltInEager(_) => { 118 MacroDefKind::BuiltInEager(_) => {
108 let id: MacroCallId = 119 let id: MacroCallId = expand_eager_macro(
109 expand_eager_macro(db, curr.with_value(child.clone()), def, macro_resolver)? 120 db,
110 .into(); 121 krate,
122 curr.with_value(child.clone()),
123 def,
124 macro_resolver,
125 )?
126 .into();
111 db.parse_or_expand(id.as_file())? 127 db.parse_or_expand(id.as_file())?
112 } 128 }
113 MacroDefKind::Declarative 129 MacroDefKind::Declarative
114 | MacroDefKind::BuiltIn(_) 130 | MacroDefKind::BuiltIn(_)
115 | MacroDefKind::BuiltInDerive(_) 131 | MacroDefKind::BuiltInDerive(_)
116 | MacroDefKind::CustomDerive(_) => { 132 | MacroDefKind::CustomDerive(_) => {
117 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?; 133 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?;
118 // replace macro inside 134 // replace macro inside
119 eager_macro_recur(db, expanded, macro_resolver)? 135 eager_macro_recur(db, expanded, krate, macro_resolver)?
120 } 136 }
121 }; 137 };
122 138
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index f440c073b..5eac2605b 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -209,8 +209,13 @@ pub struct MacroDefId {
209} 209}
210 210
211impl MacroDefId { 211impl MacroDefId {
212 pub fn as_lazy_macro(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> LazyMacroId { 212 pub fn as_lazy_macro(
213 db.intern_macro(MacroCallLoc { def: self, kind }) 213 self,
214 db: &dyn db::AstDatabase,
215 krate: CrateId,
216 kind: MacroCallKind,
217 ) -> LazyMacroId {
218 db.intern_macro(MacroCallLoc { def: self, krate, kind })
214 } 219 }
215} 220}
216 221
@@ -227,6 +232,7 @@ pub enum MacroDefKind {
227#[derive(Debug, Clone, PartialEq, Eq, Hash)] 232#[derive(Debug, Clone, PartialEq, Eq, Hash)]
228pub struct MacroCallLoc { 233pub struct MacroCallLoc {
229 pub(crate) def: MacroDefId, 234 pub(crate) def: MacroDefId,
235 pub(crate) krate: CrateId,
230 pub(crate) kind: MacroCallKind, 236 pub(crate) kind: MacroCallKind,
231} 237}
232 238
@@ -274,6 +280,7 @@ pub struct EagerCallLoc {
274 pub(crate) def: MacroDefId, 280 pub(crate) def: MacroDefId,
275 pub(crate) fragment: FragmentKind, 281 pub(crate) fragment: FragmentKind,
276 pub(crate) subtree: Arc<tt::Subtree>, 282 pub(crate) subtree: Arc<tt::Subtree>,
283 pub(crate) krate: CrateId,
277 pub(crate) file_id: HirFileId, 284 pub(crate) file_id: HirFileId,
278} 285}
279 286
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index ea495cb11..660bdfe33 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -153,6 +153,7 @@ pub mod known {
153 str, 153 str,
154 // Special names 154 // Special names
155 macro_rules, 155 macro_rules,
156 doc,
156 // Components of known path (value or mod name) 157 // Components of known path (value or mod name)
157 std, 158 std,
158 core, 159 core,
diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs
index c1fb762de..09fc18c36 100644
--- a/crates/ra_hir_expand/src/test_db.rs
+++ b/crates/ra_hir_expand/src/test_db.rs
@@ -5,7 +5,8 @@ use std::{
5 sync::{Arc, Mutex}, 5 sync::{Arc, Mutex},
6}; 6};
7 7
8use ra_db::{salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath}; 8use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate};
9use rustc_hash::FxHashSet;
9 10
10#[salsa::database( 11#[salsa::database(
11 ra_db::SourceDatabaseExtStorage, 12 ra_db::SourceDatabaseExtStorage,
@@ -41,21 +42,10 @@ impl FileLoader for TestDB {
41 fn file_text(&self, file_id: FileId) -> Arc<String> { 42 fn file_text(&self, file_id: FileId) -> Arc<String> {
42 FileLoaderDelegate(self).file_text(file_id) 43 FileLoaderDelegate(self).file_text(file_id)
43 } 44 }
44 fn resolve_relative_path( 45 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
45 &self, 46 FileLoaderDelegate(self).resolve_path(anchor, path)
46 anchor: FileId,
47 relative_path: &RelativePath,
48 ) -> Option<FileId> {
49 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
50 } 47 }
51 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 48 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
52 FileLoaderDelegate(self).relevant_crates(file_id) 49 FileLoaderDelegate(self).relevant_crates(file_id)
53 } 50 }
54 fn resolve_extern_path(
55 &self,
56 anchor: ExternSourceId,
57 relative_path: &RelativePath,
58 ) -> Option<FileId> {
59 FileLoaderDelegate(self).resolve_extern_path(anchor, relative_path)
60 }
61} 51}
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 4b8dcdc07..112fcd07e 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -27,8 +27,8 @@ test_utils = { path = "../test_utils" }
27 27
28scoped-tls = "1" 28scoped-tls = "1"
29 29
30chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" } 30chalk-solve = "0.11"
31chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" } 31chalk-ir = "0.11"
32 32
33[dev-dependencies] 33[dev-dependencies]
34insta = "0.16.0" 34insta = "0.16.0"
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs
index 0a8bb24ac..bf71d38d6 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -3,8 +3,8 @@
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::{ 5use hir_def::{
6 db::DefDatabase, DefWithBodyId, GenericDefId, ImplId, LocalFieldId, TraitId, TypeParamId, 6 db::DefDatabase, DefWithBodyId, FunctionId, GenericDefId, ImplId, LocalFieldId, TraitId,
7 VariantId, 7 TypeParamId, VariantId,
8}; 8};
9use ra_arena::map::ArenaMap; 9use ra_arena::map::ArenaMap;
10use ra_db::{impl_intern_key, salsa, CrateId, Upcast}; 10use ra_db::{impl_intern_key, salsa, CrateId, Upcast};
@@ -13,8 +13,8 @@ use ra_prof::profile;
13use crate::{ 13use crate::{
14 method_resolution::{CrateImplDefs, TyFingerprint}, 14 method_resolution::{CrateImplDefs, TyFingerprint},
15 traits::{chalk, AssocTyValue, Impl}, 15 traits::{chalk, AssocTyValue, Impl},
16 Binders, CallableDef, GenericPredicate, InferenceResult, PolyFnSig, Substs, TraitRef, Ty, 16 Binders, CallableDef, GenericPredicate, InferenceResult, OpaqueTyId, PolyFnSig,
17 TyDefId, TypeCtor, ValueTyDefId, 17 ReturnTypeImplTraits, Substs, TraitRef, Ty, TyDefId, TypeCtor, ValueTyDefId,
18}; 18};
19use hir_expand::name::Name; 19use hir_expand::name::Name;
20 20
@@ -48,6 +48,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
48 #[salsa::invoke(crate::callable_item_sig)] 48 #[salsa::invoke(crate::callable_item_sig)]
49 fn callable_item_signature(&self, def: CallableDef) -> PolyFnSig; 49 fn callable_item_signature(&self, def: CallableDef) -> PolyFnSig;
50 50
51 #[salsa::invoke(crate::lower::return_type_impl_traits)]
52 fn return_type_impl_traits(
53 &self,
54 def: FunctionId,
55 ) -> Option<Arc<Binders<ReturnTypeImplTraits>>>;
56
51 #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] 57 #[salsa::invoke(crate::lower::generic_predicates_for_param_query)]
52 #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] 58 #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)]
53 fn generic_predicates_for_param( 59 fn generic_predicates_for_param(
@@ -80,6 +86,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
80 #[salsa::interned] 86 #[salsa::interned]
81 fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId; 87 fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId;
82 #[salsa::interned] 88 #[salsa::interned]
89 fn intern_impl_trait_id(&self, id: OpaqueTyId) -> InternedOpaqueTyId;
90 #[salsa::interned]
83 fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId; 91 fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId;
84 #[salsa::interned] 92 #[salsa::interned]
85 fn intern_assoc_ty_value(&self, assoc_ty_value: AssocTyValue) -> crate::traits::AssocTyValueId; 93 fn intern_assoc_ty_value(&self, assoc_ty_value: AssocTyValue) -> crate::traits::AssocTyValueId;
@@ -142,3 +150,7 @@ fn hir_database_is_object_safe() {
142#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 150#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
143pub struct GlobalTypeParamId(salsa::InternId); 151pub struct GlobalTypeParamId(salsa::InternId);
144impl_intern_key!(GlobalTypeParamId); 152impl_intern_key!(GlobalTypeParamId);
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
155pub struct InternedOpaqueTyId(salsa::InternId);
156impl_intern_key!(InternedOpaqueTyId);
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs
index b9c4d2e89..3c97e1354 100644
--- a/crates/ra_hir_ty/src/display.rs
+++ b/crates/ra_hir_ty/src/display.rs
@@ -4,7 +4,7 @@ use std::fmt;
4 4
5use crate::{ 5use crate::{
6 db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, 6 db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate,
7 Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, 7 Obligation, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
8}; 8};
9use hir_def::{ 9use hir_def::{
10 find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, 10 find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId,
@@ -359,6 +359,21 @@ impl HirDisplay for ApplicationTy {
359 write!(f, ">")?; 359 write!(f, ">")?;
360 } 360 }
361 } 361 }
362 TypeCtor::OpaqueType(opaque_ty_id) => {
363 let bounds = match opaque_ty_id {
364 OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
365 let datas =
366 f.db.return_type_impl_traits(func).expect("impl trait id without data");
367 let data = (*datas)
368 .as_ref()
369 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
370 data.clone().subst(&self.parameters)
371 }
372 };
373 write!(f, "impl ")?;
374 write_bounds_like_dyn_trait(&bounds.value, f)?;
375 // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
376 }
362 TypeCtor::Closure { .. } => { 377 TypeCtor::Closure { .. } => {
363 let sig = self.parameters[0].callable_sig(f.db); 378 let sig = self.parameters[0].callable_sig(f.db);
364 if let Some(sig) = sig { 379 if let Some(sig) = sig {
@@ -427,14 +442,24 @@ impl HirDisplay for Ty {
427 } 442 }
428 } 443 }
429 Ty::Bound(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?, 444 Ty::Bound(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?,
430 Ty::Dyn(predicates) | Ty::Opaque(predicates) => { 445 Ty::Dyn(predicates) => {
431 match self { 446 write!(f, "dyn ")?;
432 Ty::Dyn(_) => write!(f, "dyn ")?,
433 Ty::Opaque(_) => write!(f, "impl ")?,
434 _ => unreachable!(),
435 };
436 write_bounds_like_dyn_trait(predicates, f)?; 447 write_bounds_like_dyn_trait(predicates, f)?;
437 } 448 }
449 Ty::Opaque(opaque_ty) => {
450 let bounds = match opaque_ty.opaque_ty_id {
451 OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
452 let datas =
453 f.db.return_type_impl_traits(func).expect("impl trait id without data");
454 let data = (*datas)
455 .as_ref()
456 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
457 data.clone().subst(&opaque_ty.parameters)
458 }
459 };
460 write!(f, "impl ")?;
461 write_bounds_like_dyn_trait(&bounds.value, f)?;
462 }
438 Ty::Unknown => write!(f, "{{unknown}}")?, 463 Ty::Unknown => write!(f, "{{unknown}}")?,
439 Ty::Infer(..) => write!(f, "_")?, 464 Ty::Infer(..) => write!(f, "_")?,
440 } 465 }
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs
index f04968e14..7db928dde 100644
--- a/crates/ra_hir_ty/src/expr.rs
+++ b/crates/ra_hir_ty/src/expr.rs
@@ -226,17 +226,19 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
226 None => return, 226 None => return,
227 }; 227 };
228 228
229 let std_result_path = path![std::result::Result]; 229 let core_result_path = path![core::result::Result];
230 230
231 let resolver = self.func.resolver(db.upcast()); 231 let resolver = self.func.resolver(db.upcast());
232 let std_result_enum = match resolver.resolve_known_enum(db.upcast(), &std_result_path) { 232 let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) {
233 Some(it) => it, 233 Some(it) => it,
234 _ => return, 234 _ => return,
235 }; 235 };
236 236
237 let std_result_ctor = TypeCtor::Adt(AdtId::EnumId(std_result_enum)); 237 let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum));
238 let params = match &mismatch.expected { 238 let params = match &mismatch.expected {
239 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters, 239 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => {
240 parameters
241 }
240 _ => return, 242 _ => return,
241 }; 243 };
242 244
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index dc77e88e5..3719f76a6 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -39,8 +39,7 @@ use ra_syntax::SmolStr;
39use super::{ 39use super::{
40 primitive::{FloatTy, IntTy}, 40 primitive::{FloatTy, IntTy},
41 traits::{Guidance, Obligation, ProjectionPredicate, Solution}, 41 traits::{Guidance, Obligation, ProjectionPredicate, Solution},
42 ApplicationTy, GenericPredicate, InEnvironment, ProjectionTy, Substs, TraitEnvironment, 42 InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
43 TraitRef, Ty, TypeCtor, TypeWalk, Uncertain,
44}; 43};
45use crate::{ 44use crate::{
46 db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode, 45 db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode,
@@ -312,12 +311,6 @@ impl<'a> InferenceContext<'a> {
312 fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { 311 fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
313 match ty { 312 match ty {
314 Ty::Unknown => self.table.new_type_var(), 313 Ty::Unknown => self.table.new_type_var(),
315 Ty::Apply(ApplicationTy { ctor: TypeCtor::Int(Uncertain::Unknown), .. }) => {
316 self.table.new_integer_var()
317 }
318 Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(Uncertain::Unknown), .. }) => {
319 self.table.new_float_var()
320 }
321 _ => ty, 314 _ => ty,
322 } 315 }
323 } 316 }
@@ -383,25 +376,6 @@ impl<'a> InferenceContext<'a> {
383 ) -> Ty { 376 ) -> Ty {
384 match assoc_ty { 377 match assoc_ty {
385 Some(res_assoc_ty) => { 378 Some(res_assoc_ty) => {
386 // FIXME:
387 // Check if inner_ty is is `impl Trait` and contained input TypeAlias id
388 // this is a workaround while Chalk assoc type projection doesn't always work yet,
389 // but once that is fixed I don't think we should keep this
390 // (we'll probably change how associated types are resolved anyway)
391 if let Ty::Opaque(ref predicates) = inner_ty {
392 for p in predicates.iter() {
393 if let GenericPredicate::Projection(projection) = p {
394 if projection.projection_ty.associated_ty == res_assoc_ty {
395 if let ty_app!(_, params) = &projection.ty {
396 if params.len() == 0 {
397 return projection.ty.clone();
398 }
399 }
400 }
401 }
402 }
403 }
404
405 let ty = self.table.new_type_var(); 379 let ty = self.table.new_type_var();
406 let builder = Substs::build_for_def(self.db, res_assoc_ty) 380 let builder = Substs::build_for_def(self.db, res_assoc_ty)
407 .push(inner_ty) 381 .push(inner_ty)
@@ -458,13 +432,13 @@ impl<'a> InferenceContext<'a> {
458 }; 432 };
459 return match resolution { 433 return match resolution {
460 TypeNs::AdtId(AdtId::StructId(strukt)) => { 434 TypeNs::AdtId(AdtId::StructId(strukt)) => {
461 let substs = Ty::substs_from_path(&ctx, path, strukt.into()); 435 let substs = Ty::substs_from_path(&ctx, path, strukt.into(), true);
462 let ty = self.db.ty(strukt.into()); 436 let ty = self.db.ty(strukt.into());
463 let ty = self.insert_type_vars(ty.subst(&substs)); 437 let ty = self.insert_type_vars(ty.subst(&substs));
464 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) 438 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
465 } 439 }
466 TypeNs::EnumVariantId(var) => { 440 TypeNs::EnumVariantId(var) => {
467 let substs = Ty::substs_from_path(&ctx, path, var.into()); 441 let substs = Ty::substs_from_path(&ctx, path, var.into(), true);
468 let ty = self.db.ty(var.parent.into()); 442 let ty = self.db.ty(var.parent.into());
469 let ty = self.insert_type_vars(ty.subst(&substs)); 443 let ty = self.insert_type_vars(ty.subst(&substs));
470 forbid_unresolved_segments((ty, Some(var.into())), unresolved) 444 forbid_unresolved_segments((ty, Some(var.into())), unresolved)
@@ -581,13 +555,13 @@ impl<'a> InferenceContext<'a> {
581 } 555 }
582 556
583 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> { 557 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
584 let path = path![std::iter::IntoIterator]; 558 let path = path![core::iter::IntoIterator];
585 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; 559 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
586 self.db.trait_data(trait_).associated_type_by_name(&name![Item]) 560 self.db.trait_data(trait_).associated_type_by_name(&name![Item])
587 } 561 }
588 562
589 fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> { 563 fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> {
590 let path = path![std::ops::Try]; 564 let path = path![core::ops::Try];
591 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; 565 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
592 self.db.trait_data(trait_).associated_type_by_name(&name![Ok]) 566 self.db.trait_data(trait_).associated_type_by_name(&name![Ok])
593 } 567 }
@@ -613,37 +587,37 @@ impl<'a> InferenceContext<'a> {
613 } 587 }
614 588
615 fn resolve_range_full(&self) -> Option<AdtId> { 589 fn resolve_range_full(&self) -> Option<AdtId> {
616 let path = path![std::ops::RangeFull]; 590 let path = path![core::ops::RangeFull];
617 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 591 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
618 Some(struct_.into()) 592 Some(struct_.into())
619 } 593 }
620 594
621 fn resolve_range(&self) -> Option<AdtId> { 595 fn resolve_range(&self) -> Option<AdtId> {
622 let path = path![std::ops::Range]; 596 let path = path![core::ops::Range];
623 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 597 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
624 Some(struct_.into()) 598 Some(struct_.into())
625 } 599 }
626 600
627 fn resolve_range_inclusive(&self) -> Option<AdtId> { 601 fn resolve_range_inclusive(&self) -> Option<AdtId> {
628 let path = path![std::ops::RangeInclusive]; 602 let path = path![core::ops::RangeInclusive];
629 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 603 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
630 Some(struct_.into()) 604 Some(struct_.into())
631 } 605 }
632 606
633 fn resolve_range_from(&self) -> Option<AdtId> { 607 fn resolve_range_from(&self) -> Option<AdtId> {
634 let path = path![std::ops::RangeFrom]; 608 let path = path![core::ops::RangeFrom];
635 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 609 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
636 Some(struct_.into()) 610 Some(struct_.into())
637 } 611 }
638 612
639 fn resolve_range_to(&self) -> Option<AdtId> { 613 fn resolve_range_to(&self) -> Option<AdtId> {
640 let path = path![std::ops::RangeTo]; 614 let path = path![core::ops::RangeTo];
641 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 615 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
642 Some(struct_.into()) 616 Some(struct_.into())
643 } 617 }
644 618
645 fn resolve_range_to_inclusive(&self) -> Option<AdtId> { 619 fn resolve_range_to_inclusive(&self) -> Option<AdtId> {
646 let path = path![std::ops::RangeToInclusive]; 620 let path = path![core::ops::RangeToInclusive];
647 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 621 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
648 Some(struct_.into()) 622 Some(struct_.into())
649 } 623 }
@@ -683,8 +657,8 @@ impl InferTy {
683 fn fallback_value(self) -> Ty { 657 fn fallback_value(self) -> Ty {
684 match self { 658 match self {
685 InferTy::TypeVar(..) => Ty::Unknown, 659 InferTy::TypeVar(..) => Ty::Unknown,
686 InferTy::IntVar(..) => Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::i32()))), 660 InferTy::IntVar(..) => Ty::simple(TypeCtor::Int(IntTy::i32())),
687 InferTy::FloatVar(..) => Ty::simple(TypeCtor::Float(Uncertain::Known(FloatTy::f64()))), 661 InferTy::FloatVar(..) => Ty::simple(TypeCtor::Float(FloatTy::f64())),
688 InferTy::MaybeNeverTypeVar(..) => Ty::simple(TypeCtor::Never), 662 InferTy::MaybeNeverTypeVar(..) => Ty::simple(TypeCtor::Never),
689 } 663 }
690 } 664 }
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 4a98e2deb..9fd310f69 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -18,7 +18,7 @@ use crate::{
18 traits::InEnvironment, 18 traits::InEnvironment,
19 utils::{generics, variant_data, Generics}, 19 utils::{generics, variant_data, Generics},
20 ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs, 20 ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs,
21 TraitRef, Ty, TypeCtor, Uncertain, 21 TraitRef, Ty, TypeCtor,
22}; 22};
23 23
24use super::{ 24use super::{
@@ -426,15 +426,7 @@ impl<'a> InferenceContext<'a> {
426 match &inner_ty { 426 match &inner_ty {
427 // Fast path for builtins 427 // Fast path for builtins
428 Ty::Apply(ApplicationTy { 428 Ty::Apply(ApplicationTy {
429 ctor: 429 ctor: TypeCtor::Int(IntTy { signedness: Signedness::Signed, .. }),
430 TypeCtor::Int(Uncertain::Known(IntTy {
431 signedness: Signedness::Signed,
432 ..
433 })),
434 ..
435 })
436 | Ty::Apply(ApplicationTy {
437 ctor: TypeCtor::Int(Uncertain::Unknown),
438 .. 430 ..
439 }) 431 })
440 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(_), .. }) 432 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(_), .. })
@@ -577,9 +569,7 @@ impl<'a> InferenceContext<'a> {
577 ); 569 );
578 self.infer_expr( 570 self.infer_expr(
579 *repeat, 571 *repeat,
580 &Expectation::has_type(Ty::simple(TypeCtor::Int(Uncertain::Known( 572 &Expectation::has_type(Ty::simple(TypeCtor::Int(IntTy::usize()))),
581 IntTy::usize(),
582 )))),
583 ); 573 );
584 } 574 }
585 } 575 }
@@ -592,13 +582,19 @@ impl<'a> InferenceContext<'a> {
592 Ty::apply_one(TypeCtor::Ref(Mutability::Shared), Ty::simple(TypeCtor::Str)) 582 Ty::apply_one(TypeCtor::Ref(Mutability::Shared), Ty::simple(TypeCtor::Str))
593 } 583 }
594 Literal::ByteString(..) => { 584 Literal::ByteString(..) => {
595 let byte_type = Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::u8()))); 585 let byte_type = Ty::simple(TypeCtor::Int(IntTy::u8()));
596 let array_type = Ty::apply_one(TypeCtor::Array, byte_type); 586 let array_type = Ty::apply_one(TypeCtor::Array, byte_type);
597 Ty::apply_one(TypeCtor::Ref(Mutability::Shared), array_type) 587 Ty::apply_one(TypeCtor::Ref(Mutability::Shared), array_type)
598 } 588 }
599 Literal::Char(..) => Ty::simple(TypeCtor::Char), 589 Literal::Char(..) => Ty::simple(TypeCtor::Char),
600 Literal::Int(_v, ty) => Ty::simple(TypeCtor::Int((*ty).into())), 590 Literal::Int(_v, ty) => match ty {
601 Literal::Float(_v, ty) => Ty::simple(TypeCtor::Float((*ty).into())), 591 Some(int_ty) => Ty::simple(TypeCtor::Int((*int_ty).into())),
592 None => self.table.new_integer_var(),
593 },
594 Literal::Float(_v, ty) => match ty {
595 Some(float_ty) => Ty::simple(TypeCtor::Float((*float_ty).into())),
596 None => self.table.new_float_var(),
597 },
602 }, 598 },
603 }; 599 };
604 // use a new type variable if we got Ty::Unknown here 600 // use a new type variable if we got Ty::Unknown here
diff --git a/crates/ra_hir_ty/src/infer/path.rs b/crates/ra_hir_ty/src/infer/path.rs
index 1c2e56fb0..1ad0d8397 100644
--- a/crates/ra_hir_ty/src/infer/path.rs
+++ b/crates/ra_hir_ty/src/infer/path.rs
@@ -95,7 +95,7 @@ impl<'a> InferenceContext<'a> {
95 // self_subst is just for the parent 95 // self_subst is just for the parent
96 let parent_substs = self_subst.unwrap_or_else(Substs::empty); 96 let parent_substs = self_subst.unwrap_or_else(Substs::empty);
97 let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); 97 let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
98 let substs = Ty::substs_from_path(&ctx, path, typable); 98 let substs = Ty::substs_from_path(&ctx, path, typable, true);
99 let full_substs = Substs::builder(substs.len()) 99 let full_substs = Substs::builder(substs.len())
100 .use_parent_substs(&parent_substs) 100 .use_parent_substs(&parent_substs)
101 .fill(substs.0[parent_substs.len()..].iter().cloned()) 101 .fill(substs.0[parent_substs.len()..].iter().cloned())
@@ -141,6 +141,7 @@ impl<'a> InferenceContext<'a> {
141 def, 141 def,
142 resolved_segment, 142 resolved_segment,
143 remaining_segments_for_ty, 143 remaining_segments_for_ty,
144 true,
144 ); 145 );
145 if let Ty::Unknown = ty { 146 if let Ty::Unknown = ty {
146 return None; 147 return None;
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index 9fa8d3bdc..2b9372b4b 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -58,7 +58,7 @@ use ra_db::{impl_intern_key, salsa, CrateId};
58 58
59use crate::{ 59use crate::{
60 db::HirDatabase, 60 db::HirDatabase,
61 primitive::{FloatTy, IntTy, Uncertain}, 61 primitive::{FloatTy, IntTy},
62 utils::{generics, make_mut_slice, Generics}, 62 utils::{generics, make_mut_slice, Generics},
63}; 63};
64use display::HirDisplay; 64use display::HirDisplay;
@@ -87,10 +87,10 @@ pub enum TypeCtor {
87 Char, 87 Char,
88 88
89 /// A primitive integer type. For example, `i32`. 89 /// A primitive integer type. For example, `i32`.
90 Int(Uncertain<IntTy>), 90 Int(IntTy),
91 91
92 /// A primitive floating-point type. For example, `f64`. 92 /// A primitive floating-point type. For example, `f64`.
93 Float(Uncertain<FloatTy>), 93 Float(FloatTy),
94 94
95 /// Structures, enumerations and unions. 95 /// Structures, enumerations and unions.
96 Adt(AdtId), 96 Adt(AdtId),
@@ -147,6 +147,12 @@ pub enum TypeCtor {
147 /// an **application type** like `(Iterator::Item)<T>`. 147 /// an **application type** like `(Iterator::Item)<T>`.
148 AssociatedType(TypeAliasId), 148 AssociatedType(TypeAliasId),
149 149
150 /// This represents a placeholder for an opaque type in situations where we
151 /// don't know the hidden type (i.e. currently almost always). This is
152 /// analogous to the `AssociatedType` type constructor. As with that one,
153 /// these are only produced by Chalk.
154 OpaqueType(OpaqueTyId),
155
150 /// The type of a specific closure. 156 /// The type of a specific closure.
151 /// 157 ///
152 /// The closure signature is stored in a `FnPtr` type in the first type 158 /// The closure signature is stored in a `FnPtr` type in the first type
@@ -194,6 +200,14 @@ impl TypeCtor {
194 let generic_params = generics(db.upcast(), type_alias.into()); 200 let generic_params = generics(db.upcast(), type_alias.into());
195 generic_params.len() 201 generic_params.len()
196 } 202 }
203 TypeCtor::OpaqueType(opaque_ty_id) => {
204 match opaque_ty_id {
205 OpaqueTyId::ReturnTypeImplTrait(func, _) => {
206 let generic_params = generics(db.upcast(), func.into());
207 generic_params.len()
208 }
209 }
210 }
197 TypeCtor::FnPtr { num_args } => num_args as usize + 1, 211 TypeCtor::FnPtr { num_args } => num_args as usize + 1,
198 TypeCtor::Tuple { cardinality } => cardinality as usize, 212 TypeCtor::Tuple { cardinality } => cardinality as usize,
199 } 213 }
@@ -220,6 +234,11 @@ impl TypeCtor {
220 TypeCtor::AssociatedType(type_alias) => { 234 TypeCtor::AssociatedType(type_alias) => {
221 Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate) 235 Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate)
222 } 236 }
237 TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id {
238 OpaqueTyId::ReturnTypeImplTrait(func, _) => {
239 Some(func.lookup(db.upcast()).module(db.upcast()).krate)
240 }
241 },
223 } 242 }
224 } 243 }
225 244
@@ -241,6 +260,7 @@ impl TypeCtor {
241 TypeCtor::Adt(adt) => Some(adt.into()), 260 TypeCtor::Adt(adt) => Some(adt.into()),
242 TypeCtor::FnDef(callable) => Some(callable.into()), 261 TypeCtor::FnDef(callable) => Some(callable.into()),
243 TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()), 262 TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()),
263 TypeCtor::OpaqueType(_impl_trait_id) => None,
244 } 264 }
245 } 265 }
246} 266}
@@ -254,6 +274,12 @@ pub struct ApplicationTy {
254 pub parameters: Substs, 274 pub parameters: Substs,
255} 275}
256 276
277#[derive(Clone, PartialEq, Eq, Debug, Hash)]
278pub struct OpaqueTy {
279 pub opaque_ty_id: OpaqueTyId,
280 pub parameters: Substs,
281}
282
257/// A "projection" type corresponds to an (unnormalized) 283/// A "projection" type corresponds to an (unnormalized)
258/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the 284/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
259/// trait and all its parameters are fully known. 285/// trait and all its parameters are fully known.
@@ -308,6 +334,12 @@ pub enum Ty {
308 /// trait and all its parameters are fully known. 334 /// trait and all its parameters are fully known.
309 Projection(ProjectionTy), 335 Projection(ProjectionTy),
310 336
337 /// An opaque type (`impl Trait`).
338 ///
339 /// This is currently only used for return type impl trait; each instance of
340 /// `impl Trait` in a return type gets its own ID.
341 Opaque(OpaqueTy),
342
311 /// A placeholder for a type parameter; for example, `T` in `fn f<T>(x: T) 343 /// A placeholder for a type parameter; for example, `T` in `fn f<T>(x: T)
312 /// {}` when we're type-checking the body of that function. In this 344 /// {}` when we're type-checking the body of that function. In this
313 /// situation, we know this stands for *some* type, but don't know the exact 345 /// situation, we know this stands for *some* type, but don't know the exact
@@ -332,12 +364,6 @@ pub enum Ty {
332 /// didn't seem worth the overhead yet. 364 /// didn't seem worth the overhead yet.
333 Dyn(Arc<[GenericPredicate]>), 365 Dyn(Arc<[GenericPredicate]>),
334 366
335 /// An opaque type (`impl Trait`).
336 ///
337 /// The predicates are quantified over the `Self` type; see `Ty::Dyn` for
338 /// more.
339 Opaque(Arc<[GenericPredicate]>),
340
341 /// A placeholder for a type which could not be computed; this is propagated 367 /// A placeholder for a type which could not be computed; this is propagated
342 /// to avoid useless error messages. Doubles as a placeholder where type 368 /// to avoid useless error messages. Doubles as a placeholder where type
343 /// variables are inserted before type checking, since we want to try to 369 /// variables are inserted before type checking, since we want to try to
@@ -490,7 +516,7 @@ impl Deref for Substs {
490 } 516 }
491} 517}
492 518
493#[derive(Copy, Clone, PartialEq, Eq, Debug)] 519#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
494pub struct Binders<T> { 520pub struct Binders<T> {
495 pub num_binders: usize, 521 pub num_binders: usize,
496 pub value: T, 522 pub value: T,
@@ -534,6 +560,20 @@ impl<T: TypeWalk> Binders<T> {
534 } 560 }
535} 561}
536 562
563impl<T: TypeWalk> TypeWalk for Binders<T> {
564 fn walk(&self, f: &mut impl FnMut(&Ty)) {
565 self.value.walk(f);
566 }
567
568 fn walk_mut_binders(
569 &mut self,
570 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
571 binders: DebruijnIndex,
572 ) {
573 self.value.walk_mut_binders(f, binders.shifted_in())
574 }
575}
576
537/// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait. 577/// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait.
538/// Name to be bikeshedded: TraitBound? TraitImplements? 578/// Name to be bikeshedded: TraitBound? TraitImplements?
539#[derive(Clone, PartialEq, Eq, Debug, Hash)] 579#[derive(Clone, PartialEq, Eq, Debug, Hash)]
@@ -947,11 +987,16 @@ impl TypeWalk for Ty {
947 t.walk(f); 987 t.walk(f);
948 } 988 }
949 } 989 }
950 Ty::Dyn(predicates) | Ty::Opaque(predicates) => { 990 Ty::Dyn(predicates) => {
951 for p in predicates.iter() { 991 for p in predicates.iter() {
952 p.walk(f); 992 p.walk(f);
953 } 993 }
954 } 994 }
995 Ty::Opaque(o_ty) => {
996 for t in o_ty.parameters.iter() {
997 t.walk(f);
998 }
999 }
955 Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} 1000 Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
956 } 1001 }
957 f(self); 1002 f(self);
@@ -969,13 +1014,48 @@ impl TypeWalk for Ty {
969 Ty::Projection(p_ty) => { 1014 Ty::Projection(p_ty) => {
970 p_ty.parameters.walk_mut_binders(f, binders); 1015 p_ty.parameters.walk_mut_binders(f, binders);
971 } 1016 }
972 Ty::Dyn(predicates) | Ty::Opaque(predicates) => { 1017 Ty::Dyn(predicates) => {
973 for p in make_mut_slice(predicates) { 1018 for p in make_mut_slice(predicates) {
974 p.walk_mut_binders(f, binders.shifted_in()); 1019 p.walk_mut_binders(f, binders.shifted_in());
975 } 1020 }
976 } 1021 }
1022 Ty::Opaque(o_ty) => {
1023 o_ty.parameters.walk_mut_binders(f, binders);
1024 }
977 Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} 1025 Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
978 } 1026 }
979 f(self, binders); 1027 f(self, binders);
980 } 1028 }
981} 1029}
1030
1031impl<T: TypeWalk> TypeWalk for Vec<T> {
1032 fn walk(&self, f: &mut impl FnMut(&Ty)) {
1033 for t in self {
1034 t.walk(f);
1035 }
1036 }
1037 fn walk_mut_binders(
1038 &mut self,
1039 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
1040 binders: DebruijnIndex,
1041 ) {
1042 for t in self {
1043 t.walk_mut_binders(f, binders);
1044 }
1045 }
1046}
1047
1048#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
1049pub enum OpaqueTyId {
1050 ReturnTypeImplTrait(hir_def::FunctionId, u16),
1051}
1052
1053#[derive(Clone, PartialEq, Eq, Debug, Hash)]
1054pub struct ReturnTypeImplTraits {
1055 pub(crate) impl_traits: Vec<ReturnTypeImplTrait>,
1056}
1057
1058#[derive(Clone, PartialEq, Eq, Debug, Hash)]
1059pub(crate) struct ReturnTypeImplTrait {
1060 pub(crate) bounds: Binders<Vec<GenericPredicate>>,
1061}
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index 35ac86a46..42713928f 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -21,8 +21,10 @@ use hir_def::{
21 HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, 21 HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
22 UnionId, VariantId, 22 UnionId, VariantId,
23}; 23};
24use hir_expand::name::Name;
24use ra_arena::map::ArenaMap; 25use ra_arena::map::ArenaMap;
25use ra_db::CrateId; 26use ra_db::CrateId;
27use test_utils::mark;
26 28
27use crate::{ 29use crate::{
28 db::HirDatabase, 30 db::HirDatabase,
@@ -31,10 +33,10 @@ use crate::{
31 all_super_trait_refs, associated_type_by_name_including_super_traits, generics, 33 all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
32 make_mut_slice, variant_data, 34 make_mut_slice, variant_data,
33 }, 35 },
34 Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, 36 Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, OpaqueTy, OpaqueTyId, PolyFnSig,
35 ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, 37 ProjectionPredicate, ProjectionTy, ReturnTypeImplTrait, ReturnTypeImplTraits, Substs,
38 TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
36}; 39};
37use hir_expand::name::Name;
38 40
39#[derive(Debug)] 41#[derive(Debug)]
40pub struct TyLoweringContext<'a> { 42pub struct TyLoweringContext<'a> {
@@ -47,7 +49,16 @@ pub struct TyLoweringContext<'a> {
47 /// possible currently, so this should be fine for now. 49 /// possible currently, so this should be fine for now.
48 pub type_param_mode: TypeParamLoweringMode, 50 pub type_param_mode: TypeParamLoweringMode,
49 pub impl_trait_mode: ImplTraitLoweringMode, 51 pub impl_trait_mode: ImplTraitLoweringMode,
50 pub impl_trait_counter: std::cell::Cell<u16>, 52 impl_trait_counter: std::cell::Cell<u16>,
53 /// When turning `impl Trait` into opaque types, we have to collect the
54 /// bounds at the same time to get the IDs correct (without becoming too
55 /// complicated). I don't like using interior mutability (as for the
56 /// counter), but I've tried and failed to make the lifetimes work for
57 /// passing around a `&mut TyLoweringContext`. The core problem is that
58 /// we're grouping the mutable data (the counter and this field) together
59 /// with the immutable context (the references to the DB and resolver).
60 /// Splitting this up would be a possible fix.
61 opaque_type_data: std::cell::RefCell<Vec<ReturnTypeImplTrait>>,
51} 62}
52 63
53impl<'a> TyLoweringContext<'a> { 64impl<'a> TyLoweringContext<'a> {
@@ -56,26 +67,42 @@ impl<'a> TyLoweringContext<'a> {
56 let impl_trait_mode = ImplTraitLoweringMode::Disallowed; 67 let impl_trait_mode = ImplTraitLoweringMode::Disallowed;
57 let type_param_mode = TypeParamLoweringMode::Placeholder; 68 let type_param_mode = TypeParamLoweringMode::Placeholder;
58 let in_binders = DebruijnIndex::INNERMOST; 69 let in_binders = DebruijnIndex::INNERMOST;
59 Self { db, resolver, in_binders, impl_trait_mode, impl_trait_counter, type_param_mode } 70 let opaque_type_data = std::cell::RefCell::new(Vec::new());
71 Self {
72 db,
73 resolver,
74 in_binders,
75 impl_trait_mode,
76 impl_trait_counter,
77 type_param_mode,
78 opaque_type_data,
79 }
60 } 80 }
61 81
62 pub fn with_shifted_in<T>( 82 pub fn with_debruijn<T>(
63 &self, 83 &self,
64 debruijn: DebruijnIndex, 84 debruijn: DebruijnIndex,
65 f: impl FnOnce(&TyLoweringContext) -> T, 85 f: impl FnOnce(&TyLoweringContext) -> T,
66 ) -> T { 86 ) -> T {
87 let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new());
67 let new_ctx = Self { 88 let new_ctx = Self {
68 in_binders: self.in_binders.shifted_in_from(debruijn), 89 in_binders: debruijn,
69 impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), 90 impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()),
91 opaque_type_data: std::cell::RefCell::new(opaque_ty_data_vec),
70 ..*self 92 ..*self
71 }; 93 };
72 let result = f(&new_ctx); 94 let result = f(&new_ctx);
73 self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); 95 self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
96 self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner());
74 result 97 result
75 } 98 }
76 99
77 pub fn shifted_in(self, debruijn: DebruijnIndex) -> Self { 100 pub fn with_shifted_in<T>(
78 Self { in_binders: self.in_binders.shifted_in_from(debruijn), ..self } 101 &self,
102 debruijn: DebruijnIndex,
103 f: impl FnOnce(&TyLoweringContext) -> T,
104 ) -> T {
105 self.with_debruijn(self.in_binders.shifted_in_from(debruijn), f)
79 } 106 }
80 107
81 pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { 108 pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self {
@@ -167,20 +194,44 @@ impl Ty {
167 TypeRef::ImplTrait(bounds) => { 194 TypeRef::ImplTrait(bounds) => {
168 match ctx.impl_trait_mode { 195 match ctx.impl_trait_mode {
169 ImplTraitLoweringMode::Opaque => { 196 ImplTraitLoweringMode::Opaque => {
170 let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); 197 let idx = ctx.impl_trait_counter.get();
171 let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { 198 ctx.impl_trait_counter.set(idx + 1);
172 bounds 199
173 .iter() 200 assert!(idx as usize == ctx.opaque_type_data.borrow().len());
174 .flat_map(|b| { 201 // this dance is to make sure the data is in the right
175 GenericPredicate::from_type_bound(ctx, b, self_ty.clone()) 202 // place even if we encounter more opaque types while
176 }) 203 // lowering the bounds
177 .collect() 204 ctx.opaque_type_data
178 }); 205 .borrow_mut()
179 Ty::Opaque(predicates) 206 .push(ReturnTypeImplTrait { bounds: Binders::new(1, Vec::new()) });
207 // We don't want to lower the bounds inside the binders
208 // we're currently in, because they don't end up inside
209 // those binders. E.g. when we have `impl Trait<impl
210 // OtherTrait<T>>`, the `impl OtherTrait<T>` can't refer
211 // to the self parameter from `impl Trait`, and the
212 // bounds aren't actually stored nested within each
213 // other, but separately. So if the `T` refers to a type
214 // parameter of the outer function, it's just one binder
215 // away instead of two.
216 let actual_opaque_type_data = ctx
217 .with_debruijn(DebruijnIndex::INNERMOST, |ctx| {
218 ReturnTypeImplTrait::from_hir(ctx, &bounds)
219 });
220 ctx.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data;
221
222 let func = match ctx.resolver.generic_def() {
223 Some(GenericDefId::FunctionId(f)) => f,
224 _ => panic!("opaque impl trait lowering in non-function"),
225 };
226 let impl_trait_id = OpaqueTyId::ReturnTypeImplTrait(func, idx);
227 let generics = generics(ctx.db.upcast(), func.into());
228 let parameters = Substs::bound_vars(&generics, ctx.in_binders);
229 Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters })
180 } 230 }
181 ImplTraitLoweringMode::Param => { 231 ImplTraitLoweringMode::Param => {
182 let idx = ctx.impl_trait_counter.get(); 232 let idx = ctx.impl_trait_counter.get();
183 ctx.impl_trait_counter.set(idx + 1); 233 // FIXME we're probably doing something wrong here
234 ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
184 if let Some(def) = ctx.resolver.generic_def() { 235 if let Some(def) = ctx.resolver.generic_def() {
185 let generics = generics(ctx.db.upcast(), def); 236 let generics = generics(ctx.db.upcast(), def);
186 let param = generics 237 let param = generics
@@ -197,7 +248,8 @@ impl Ty {
197 } 248 }
198 ImplTraitLoweringMode::Variable => { 249 ImplTraitLoweringMode::Variable => {
199 let idx = ctx.impl_trait_counter.get(); 250 let idx = ctx.impl_trait_counter.get();
200 ctx.impl_trait_counter.set(idx + 1); 251 // FIXME we're probably doing something wrong here
252 ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
201 let (parent_params, self_params, list_params, _impl_trait_params) = 253 let (parent_params, self_params, list_params, _impl_trait_params) =
202 if let Some(def) = ctx.resolver.generic_def() { 254 if let Some(def) = ctx.resolver.generic_def() {
203 let generics = generics(ctx.db.upcast(), def); 255 let generics = generics(ctx.db.upcast(), def);
@@ -271,6 +323,7 @@ impl Ty {
271 resolution: TypeNs, 323 resolution: TypeNs,
272 resolved_segment: PathSegment<'_>, 324 resolved_segment: PathSegment<'_>,
273 remaining_segments: PathSegments<'_>, 325 remaining_segments: PathSegments<'_>,
326 infer_args: bool,
274 ) -> (Ty, Option<TypeNs>) { 327 ) -> (Ty, Option<TypeNs>) {
275 let ty = match resolution { 328 let ty = match resolution {
276 TypeNs::TraitId(trait_) => { 329 TypeNs::TraitId(trait_) => {
@@ -348,9 +401,15 @@ impl Ty {
348 ctx.db.ty(adt.into()).subst(&substs) 401 ctx.db.ty(adt.into()).subst(&substs)
349 } 402 }
350 403
351 TypeNs::AdtId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), 404 TypeNs::AdtId(it) => {
352 TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), 405 Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
353 TypeNs::TypeAliasId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), 406 }
407 TypeNs::BuiltinType(it) => {
408 Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
409 }
410 TypeNs::TypeAliasId(it) => {
411 Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
412 }
354 // FIXME: report error 413 // FIXME: report error
355 TypeNs::EnumVariantId(_) => return (Ty::Unknown, None), 414 TypeNs::EnumVariantId(_) => return (Ty::Unknown, None),
356 }; 415 };
@@ -376,7 +435,13 @@ impl Ty {
376 ), 435 ),
377 Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)), 436 Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
378 }; 437 };
379 Ty::from_partly_resolved_hir_path(ctx, resolution, resolved_segment, remaining_segments) 438 Ty::from_partly_resolved_hir_path(
439 ctx,
440 resolution,
441 resolved_segment,
442 remaining_segments,
443 false,
444 )
380 } 445 }
381 446
382 fn select_associated_type( 447 fn select_associated_type(
@@ -422,13 +487,14 @@ impl Ty {
422 ctx: &TyLoweringContext<'_>, 487 ctx: &TyLoweringContext<'_>,
423 segment: PathSegment<'_>, 488 segment: PathSegment<'_>,
424 typable: TyDefId, 489 typable: TyDefId,
490 infer_args: bool,
425 ) -> Ty { 491 ) -> Ty {
426 let generic_def = match typable { 492 let generic_def = match typable {
427 TyDefId::BuiltinType(_) => None, 493 TyDefId::BuiltinType(_) => None,
428 TyDefId::AdtId(it) => Some(it.into()), 494 TyDefId::AdtId(it) => Some(it.into()),
429 TyDefId::TypeAliasId(it) => Some(it.into()), 495 TyDefId::TypeAliasId(it) => Some(it.into()),
430 }; 496 };
431 let substs = substs_from_path_segment(ctx, segment, generic_def, false); 497 let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args);
432 ctx.db.ty(typable).subst(&substs) 498 ctx.db.ty(typable).subst(&substs)
433 } 499 }
434 500
@@ -441,6 +507,7 @@ impl Ty {
441 // `ValueTyDefId` is just a convenient way to pass generics and 507 // `ValueTyDefId` is just a convenient way to pass generics and
442 // special-case enum variants 508 // special-case enum variants
443 resolved: ValueTyDefId, 509 resolved: ValueTyDefId,
510 infer_args: bool,
444 ) -> Substs { 511 ) -> Substs {
445 let last = path.segments().last().expect("path should have at least one segment"); 512 let last = path.segments().last().expect("path should have at least one segment");
446 let (segment, generic_def) = match resolved { 513 let (segment, generic_def) = match resolved {
@@ -463,22 +530,27 @@ impl Ty {
463 (segment, Some(var.parent.into())) 530 (segment, Some(var.parent.into()))
464 } 531 }
465 }; 532 };
466 substs_from_path_segment(ctx, segment, generic_def, false) 533 substs_from_path_segment(ctx, segment, generic_def, infer_args)
467 } 534 }
468} 535}
469 536
470pub(super) fn substs_from_path_segment( 537fn substs_from_path_segment(
471 ctx: &TyLoweringContext<'_>, 538 ctx: &TyLoweringContext<'_>,
472 segment: PathSegment<'_>, 539 segment: PathSegment<'_>,
473 def_generic: Option<GenericDefId>, 540 def_generic: Option<GenericDefId>,
474 _add_self_param: bool, 541 infer_args: bool,
475) -> Substs { 542) -> Substs {
476 let mut substs = Vec::new(); 543 let mut substs = Vec::new();
477 let def_generics = def_generic.map(|def| generics(ctx.db.upcast(), def)); 544 let def_generics = def_generic.map(|def| generics(ctx.db.upcast(), def));
478 545
479 let (parent_params, self_params, type_params, impl_trait_params) = 546 let (parent_params, self_params, type_params, impl_trait_params) =
480 def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split()); 547 def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split());
548 let total_len = parent_params + self_params + type_params + impl_trait_params;
549
481 substs.extend(iter::repeat(Ty::Unknown).take(parent_params)); 550 substs.extend(iter::repeat(Ty::Unknown).take(parent_params));
551
552 let mut had_explicit_args = false;
553
482 if let Some(generic_args) = &segment.args_and_bindings { 554 if let Some(generic_args) = &segment.args_and_bindings {
483 if !generic_args.has_self_type { 555 if !generic_args.has_self_type {
484 substs.extend(iter::repeat(Ty::Unknown).take(self_params)); 556 substs.extend(iter::repeat(Ty::Unknown).take(self_params));
@@ -490,31 +562,35 @@ pub(super) fn substs_from_path_segment(
490 for arg in generic_args.args.iter().skip(skip).take(expected_num) { 562 for arg in generic_args.args.iter().skip(skip).take(expected_num) {
491 match arg { 563 match arg {
492 GenericArg::Type(type_ref) => { 564 GenericArg::Type(type_ref) => {
565 had_explicit_args = true;
493 let ty = Ty::from_hir(ctx, type_ref); 566 let ty = Ty::from_hir(ctx, type_ref);
494 substs.push(ty); 567 substs.push(ty);
495 } 568 }
496 } 569 }
497 } 570 }
498 } 571 }
499 let total_len = parent_params + self_params + type_params + impl_trait_params;
500 // add placeholders for args that were not provided
501 for _ in substs.len()..total_len {
502 substs.push(Ty::Unknown);
503 }
504 assert_eq!(substs.len(), total_len);
505 572
506 // handle defaults 573 // handle defaults. In expression or pattern path segments without
507 if let Some(def_generic) = def_generic { 574 // explicitly specified type arguments, missing type arguments are inferred
508 let default_substs = ctx.db.generic_defaults(def_generic); 575 // (i.e. defaults aren't used).
509 assert_eq!(substs.len(), default_substs.len()); 576 if !infer_args || had_explicit_args {
577 if let Some(def_generic) = def_generic {
578 let default_substs = ctx.db.generic_defaults(def_generic);
579 assert_eq!(total_len, default_substs.len());
510 580
511 for (i, default_ty) in default_substs.iter().enumerate() { 581 for default_ty in default_substs.iter().skip(substs.len()) {
512 if substs[i] == Ty::Unknown { 582 substs.push(default_ty.clone());
513 substs[i] = default_ty.clone();
514 } 583 }
515 } 584 }
516 } 585 }
517 586
587 // add placeholders for args that were not provided
588 // FIXME: emit diagnostics in contexts where this is not allowed
589 for _ in substs.len()..total_len {
590 substs.push(Ty::Unknown);
591 }
592 assert_eq!(substs.len(), total_len);
593
518 Substs(substs.into()) 594 Substs(substs.into())
519} 595}
520 596
@@ -563,9 +639,7 @@ impl TraitRef {
563 segment: PathSegment<'_>, 639 segment: PathSegment<'_>,
564 resolved: TraitId, 640 resolved: TraitId,
565 ) -> Substs { 641 ) -> Substs {
566 let has_self_param = 642 substs_from_path_segment(ctx, segment, Some(resolved.into()), false)
567 segment.args_and_bindings.as_ref().map(|a| a.has_self_type).unwrap_or(false);
568 substs_from_path_segment(ctx, segment, Some(resolved.into()), !has_self_param)
569 } 643 }
570 644
571 pub(crate) fn from_type_bound( 645 pub(crate) fn from_type_bound(
@@ -663,6 +737,30 @@ fn assoc_type_bindings_from_type_bound<'a>(
663 }) 737 })
664} 738}
665 739
740impl ReturnTypeImplTrait {
741 fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self {
742 mark::hit!(lower_rpit);
743 let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
744 let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| {
745 bounds
746 .iter()
747 .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone()))
748 .collect()
749 });
750 ReturnTypeImplTrait { bounds: Binders::new(1, predicates) }
751 }
752}
753
754fn count_impl_traits(type_ref: &TypeRef) -> usize {
755 let mut count = 0;
756 type_ref.walk(&mut |type_ref| {
757 if matches!(type_ref, TypeRef::ImplTrait(_)) {
758 count += 1;
759 }
760 });
761 count
762}
763
666/// Build the signature of a callable item (function, struct or enum variant). 764/// Build the signature of a callable item (function, struct or enum variant).
667pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig { 765pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig {
668 match def { 766 match def {
@@ -864,7 +962,9 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
864 .with_impl_trait_mode(ImplTraitLoweringMode::Variable) 962 .with_impl_trait_mode(ImplTraitLoweringMode::Variable)
865 .with_type_param_mode(TypeParamLoweringMode::Variable); 963 .with_type_param_mode(TypeParamLoweringMode::Variable);
866 let params = data.params.iter().map(|tr| Ty::from_hir(&ctx_params, tr)).collect::<Vec<_>>(); 964 let params = data.params.iter().map(|tr| Ty::from_hir(&ctx_params, tr)).collect::<Vec<_>>();
867 let ctx_ret = ctx_params.with_impl_trait_mode(ImplTraitLoweringMode::Opaque); 965 let ctx_ret = TyLoweringContext::new(db, &resolver)
966 .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
967 .with_type_param_mode(TypeParamLoweringMode::Variable);
868 let ret = Ty::from_hir(&ctx_ret, &data.ret_type); 968 let ret = Ty::from_hir(&ctx_ret, &data.ret_type);
869 let generics = generics(db.upcast(), def.into()); 969 let generics = generics(db.upcast(), def.into());
870 let num_binders = generics.len(); 970 let num_binders = generics.len();
@@ -1084,3 +1184,25 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<
1084 TraitRef::from_hir(&ctx, target_trait, Some(self_ty.value))?, 1184 TraitRef::from_hir(&ctx, target_trait, Some(self_ty.value))?,
1085 )) 1185 ))
1086} 1186}
1187
1188pub(crate) fn return_type_impl_traits(
1189 db: &impl HirDatabase,
1190 def: hir_def::FunctionId,
1191) -> Option<Arc<Binders<ReturnTypeImplTraits>>> {
1192 // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
1193 let data = db.function_data(def);
1194 let resolver = def.resolver(db.upcast());
1195 let ctx_ret = TyLoweringContext::new(db, &resolver)
1196 .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
1197 .with_type_param_mode(TypeParamLoweringMode::Variable);
1198 let _ret = Ty::from_hir(&ctx_ret, &data.ret_type);
1199 let generics = generics(db.upcast(), def.into());
1200 let num_binders = generics.len();
1201 let return_type_impl_traits =
1202 ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() };
1203 if return_type_impl_traits.impl_traits.is_empty() {
1204 None
1205 } else {
1206 Some(Arc::new(Binders::new(num_binders, return_type_impl_traits)))
1207 }
1208}
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index e19628fdf..e83b39456 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -16,12 +16,8 @@ use rustc_hash::{FxHashMap, FxHashSet};
16 16
17use super::Substs; 17use super::Substs;
18use crate::{ 18use crate::{
19 autoderef, 19 autoderef, db::HirDatabase, primitive::FloatBitness, utils::all_super_traits, ApplicationTy,
20 db::HirDatabase, 20 Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
21 primitive::{FloatBitness, Uncertain},
22 utils::all_super_traits,
23 ApplicationTy, Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty,
24 TypeCtor, TypeWalk,
25}; 21};
26 22
27/// This is used as a key for indexing impls. 23/// This is used as a key for indexing impls.
@@ -147,12 +143,12 @@ impl Ty {
147 } 143 }
148 TypeCtor::Bool => lang_item_crate!("bool"), 144 TypeCtor::Bool => lang_item_crate!("bool"),
149 TypeCtor::Char => lang_item_crate!("char"), 145 TypeCtor::Char => lang_item_crate!("char"),
150 TypeCtor::Float(Uncertain::Known(f)) => match f.bitness { 146 TypeCtor::Float(f) => match f.bitness {
151 // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime) 147 // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime)
152 FloatBitness::X32 => lang_item_crate!("f32", "f32_runtime"), 148 FloatBitness::X32 => lang_item_crate!("f32", "f32_runtime"),
153 FloatBitness::X64 => lang_item_crate!("f64", "f64_runtime"), 149 FloatBitness::X64 => lang_item_crate!("f64", "f64_runtime"),
154 }, 150 },
155 TypeCtor::Int(Uncertain::Known(i)) => lang_item_crate!(i.ty_to_string()), 151 TypeCtor::Int(i) => lang_item_crate!(i.ty_to_string()),
156 TypeCtor::Str => lang_item_crate!("str_alloc", "str"), 152 TypeCtor::Str => lang_item_crate!("str_alloc", "str"),
157 TypeCtor::Slice => lang_item_crate!("slice_alloc", "slice"), 153 TypeCtor::Slice => lang_item_crate!("slice_alloc", "slice"),
158 TypeCtor::RawPtr(Mutability::Shared) => lang_item_crate!("const_ptr"), 154 TypeCtor::RawPtr(Mutability::Shared) => lang_item_crate!("const_ptr"),
diff --git a/crates/ra_hir_ty/src/primitive.rs b/crates/ra_hir_ty/src/primitive.rs
index 02a8179d9..37966b709 100644
--- a/crates/ra_hir_ty/src/primitive.rs
+++ b/crates/ra_hir_ty/src/primitive.rs
@@ -7,42 +7,6 @@ use std::fmt;
7 7
8pub use hir_def::builtin_type::{BuiltinFloat, BuiltinInt, FloatBitness, IntBitness, Signedness}; 8pub use hir_def::builtin_type::{BuiltinFloat, BuiltinInt, FloatBitness, IntBitness, Signedness};
9 9
10#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
11pub enum Uncertain<T> {
12 Unknown,
13 Known(T),
14}
15
16impl From<IntTy> for Uncertain<IntTy> {
17 fn from(ty: IntTy) -> Self {
18 Uncertain::Known(ty)
19 }
20}
21
22impl fmt::Display for Uncertain<IntTy> {
23 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24 match *self {
25 Uncertain::Unknown => write!(f, "{{integer}}"),
26 Uncertain::Known(ty) => write!(f, "{}", ty),
27 }
28 }
29}
30
31impl From<FloatTy> for Uncertain<FloatTy> {
32 fn from(ty: FloatTy) -> Self {
33 Uncertain::Known(ty)
34 }
35}
36
37impl fmt::Display for Uncertain<FloatTy> {
38 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39 match *self {
40 Uncertain::Unknown => write!(f, "{{float}}"),
41 Uncertain::Known(ty) => write!(f, "{}", ty),
42 }
43 }
44}
45
46#[derive(Copy, Clone, Eq, PartialEq, Hash)] 10#[derive(Copy, Clone, Eq, PartialEq, Hash)]
47pub struct IntTy { 11pub struct IntTy {
48 pub signedness: Signedness, 12 pub signedness: Signedness,
@@ -173,21 +137,3 @@ impl From<BuiltinFloat> for FloatTy {
173 FloatTy { bitness: t.bitness } 137 FloatTy { bitness: t.bitness }
174 } 138 }
175} 139}
176
177impl From<Option<BuiltinInt>> for Uncertain<IntTy> {
178 fn from(t: Option<BuiltinInt>) -> Self {
179 match t {
180 None => Uncertain::Unknown,
181 Some(t) => Uncertain::Known(t.into()),
182 }
183 }
184}
185
186impl From<Option<BuiltinFloat>> for Uncertain<FloatTy> {
187 fn from(t: Option<BuiltinFloat>) -> Self {
188 match t {
189 None => Uncertain::Unknown,
190 Some(t) => Uncertain::Known(t.into()),
191 }
192 }
193}
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs
index 8498d3d96..ad04e3e0f 100644
--- a/crates/ra_hir_ty/src/test_db.rs
+++ b/crates/ra_hir_ty/src/test_db.rs
@@ -7,9 +7,8 @@ use std::{
7 7
8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; 8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId};
9use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; 9use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink};
10use ra_db::{ 10use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast};
11 salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, SourceDatabase, Upcast, 11use rustc_hash::FxHashSet;
12};
13use stdx::format_to; 12use stdx::format_to;
14 13
15use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator}; 14use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator};
@@ -72,23 +71,12 @@ impl FileLoader for TestDB {
72 fn file_text(&self, file_id: FileId) -> Arc<String> { 71 fn file_text(&self, file_id: FileId) -> Arc<String> {
73 FileLoaderDelegate(self).file_text(file_id) 72 FileLoaderDelegate(self).file_text(file_id)
74 } 73 }
75 fn resolve_relative_path( 74 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
76 &self, 75 FileLoaderDelegate(self).resolve_path(anchor, path)
77 anchor: FileId,
78 relative_path: &RelativePath,
79 ) -> Option<FileId> {
80 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
81 } 76 }
82 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 77 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
83 FileLoaderDelegate(self).relevant_crates(file_id) 78 FileLoaderDelegate(self).relevant_crates(file_id)
84 } 79 }
85 fn resolve_extern_path(
86 &self,
87 extern_id: ra_db::ExternSourceId,
88 relative_path: &RelativePath,
89 ) -> Option<FileId> {
90 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
91 }
92} 80}
93 81
94impl TestDB { 82impl TestDB {
diff --git a/crates/ra_hir_ty/src/tests/display_source_code.rs b/crates/ra_hir_ty/src/tests/display_source_code.rs
index 4088b1d22..5dfa0a014 100644
--- a/crates/ra_hir_ty/src/tests/display_source_code.rs
+++ b/crates/ra_hir_ty/src/tests/display_source_code.rs
@@ -29,7 +29,7 @@ fn omit_default_type_parameters() {
29 //- /main.rs 29 //- /main.rs
30 struct Foo<T = u8> { t: T } 30 struct Foo<T = u8> { t: T }
31 fn main() { 31 fn main() {
32 let foo = Foo { t: 5 }; 32 let foo = Foo { t: 5u8 };
33 foo<|>; 33 foo<|>;
34 } 34 }
35 ", 35 ",
@@ -41,7 +41,7 @@ fn omit_default_type_parameters() {
41 //- /main.rs 41 //- /main.rs
42 struct Foo<K, T = u8> { k: K, t: T } 42 struct Foo<K, T = u8> { k: K, t: T }
43 fn main() { 43 fn main() {
44 let foo = Foo { k: 400, t: 5 }; 44 let foo = Foo { k: 400, t: 5u8 };
45 foo<|>; 45 foo<|>;
46 } 46 }
47 ", 47 ",
diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs
index 558a70022..804297315 100644
--- a/crates/ra_hir_ty/src/tests/method_resolution.rs
+++ b/crates/ra_hir_ty/src/tests/method_resolution.rs
@@ -184,60 +184,6 @@ fn test() {
184} 184}
185 185
186#[test] 186#[test]
187fn infer_associated_method_generics_with_default_param() {
188 assert_snapshot!(
189 infer(r#"
190struct Gen<T=u32> {
191 val: T
192}
193
194impl<T> Gen<T> {
195 pub fn make() -> Gen<T> {
196 loop { }
197 }
198}
199
200fn test() {
201 let a = Gen::make();
202}
203"#),
204 @r###"
205 80..104 '{ ... }': Gen<T>
206 90..98 'loop { }': !
207 95..98 '{ }': ()
208 118..146 '{ ...e(); }': ()
209 128..129 'a': Gen<u32>
210 132..141 'Gen::make': fn make<u32>() -> Gen<u32>
211 132..143 'Gen::make()': Gen<u32>
212 "###
213 );
214}
215
216#[test]
217fn infer_associated_method_generics_with_default_tuple_param() {
218 let t = type_at(
219 r#"
220//- /main.rs
221struct Gen<T=()> {
222 val: T
223}
224
225impl<T> Gen<T> {
226 pub fn make() -> Gen<T> {
227 loop { }
228 }
229}
230
231fn test() {
232 let a = Gen::make();
233 a.val<|>;
234}
235"#,
236 );
237 assert_eq!(t, "()");
238}
239
240#[test]
241fn infer_associated_method_generics_without_args() { 187fn infer_associated_method_generics_without_args() {
242 assert_snapshot!( 188 assert_snapshot!(
243 infer(r#" 189 infer(r#"
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 88309157b..37659cd02 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -95,7 +95,7 @@ fn foo() {
95fn infer_ranges() { 95fn infer_ranges() {
96 let (db, pos) = TestDB::with_position( 96 let (db, pos) = TestDB::with_position(
97 r#" 97 r#"
98//- /main.rs crate:main deps:std 98//- /main.rs crate:main deps:core
99fn test() { 99fn test() {
100 let a = ..; 100 let a = ..;
101 let b = 1..; 101 let b = 1..;
@@ -108,7 +108,7 @@ fn test() {
108 t<|>; 108 t<|>;
109} 109}
110 110
111//- /std.rs crate:std 111//- /core.rs crate:core
112#[prelude_import] use prelude::*; 112#[prelude_import] use prelude::*;
113mod prelude {} 113mod prelude {}
114 114
@@ -1997,3 +1997,111 @@ fn foo() {
1997 "### 1997 "###
1998 ); 1998 );
1999} 1999}
2000
2001#[test]
2002fn generic_default() {
2003 assert_snapshot!(
2004 infer(r#"
2005struct Thing<T = ()> { t: T }
2006enum OtherThing<T = ()> {
2007 One { t: T },
2008 Two(T),
2009}
2010
2011fn test(t1: Thing, t2: OtherThing, t3: Thing<i32>, t4: OtherThing<i32>) {
2012 t1.t;
2013 t3.t;
2014 match t2 {
2015 OtherThing::One { t } => { t; },
2016 OtherThing::Two(t) => { t; },
2017 }
2018 match t4 {
2019 OtherThing::One { t } => { t; },
2020 OtherThing::Two(t) => { t; },
2021 }
2022}
2023"#),
2024 @r###"
2025 98..100 't1': Thing<()>
2026 109..111 't2': OtherThing<()>
2027 125..127 't3': Thing<i32>
2028 141..143 't4': OtherThing<i32>
2029 162..385 '{ ... } }': ()
2030 168..170 't1': Thing<()>
2031 168..172 't1.t': ()
2032 178..180 't3': Thing<i32>
2033 178..182 't3.t': i32
2034 188..283 'match ... }': ()
2035 194..196 't2': OtherThing<()>
2036 207..228 'OtherT... { t }': OtherThing<()>
2037 225..226 't': ()
2038 232..238 '{ t; }': ()
2039 234..235 't': ()
2040 248..266 'OtherT...Two(t)': OtherThing<()>
2041 264..265 't': ()
2042 270..276 '{ t; }': ()
2043 272..273 't': ()
2044 288..383 'match ... }': ()
2045 294..296 't4': OtherThing<i32>
2046 307..328 'OtherT... { t }': OtherThing<i32>
2047 325..326 't': i32
2048 332..338 '{ t; }': ()
2049 334..335 't': i32
2050 348..366 'OtherT...Two(t)': OtherThing<i32>
2051 364..365 't': i32
2052 370..376 '{ t; }': ()
2053 372..373 't': i32
2054 "###
2055 );
2056}
2057
2058#[test]
2059fn generic_default_in_struct_literal() {
2060 assert_snapshot!(
2061 infer(r#"
2062struct Thing<T = ()> { t: T }
2063enum OtherThing<T = ()> {
2064 One { t: T },
2065 Two(T),
2066}
2067
2068fn test() {
2069 let x = Thing { t: loop {} };
2070 let y = Thing { t: () };
2071 let z = Thing { t: 1i32 };
2072 if let Thing { t } = z {
2073 t;
2074 }
2075
2076 let a = OtherThing::One { t: 1i32 };
2077 let b = OtherThing::Two(1i32);
2078}
2079"#),
2080 @r###"
2081 100..320 '{ ...32); }': ()
2082 110..111 'x': Thing<!>
2083 114..134 'Thing ...p {} }': Thing<!>
2084 125..132 'loop {}': !
2085 130..132 '{}': ()
2086 144..145 'y': Thing<()>
2087 148..163 'Thing { t: () }': Thing<()>
2088 159..161 '()': ()
2089 173..174 'z': Thing<i32>
2090 177..194 'Thing ...1i32 }': Thing<i32>
2091 188..192 '1i32': i32
2092 200..241 'if let... }': ()
2093 207..218 'Thing { t }': Thing<i32>
2094 215..216 't': i32
2095 221..222 'z': Thing<i32>
2096 223..241 '{ ... }': ()
2097 233..234 't': i32
2098 251..252 'a': OtherThing<i32>
2099 255..282 'OtherT...1i32 }': OtherThing<i32>
2100 276..280 '1i32': i32
2101 292..293 'b': OtherThing<i32>
2102 296..311 'OtherThing::Two': Two<i32>(i32) -> OtherThing<i32>
2103 296..317 'OtherT...(1i32)': OtherThing<i32>
2104 312..316 '1i32': i32
2105 "###
2106 );
2107}
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index e8778d419..e81193a3c 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -10,7 +10,7 @@ use super::{infer, infer_with_mismatches, type_at, type_at_pos};
10fn infer_await() { 10fn infer_await() {
11 let (db, pos) = TestDB::with_position( 11 let (db, pos) = TestDB::with_position(
12 r#" 12 r#"
13//- /main.rs crate:main deps:std 13//- /main.rs crate:main deps:core
14 14
15struct IntFuture; 15struct IntFuture;
16 16
@@ -24,7 +24,7 @@ fn test() {
24 v<|>; 24 v<|>;
25} 25}
26 26
27//- /std.rs crate:std 27//- /core.rs crate:core
28#[prelude_import] use future::*; 28#[prelude_import] use future::*;
29mod future { 29mod future {
30 #[lang = "future_trait"] 30 #[lang = "future_trait"]
@@ -42,7 +42,7 @@ mod future {
42fn infer_async() { 42fn infer_async() {
43 let (db, pos) = TestDB::with_position( 43 let (db, pos) = TestDB::with_position(
44 r#" 44 r#"
45//- /main.rs crate:main deps:std 45//- /main.rs crate:main deps:core
46 46
47async fn foo() -> u64 { 47async fn foo() -> u64 {
48 128 48 128
@@ -54,7 +54,7 @@ fn test() {
54 v<|>; 54 v<|>;
55} 55}
56 56
57//- /std.rs crate:std 57//- /core.rs crate:core
58#[prelude_import] use future::*; 58#[prelude_import] use future::*;
59mod future { 59mod future {
60 #[lang = "future_trait"] 60 #[lang = "future_trait"]
@@ -72,7 +72,7 @@ mod future {
72fn infer_desugar_async() { 72fn infer_desugar_async() {
73 let (db, pos) = TestDB::with_position( 73 let (db, pos) = TestDB::with_position(
74 r#" 74 r#"
75//- /main.rs crate:main deps:std 75//- /main.rs crate:main deps:core
76 76
77async fn foo() -> u64 { 77async fn foo() -> u64 {
78 128 78 128
@@ -83,7 +83,7 @@ fn test() {
83 r<|>; 83 r<|>;
84} 84}
85 85
86//- /std.rs crate:std 86//- /core.rs crate:core
87#[prelude_import] use future::*; 87#[prelude_import] use future::*;
88mod future { 88mod future {
89 trait Future { 89 trait Future {
@@ -100,7 +100,7 @@ mod future {
100fn infer_try() { 100fn infer_try() {
101 let (db, pos) = TestDB::with_position( 101 let (db, pos) = TestDB::with_position(
102 r#" 102 r#"
103//- /main.rs crate:main deps:std 103//- /main.rs crate:main deps:core
104 104
105fn test() { 105fn test() {
106 let r: Result<i32, u64> = Result::Ok(1); 106 let r: Result<i32, u64> = Result::Ok(1);
@@ -108,7 +108,7 @@ fn test() {
108 v<|>; 108 v<|>;
109} 109}
110 110
111//- /std.rs crate:std 111//- /core.rs crate:core
112 112
113#[prelude_import] use ops::*; 113#[prelude_import] use ops::*;
114mod ops { 114mod ops {
@@ -140,9 +140,9 @@ mod result {
140fn infer_for_loop() { 140fn infer_for_loop() {
141 let (db, pos) = TestDB::with_position( 141 let (db, pos) = TestDB::with_position(
142 r#" 142 r#"
143//- /main.rs crate:main deps:std 143//- /main.rs crate:main deps:core,alloc
144 144
145use std::collections::Vec; 145use alloc::collections::Vec;
146 146
147fn test() { 147fn test() {
148 let v = Vec::new(); 148 let v = Vec::new();
@@ -152,7 +152,7 @@ fn test() {
152 } 152 }
153} 153}
154 154
155//- /std.rs crate:std 155//- /core.rs crate:core
156 156
157#[prelude_import] use iter::*; 157#[prelude_import] use iter::*;
158mod iter { 158mod iter {
@@ -161,6 +161,8 @@ mod iter {
161 } 161 }
162} 162}
163 163
164//- /alloc.rs crate:alloc deps:core
165
164mod collections { 166mod collections {
165 struct Vec<T> {} 167 struct Vec<T> {}
166 impl<T> Vec<T> { 168 impl<T> Vec<T> {
@@ -168,7 +170,7 @@ mod collections {
168 fn push(&mut self, t: T) { } 170 fn push(&mut self, t: T) { }
169 } 171 }
170 172
171 impl<T> crate::iter::IntoIterator for Vec<T> { 173 impl<T> IntoIterator for Vec<T> {
172 type Item=T; 174 type Item=T;
173 } 175 }
174} 176}
@@ -1110,7 +1112,6 @@ fn test() {
1110} 1112}
1111 1113
1112#[test] 1114#[test]
1113#[ignore]
1114fn impl_trait() { 1115fn impl_trait() {
1115 assert_snapshot!( 1116 assert_snapshot!(
1116 infer(r#" 1117 infer(r#"
@@ -1161,6 +1162,95 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u64>) {
1161} 1162}
1162 1163
1163#[test] 1164#[test]
1165fn simple_return_pos_impl_trait() {
1166 mark::check!(lower_rpit);
1167 assert_snapshot!(
1168 infer(r#"
1169trait Trait<T> {
1170 fn foo(&self) -> T;
1171}
1172fn bar() -> impl Trait<u64> { loop {} }
1173
1174fn test() {
1175 let a = bar();
1176 a.foo();
1177}
1178"#),
1179 @r###"
1180 30..34 'self': &Self
1181 72..83 '{ loop {} }': !
1182 74..81 'loop {}': !
1183 79..81 '{}': ()
1184 95..130 '{ ...o(); }': ()
1185 105..106 'a': impl Trait<u64>
1186 109..112 'bar': fn bar() -> impl Trait<u64>
1187 109..114 'bar()': impl Trait<u64>
1188 120..121 'a': impl Trait<u64>
1189 120..127 'a.foo()': u64
1190 "###
1191 );
1192}
1193
1194#[test]
1195fn more_return_pos_impl_trait() {
1196 assert_snapshot!(
1197 infer(r#"
1198trait Iterator {
1199 type Item;
1200 fn next(&mut self) -> Self::Item;
1201}
1202trait Trait<T> {
1203 fn foo(&self) -> T;
1204}
1205fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>) { loop {} }
1206fn baz<T>(t: T) -> (impl Iterator<Item = impl Trait<T>>, impl Trait<T>) { loop {} }
1207
1208fn test() {
1209 let (a, b) = bar();
1210 a.next().foo();
1211 b.foo();
1212 let (c, d) = baz(1u128);
1213 c.next().foo();
1214 d.foo();
1215}
1216"#),
1217 @r###"
1218 50..54 'self': &mut Self
1219 102..106 'self': &Self
1220 185..196 '{ loop {} }': ({unknown}, {unknown})
1221 187..194 'loop {}': !
1222 192..194 '{}': ()
1223 207..208 't': T
1224 269..280 '{ loop {} }': ({unknown}, {unknown})
1225 271..278 'loop {}': !
1226 276..278 '{}': ()
1227 292..414 '{ ...o(); }': ()
1228 302..308 '(a, b)': (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
1229 303..304 'a': impl Iterator<Item = impl Trait<u32>>
1230 306..307 'b': impl Trait<u64>
1231 311..314 'bar': fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
1232 311..316 'bar()': (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
1233 322..323 'a': impl Iterator<Item = impl Trait<u32>>
1234 322..330 'a.next()': impl Trait<u32>
1235 322..336 'a.next().foo()': u32
1236 342..343 'b': impl Trait<u64>
1237 342..349 'b.foo()': u64
1238 359..365 '(c, d)': (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
1239 360..361 'c': impl Iterator<Item = impl Trait<u128>>
1240 363..364 'd': impl Trait<u128>
1241 368..371 'baz': fn baz<u128>(u128) -> (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
1242 368..378 'baz(1u128)': (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
1243 372..377 '1u128': u128
1244 384..385 'c': impl Iterator<Item = impl Trait<u128>>
1245 384..392 'c.next()': impl Trait<u128>
1246 384..398 'c.next().foo()': u128
1247 404..405 'd': impl Trait<u128>
1248 404..411 'd.foo()': u128
1249 "###
1250 );
1251}
1252
1253#[test]
1164fn dyn_trait() { 1254fn dyn_trait() {
1165 assert_snapshot!( 1255 assert_snapshot!(
1166 infer(r#" 1256 infer(r#"
@@ -1718,33 +1808,33 @@ fn test() {
1718} 1808}
1719"#), 1809"#),
1720 @r###" 1810 @r###"
172165..69 'self': &Self 1811 65..69 'self': &Self
1722166..170 'self': Self 1812 166..170 'self': Self
1723172..176 'args': Args 1813 172..176 'args': Args
1724240..244 'self': &Foo 1814 240..244 'self': &Foo
1725255..257 '{}': () 1815 255..257 '{}': ()
1726335..336 'f': F 1816 335..336 'f': F
1727355..357 '{}': () 1817 355..357 '{}': ()
1728444..690 '{ ...o(); }': () 1818 444..690 '{ ...o(); }': ()
1729454..459 'lazy1': Lazy<Foo, fn() -> T> 1819 454..459 'lazy1': Lazy<Foo, || -> Foo>
1730476..485 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T> 1820 476..485 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> Foo>
1731476..493 'Lazy::...| Foo)': Lazy<Foo, fn() -> T> 1821 476..493 'Lazy::...| Foo)': Lazy<Foo, || -> Foo>
1732486..492 '|| Foo': || -> T 1822 486..492 '|| Foo': || -> Foo
1733489..492 'Foo': Foo 1823 489..492 'Foo': Foo
1734503..505 'r1': {unknown} 1824 503..505 'r1': usize
1735508..513 'lazy1': Lazy<Foo, fn() -> T> 1825 508..513 'lazy1': Lazy<Foo, || -> Foo>
1736508..519 'lazy1.foo()': {unknown} 1826 508..519 'lazy1.foo()': usize
1737561..576 'make_foo_fn_ptr': fn() -> Foo 1827 561..576 'make_foo_fn_ptr': fn() -> Foo
1738592..603 'make_foo_fn': fn make_foo_fn() -> Foo 1828 592..603 'make_foo_fn': fn make_foo_fn() -> Foo
1739613..618 'lazy2': Lazy<Foo, fn() -> T> 1829 613..618 'lazy2': Lazy<Foo, fn() -> Foo>
1740635..644 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T> 1830 635..644 'Lazy::new': fn new<Foo, fn() -> Foo>(fn() -> Foo) -> Lazy<Foo, fn() -> Foo>
1741635..661 'Lazy::...n_ptr)': Lazy<Foo, fn() -> T> 1831 635..661 'Lazy::...n_ptr)': Lazy<Foo, fn() -> Foo>
1742645..660 'make_foo_fn_ptr': fn() -> Foo 1832 645..660 'make_foo_fn_ptr': fn() -> Foo
1743671..673 'r2': {unknown} 1833 671..673 'r2': {unknown}
1744676..681 'lazy2': Lazy<Foo, fn() -> T> 1834 676..681 'lazy2': Lazy<Foo, fn() -> Foo>
1745676..687 'lazy2.foo()': {unknown} 1835 676..687 'lazy2.foo()': {unknown}
1746550..552 '{}': () 1836 550..552 '{}': ()
1747"### 1837 "###
1748 ); 1838 );
1749} 1839}
1750 1840
@@ -2758,12 +2848,12 @@ fn test() {
2758fn integer_range_iterate() { 2848fn integer_range_iterate() {
2759 let t = type_at( 2849 let t = type_at(
2760 r#" 2850 r#"
2761//- /main.rs crate:main deps:std 2851//- /main.rs crate:main deps:core
2762fn test() { 2852fn test() {
2763 for x in 0..100 { x<|>; } 2853 for x in 0..100 { x<|>; }
2764} 2854}
2765 2855
2766//- /std.rs crate:std 2856//- /core.rs crate:core
2767pub mod ops { 2857pub mod ops {
2768 pub struct Range<Idx> { 2858 pub struct Range<Idx> {
2769 pub start: Idx, 2859 pub start: Idx,
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index 61de3cc30..a72a82f5a 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -4,7 +4,7 @@ use std::sync::Arc;
4use log::debug; 4use log::debug;
5 5
6use chalk_ir::{fold::shift::Shift, GenericArg, TypeName}; 6use chalk_ir::{fold::shift::Shift, GenericArg, TypeName};
7use chalk_solve::rust_ir::{self, WellKnownTrait}; 7use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
8 8
9use hir_def::{ 9use hir_def::{
10 lang_item::{lang_attr, LangItemTarget}, 10 lang_item::{lang_attr, LangItemTarget},
@@ -100,6 +100,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
100 fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> { 100 fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> {
101 self.db.associated_ty_value(self.krate, id) 101 self.db.associated_ty_value(self.krate, id)
102 } 102 }
103
103 fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<Interner>> { 104 fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<Interner>> {
104 vec![] 105 vec![]
105 } 106 }
@@ -130,11 +131,34 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
130 self.db.program_clauses_for_chalk_env(self.krate, environment.clone()) 131 self.db.program_clauses_for_chalk_env(self.krate, environment.clone())
131 } 132 }
132 133
133 fn opaque_ty_data( 134 fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId<Interner>) -> Arc<OpaqueTyDatum> {
134 &self, 135 let interned_id = crate::db::InternedOpaqueTyId::from(id);
135 _id: chalk_ir::OpaqueTyId<Interner>, 136 let full_id = self.db.lookup_intern_impl_trait_id(interned_id);
136 ) -> Arc<rust_ir::OpaqueTyDatum<Interner>> { 137 let (func, idx) = match full_id {
137 unimplemented!() 138 crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => (func, idx),
139 };
140 let datas =
141 self.db.return_type_impl_traits(func).expect("impl trait id without impl traits");
142 let data = &datas.value.impl_traits[idx as usize];
143 let bound = OpaqueTyDatumBound {
144 bounds: make_binders(
145 data.bounds
146 .value
147 .iter()
148 .cloned()
149 .filter(|b| !b.is_error())
150 .map(|b| b.to_chalk(self.db))
151 .collect(),
152 1,
153 ),
154 };
155 let num_vars = datas.num_binders;
156 Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound: make_binders(bound, num_vars) })
157 }
158
159 fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> {
160 // FIXME: actually provide the hidden type; it is relevant for auto traits
161 Ty::Unknown.to_chalk(self.db)
138 } 162 }
139 163
140 fn force_impl_for( 164 fn force_impl_for(
@@ -150,10 +174,6 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
150 // FIXME: implement actual object safety 174 // FIXME: implement actual object safety
151 true 175 true
152 } 176 }
153
154 fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> {
155 Ty::Unknown.to_chalk(self.db)
156 }
157} 177}
158 178
159pub(crate) fn program_clauses_for_chalk_env_query( 179pub(crate) fn program_clauses_for_chalk_env_query(
@@ -460,6 +480,18 @@ impl From<crate::traits::GlobalImplId> for ImplId {
460 } 480 }
461} 481}
462 482
483impl From<OpaqueTyId> for crate::db::InternedOpaqueTyId {
484 fn from(id: OpaqueTyId) -> Self {
485 InternKey::from_intern_id(id.0)
486 }
487}
488
489impl From<crate::db::InternedOpaqueTyId> for OpaqueTyId {
490 fn from(id: crate::db::InternedOpaqueTyId) -> Self {
491 chalk_ir::OpaqueTyId(id.as_intern_id())
492 }
493}
494
463impl From<rust_ir::AssociatedTyValueId<Interner>> for crate::traits::AssocTyValueId { 495impl From<rust_ir::AssociatedTyValueId<Interner>> for crate::traits::AssocTyValueId {
464 fn from(id: rust_ir::AssociatedTyValueId<Interner>) -> Self { 496 fn from(id: rust_ir::AssociatedTyValueId<Interner>) -> Self {
465 Self::from_intern_id(id.0) 497 Self::from_intern_id(id.0)
diff --git a/crates/ra_hir_ty/src/traits/chalk/interner.rs b/crates/ra_hir_ty/src/traits/chalk/interner.rs
index e27074ba6..56aab640c 100644
--- a/crates/ra_hir_ty/src/traits/chalk/interner.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/interner.rs
@@ -22,6 +22,8 @@ pub type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId<Interne
22pub type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>; 22pub type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>;
23pub type FnDefId = chalk_ir::FnDefId<Interner>; 23pub type FnDefId = chalk_ir::FnDefId<Interner>;
24pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>; 24pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>;
25pub type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>;
26pub type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>;
25 27
26impl chalk_ir::interner::Interner for Interner { 28impl chalk_ir::interner::Interner for Interner {
27 type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc? 29 type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc?
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
index 5f6daf842..18e5c9c16 100644
--- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
@@ -14,10 +14,10 @@ use ra_db::salsa::InternKey;
14 14
15use crate::{ 15use crate::{
16 db::HirDatabase, 16 db::HirDatabase,
17 primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain}, 17 primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness},
18 traits::{builtin, AssocTyValue, Canonical, Impl, Obligation}, 18 traits::{builtin, AssocTyValue, Canonical, Impl, Obligation},
19 ApplicationTy, CallableDef, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy, 19 ApplicationTy, CallableDef, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId,
20 Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, 20 ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
21}; 21};
22 22
23use super::interner::*; 23use super::interner::*;
@@ -68,7 +68,16 @@ impl ToChalk for Ty {
68 let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) }; 68 let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) };
69 chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner) 69 chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner)
70 } 70 }
71 Ty::Opaque(_) | Ty::Unknown => { 71 Ty::Opaque(opaque_ty) => {
72 let opaque_ty_id = opaque_ty.opaque_ty_id.to_chalk(db);
73 let substitution = opaque_ty.parameters.to_chalk(db);
74 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy {
75 opaque_ty_id,
76 substitution,
77 }))
78 .intern(&Interner)
79 }
80 Ty::Unknown => {
72 let substitution = chalk_ir::Substitution::empty(&Interner); 81 let substitution = chalk_ir::Substitution::empty(&Interner);
73 let name = TypeName::Error; 82 let name = TypeName::Error;
74 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner) 83 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
@@ -98,7 +107,11 @@ impl ToChalk for Ty {
98 let parameters = from_chalk(db, proj.substitution); 107 let parameters = from_chalk(db, proj.substitution);
99 Ty::Projection(ProjectionTy { associated_ty, parameters }) 108 Ty::Projection(ProjectionTy { associated_ty, parameters })
100 } 109 }
101 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(), 110 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(opaque_ty)) => {
111 let impl_trait_id = from_chalk(db, opaque_ty.opaque_ty_id);
112 let parameters = from_chalk(db, opaque_ty.substitution);
113 Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters })
114 }
102 chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => { 115 chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => {
103 let parameters: Substs = from_chalk(db, substitution); 116 let parameters: Substs = from_chalk(db, substitution);
104 Ty::Apply(ApplicationTy { 117 Ty::Apply(ApplicationTy {
@@ -204,6 +217,21 @@ impl ToChalk for hir_def::TraitId {
204 } 217 }
205} 218}
206 219
220impl ToChalk for OpaqueTyId {
221 type Chalk = chalk_ir::OpaqueTyId<Interner>;
222
223 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::OpaqueTyId<Interner> {
224 db.intern_impl_trait_id(self).into()
225 }
226
227 fn from_chalk(
228 db: &dyn HirDatabase,
229 opaque_ty_id: chalk_ir::OpaqueTyId<Interner>,
230 ) -> OpaqueTyId {
231 db.lookup_intern_impl_trait_id(opaque_ty_id.into())
232 }
233}
234
207impl ToChalk for TypeCtor { 235impl ToChalk for TypeCtor {
208 type Chalk = TypeName<Interner>; 236 type Chalk = TypeName<Interner>;
209 237
@@ -214,13 +242,18 @@ impl ToChalk for TypeCtor {
214 TypeName::AssociatedType(type_id) 242 TypeName::AssociatedType(type_id)
215 } 243 }
216 244
245 TypeCtor::OpaqueType(impl_trait_id) => {
246 let id = impl_trait_id.to_chalk(db);
247 TypeName::OpaqueType(id)
248 }
249
217 TypeCtor::Bool => TypeName::Scalar(Scalar::Bool), 250 TypeCtor::Bool => TypeName::Scalar(Scalar::Bool),
218 TypeCtor::Char => TypeName::Scalar(Scalar::Char), 251 TypeCtor::Char => TypeName::Scalar(Scalar::Char),
219 TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)), 252 TypeCtor::Int(int_ty) => TypeName::Scalar(int_ty_to_chalk(int_ty)),
220 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 })) => { 253 TypeCtor::Float(FloatTy { bitness: FloatBitness::X32 }) => {
221 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) 254 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32))
222 } 255 }
223 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 })) => { 256 TypeCtor::Float(FloatTy { bitness: FloatBitness::X64 }) => {
224 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) 257 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64))
225 } 258 }
226 259
@@ -235,9 +268,7 @@ impl ToChalk for TypeCtor {
235 } 268 }
236 TypeCtor::Never => TypeName::Never, 269 TypeCtor::Never => TypeName::Never,
237 270
238 TypeCtor::Int(Uncertain::Unknown) 271 TypeCtor::Adt(_)
239 | TypeCtor::Float(Uncertain::Unknown)
240 | TypeCtor::Adt(_)
241 | TypeCtor::Array 272 | TypeCtor::Array
242 | TypeCtor::FnPtr { .. } 273 | TypeCtor::FnPtr { .. }
243 | TypeCtor::Closure { .. } => { 274 | TypeCtor::Closure { .. } => {
@@ -252,23 +283,25 @@ impl ToChalk for TypeCtor {
252 match type_name { 283 match type_name {
253 TypeName::Adt(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), 284 TypeName::Adt(struct_id) => db.lookup_intern_type_ctor(struct_id.into()),
254 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), 285 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
255 TypeName::OpaqueType(_) => unreachable!(), 286 TypeName::OpaqueType(opaque_type_id) => {
287 TypeCtor::OpaqueType(from_chalk(db, opaque_type_id))
288 }
256 289
257 TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool, 290 TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool,
258 TypeName::Scalar(Scalar::Char) => TypeCtor::Char, 291 TypeName::Scalar(Scalar::Char) => TypeCtor::Char,
259 TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(Uncertain::Known(IntTy { 292 TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(IntTy {
260 signedness: Signedness::Signed, 293 signedness: Signedness::Signed,
261 bitness: bitness_from_chalk_int(int_ty), 294 bitness: bitness_from_chalk_int(int_ty),
262 })), 295 }),
263 TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(Uncertain::Known(IntTy { 296 TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(IntTy {
264 signedness: Signedness::Unsigned, 297 signedness: Signedness::Unsigned,
265 bitness: bitness_from_chalk_uint(uint_ty), 298 bitness: bitness_from_chalk_uint(uint_ty),
266 })), 299 }),
267 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) => { 300 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) => {
268 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 })) 301 TypeCtor::Float(FloatTy { bitness: FloatBitness::X32 })
269 } 302 }
270 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) => { 303 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) => {
271 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 })) 304 TypeCtor::Float(FloatTy { bitness: FloatBitness::X64 })
272 } 305 }
273 TypeName::Tuple(cardinality) => TypeCtor::Tuple { cardinality: cardinality as u16 }, 306 TypeName::Tuple(cardinality) => TypeCtor::Tuple { cardinality: cardinality as u16 },
274 TypeName::Raw(mutability) => TypeCtor::RawPtr(from_chalk(db, mutability)), 307 TypeName::Raw(mutability) => TypeCtor::RawPtr(from_chalk(db, mutability)),
@@ -447,6 +480,11 @@ impl ToChalk for GenericPredicate {
447 let ty = from_chalk(db, projection_eq.ty); 480 let ty = from_chalk(db, projection_eq.ty);
448 GenericPredicate::Projection(ProjectionPredicate { projection_ty, ty }) 481 GenericPredicate::Projection(ProjectionPredicate { projection_ty, ty })
449 } 482 }
483
484 chalk_ir::WhereClause::LifetimeOutlives(_) => {
485 // we shouldn't get these from Chalk
486 panic!("encountered LifetimeOutlives from Chalk")
487 }
450 } 488 }
451 } 489 }
452} 490}
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs
index d88828c7c..556af7098 100644
--- a/crates/ra_hir_ty/src/traits/chalk/tls.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs
@@ -69,6 +69,11 @@ impl DebugContext<'_> {
69 let name = self.0.type_alias_data(type_alias).name.clone(); 69 let name = self.0.type_alias_data(type_alias).name.clone();
70 write!(f, "{}::{}", trait_name, name)?; 70 write!(f, "{}::{}", trait_name, name)?;
71 } 71 }
72 TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id {
73 crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
74 write!(f, "{{impl trait {} of {:?}}}", idx, func)?;
75 }
76 },
72 TypeCtor::Closure { def, expr } => { 77 TypeCtor::Closure { def, expr } => {
73 write!(f, "{{closure {:?} in ", expr.into_raw())?; 78 write!(f, "{{closure {:?} in ", expr.into_raw())?;
74 match def { 79 match def {
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index fa37b6955..e1fcf379d 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -126,3 +126,81 @@ pub(crate) fn completions(
126 126
127 Some(acc) 127 Some(acc)
128} 128}
129
130#[cfg(test)]
131mod tests {
132 use crate::completion::completion_config::CompletionConfig;
133 use crate::mock_analysis::analysis_and_position;
134
135 struct DetailAndDocumentation<'a> {
136 detail: &'a str,
137 documentation: &'a str,
138 }
139
140 fn check_detail_and_documentation(fixture: &str, expected: DetailAndDocumentation) {
141 let (analysis, position) = analysis_and_position(fixture);
142 let config = CompletionConfig::default();
143 let completions = analysis.completions(&config, position).unwrap().unwrap();
144 for item in completions {
145 if item.detail() == Some(expected.detail) {
146 let opt = item.documentation();
147 let doc = opt.as_ref().map(|it| it.as_str());
148 assert_eq!(doc, Some(expected.documentation));
149 return;
150 }
151 }
152 panic!("completion detail not found: {}", expected.detail)
153 }
154
155 #[test]
156 fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
157 check_detail_and_documentation(
158 r#"
159 //- /lib.rs
160 macro_rules! bar {
161 () => {
162 struct Bar;
163 impl Bar {
164 #[doc = "Do the foo"]
165 fn foo(&self) {}
166 }
167 }
168 }
169
170 bar!();
171
172 fn foo() {
173 let bar = Bar;
174 bar.fo<|>;
175 }
176 "#,
177 DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
178 );
179 }
180
181 #[test]
182 fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
183 check_detail_and_documentation(
184 r#"
185 //- /lib.rs
186 macro_rules! bar {
187 () => {
188 struct Bar;
189 impl Bar {
190 /// Do the foo
191 fn foo(&self) {}
192 }
193 }
194 }
195
196 bar!();
197
198 fn foo() {
199 let bar = Bar;
200 bar.fo<|>;
201 }
202 "#,
203 DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
204 );
205 }
206}
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 15dc50cf1..e1bfd72f9 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -21,7 +21,7 @@ use ra_syntax::{
21}; 21};
22use ra_text_edit::{TextEdit, TextEditBuilder}; 22use ra_text_edit::{TextEdit, TextEditBuilder};
23 23
24use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceChange, SourceFileEdit}; 24use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceFileEdit};
25 25
26#[derive(Debug, Copy, Clone)] 26#[derive(Debug, Copy, Clone)]
27pub enum Severity { 27pub enum Severity {
@@ -115,7 +115,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
115 let node = d.ast(db); 115 let node = d.ast(db);
116 let replacement = format!("Ok({})", node.syntax()); 116 let replacement = format!("Ok({})", node.syntax());
117 let edit = TextEdit::replace(node.syntax().text_range(), replacement); 117 let edit = TextEdit::replace(node.syntax().text_range(), replacement);
118 let source_change = SourceChange::source_file_edit_from(file_id, edit); 118 let source_change = SourceFileEdit { file_id, edit }.into();
119 let fix = Fix::new("Wrap with ok", source_change); 119 let fix = Fix::new("Wrap with ok", source_change);
120 res.borrow_mut().push(Diagnostic { 120 res.borrow_mut().push(Diagnostic {
121 range: sema.diagnostics_range(d).range, 121 range: sema.diagnostics_range(d).range,
@@ -187,7 +187,8 @@ fn check_struct_shorthand_initialization(
187 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) { 187 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) {
188 let field_name = name_ref.syntax().text().to_string(); 188 let field_name = name_ref.syntax().text().to_string();
189 let field_expr = expr.syntax().text().to_string(); 189 let field_expr = expr.syntax().text().to_string();
190 if field_name == field_expr { 190 let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
191 if field_name == field_expr && !field_name_is_tup_index {
191 let mut edit_builder = TextEditBuilder::default(); 192 let mut edit_builder = TextEditBuilder::default();
192 edit_builder.delete(record_field.syntax().text_range()); 193 edit_builder.delete(record_field.syntax().text_range());
193 edit_builder.insert(record_field.syntax().text_range().start(), field_name); 194 edit_builder.insert(record_field.syntax().text_range().start(), field_name);
@@ -321,29 +322,26 @@ mod tests {
321 fn test_wrap_return_type() { 322 fn test_wrap_return_type() {
322 let before = r#" 323 let before = r#"
323 //- /main.rs 324 //- /main.rs
324 use std::{string::String, result::Result::{self, Ok, Err}}; 325 use core::result::Result::{self, Ok, Err};
325 326
326 fn div(x: i32, y: i32) -> Result<i32, String> { 327 fn div(x: i32, y: i32) -> Result<i32, ()> {
327 if y == 0 { 328 if y == 0 {
328 return Err("div by zero".into()); 329 return Err(());
329 } 330 }
330 x / y<|> 331 x / y<|>
331 } 332 }
332 333
333 //- /std/lib.rs 334 //- /core/lib.rs
334 pub mod string {
335 pub struct String { }
336 }
337 pub mod result { 335 pub mod result {
338 pub enum Result<T, E> { Ok(T), Err(E) } 336 pub enum Result<T, E> { Ok(T), Err(E) }
339 } 337 }
340 "#; 338 "#;
341 let after = r#" 339 let after = r#"
342 use std::{string::String, result::Result::{self, Ok, Err}}; 340 use core::result::Result::{self, Ok, Err};
343 341
344 fn div(x: i32, y: i32) -> Result<i32, String> { 342 fn div(x: i32, y: i32) -> Result<i32, ()> {
345 if y == 0 { 343 if y == 0 {
346 return Err("div by zero".into()); 344 return Err(());
347 } 345 }
348 Ok(x / y) 346 Ok(x / y)
349 } 347 }
@@ -355,7 +353,7 @@ mod tests {
355 fn test_wrap_return_type_handles_generic_functions() { 353 fn test_wrap_return_type_handles_generic_functions() {
356 let before = r#" 354 let before = r#"
357 //- /main.rs 355 //- /main.rs
358 use std::result::Result::{self, Ok, Err}; 356 use core::result::Result::{self, Ok, Err};
359 357
360 fn div<T>(x: T) -> Result<T, i32> { 358 fn div<T>(x: T) -> Result<T, i32> {
361 if x == 0 { 359 if x == 0 {
@@ -364,13 +362,13 @@ mod tests {
364 <|>x 362 <|>x
365 } 363 }
366 364
367 //- /std/lib.rs 365 //- /core/lib.rs
368 pub mod result { 366 pub mod result {
369 pub enum Result<T, E> { Ok(T), Err(E) } 367 pub enum Result<T, E> { Ok(T), Err(E) }
370 } 368 }
371 "#; 369 "#;
372 let after = r#" 370 let after = r#"
373 use std::result::Result::{self, Ok, Err}; 371 use core::result::Result::{self, Ok, Err};
374 372
375 fn div<T>(x: T) -> Result<T, i32> { 373 fn div<T>(x: T) -> Result<T, i32> {
376 if x == 0 { 374 if x == 0 {
@@ -386,32 +384,29 @@ mod tests {
386 fn test_wrap_return_type_handles_type_aliases() { 384 fn test_wrap_return_type_handles_type_aliases() {
387 let before = r#" 385 let before = r#"
388 //- /main.rs 386 //- /main.rs
389 use std::{string::String, result::Result::{self, Ok, Err}}; 387 use core::result::Result::{self, Ok, Err};
390 388
391 type MyResult<T> = Result<T, String>; 389 type MyResult<T> = Result<T, ()>;
392 390
393 fn div(x: i32, y: i32) -> MyResult<i32> { 391 fn div(x: i32, y: i32) -> MyResult<i32> {
394 if y == 0 { 392 if y == 0 {
395 return Err("div by zero".into()); 393 return Err(());
396 } 394 }
397 x <|>/ y 395 x <|>/ y
398 } 396 }
399 397
400 //- /std/lib.rs 398 //- /core/lib.rs
401 pub mod string {
402 pub struct String { }
403 }
404 pub mod result { 399 pub mod result {
405 pub enum Result<T, E> { Ok(T), Err(E) } 400 pub enum Result<T, E> { Ok(T), Err(E) }
406 } 401 }
407 "#; 402 "#;
408 let after = r#" 403 let after = r#"
409 use std::{string::String, result::Result::{self, Ok, Err}}; 404 use core::result::Result::{self, Ok, Err};
410 405
411 type MyResult<T> = Result<T, String>; 406 type MyResult<T> = Result<T, ()>;
412 fn div(x: i32, y: i32) -> MyResult<i32> { 407 fn div(x: i32, y: i32) -> MyResult<i32> {
413 if y == 0 { 408 if y == 0 {
414 return Err("div by zero".into()); 409 return Err(());
415 } 410 }
416 Ok(x / y) 411 Ok(x / y)
417 } 412 }
@@ -423,16 +418,13 @@ mod tests {
423 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { 418 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
424 let content = r#" 419 let content = r#"
425 //- /main.rs 420 //- /main.rs
426 use std::{string::String, result::Result::{self, Ok, Err}}; 421 use core::result::Result::{self, Ok, Err};
427 422
428 fn foo() -> Result<String, i32> { 423 fn foo() -> Result<(), i32> {
429 0<|> 424 0<|>
430 } 425 }
431 426
432 //- /std/lib.rs 427 //- /core/lib.rs
433 pub mod string {
434 pub struct String { }
435 }
436 pub mod result { 428 pub mod result {
437 pub enum Result<T, E> { Ok(T), Err(E) } 429 pub enum Result<T, E> { Ok(T), Err(E) }
438 } 430 }
@@ -444,7 +436,7 @@ mod tests {
444 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { 436 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
445 let content = r#" 437 let content = r#"
446 //- /main.rs 438 //- /main.rs
447 use std::{string::String, result::Result::{self, Ok, Err}}; 439 use core::result::Result::{self, Ok, Err};
448 440
449 enum SomeOtherEnum { 441 enum SomeOtherEnum {
450 Ok(i32), 442 Ok(i32),
@@ -455,10 +447,7 @@ mod tests {
455 0<|> 447 0<|>
456 } 448 }
457 449
458 //- /std/lib.rs 450 //- /core/lib.rs
459 pub mod string {
460 pub struct String { }
461 }
462 pub mod result { 451 pub mod result {
463 pub enum Result<T, E> { Ok(T), Err(E) } 452 pub enum Result<T, E> { Ok(T), Err(E) }
464 } 453 }
@@ -731,6 +720,18 @@ mod tests {
731 "#, 720 "#,
732 check_struct_shorthand_initialization, 721 check_struct_shorthand_initialization,
733 ); 722 );
723 check_not_applicable(
724 r#"
725 struct A(usize);
726
727 fn main() {
728 A {
729 0: 0
730 }
731 }
732 "#,
733 check_struct_shorthand_initialization,
734 );
734 735
735 check_apply( 736 check_apply(
736 r#" 737 r#"
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index 9572debd8..ca8a6a650 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -10,7 +10,7 @@ use std::{
10use hir::{Docs, Documentation, HasSource, HirDisplay}; 10use hir::{Docs, Documentation, HasSource, HirDisplay};
11use ra_ide_db::RootDatabase; 11use ra_ide_db::RootDatabase;
12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; 12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
13use stdx::SepBy; 13use stdx::{split1, SepBy};
14 14
15use crate::display::{generic_parameters, where_predicates}; 15use crate::display::{generic_parameters, where_predicates};
16 16
@@ -207,7 +207,16 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
207 res.push(raw_param); 207 res.push(raw_param);
208 } 208 }
209 209
210 res.extend(param_list.params().map(|param| param.syntax().text().to_string())); 210 // macro-generated functions are missing whitespace
211 fn fmt_param(param: ast::Param) -> String {
212 let text = param.syntax().text().to_string();
213 match split1(&text, ':') {
214 Some((left, right)) => format!("{}: {}", left.trim(), right.trim()),
215 _ => text,
216 }
217 }
218
219 res.extend(param_list.params().map(fmt_param));
211 res_types.extend(param_list.params().map(|param| { 220 res_types.extend(param_list.params().map(|param| {
212 let param_text = param.syntax().text().to_string(); 221 let param_text = param.syntax().text().to_string();
213 match param_text.split(':').nth(1).and_then(|it| it.get(1..)) { 222 match param_text.split(':').nth(1).and_then(|it| it.get(1..)) {
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index 5da28edd2..c7bb1e69f 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -92,15 +92,16 @@ impl NavigationTarget {
92 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 92 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
93 if let Some(src) = module.declaration_source(db) { 93 if let Some(src) = module.declaration_source(db) {
94 let frange = original_range(db, src.as_ref().map(|it| it.syntax())); 94 let frange = original_range(db, src.as_ref().map(|it| it.syntax()));
95 return NavigationTarget::from_syntax( 95 let mut res = NavigationTarget::from_syntax(
96 frange.file_id, 96 frange.file_id,
97 name, 97 name,
98 None, 98 None,
99 frange.range, 99 frange.range,
100 src.value.syntax().kind(), 100 src.value.syntax().kind(),
101 src.value.doc_comment_text(),
102 src.value.short_label(),
103 ); 101 );
102 res.docs = src.value.doc_comment_text();
103 res.description = src.value.short_label();
104 return res;
104 } 105 }
105 module.to_nav(db) 106 module.to_nav(db)
106 } 107 }
@@ -130,11 +131,9 @@ impl NavigationTarget {
130 } 131 }
131 132
132 /// Allows `NavigationTarget` to be created from a `NameOwner` 133 /// Allows `NavigationTarget` to be created from a `NameOwner`
133 fn from_named( 134 pub(crate) fn from_named(
134 db: &RootDatabase, 135 db: &RootDatabase,
135 node: InFile<&dyn ast::NameOwner>, 136 node: InFile<&dyn ast::NameOwner>,
136 docs: Option<String>,
137 description: Option<String>,
138 ) -> NavigationTarget { 137 ) -> NavigationTarget {
139 //FIXME: use `_` instead of empty string 138 //FIXME: use `_` instead of empty string
140 let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default(); 139 let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default();
@@ -148,8 +147,6 @@ impl NavigationTarget {
148 focus_range, 147 focus_range,
149 frange.range, 148 frange.range,
150 node.value.syntax().kind(), 149 node.value.syntax().kind(),
151 docs,
152 description,
153 ) 150 )
154 } 151 }
155 152
@@ -159,8 +156,6 @@ impl NavigationTarget {
159 focus_range: Option<TextRange>, 156 focus_range: Option<TextRange>,
160 full_range: TextRange, 157 full_range: TextRange,
161 kind: SyntaxKind, 158 kind: SyntaxKind,
162 docs: Option<String>,
163 description: Option<String>,
164 ) -> NavigationTarget { 159 ) -> NavigationTarget {
165 NavigationTarget { 160 NavigationTarget {
166 file_id, 161 file_id,
@@ -169,8 +164,8 @@ impl NavigationTarget {
169 full_range, 164 full_range,
170 focus_range, 165 focus_range,
171 container_name: None, 166 container_name: None,
172 description, 167 description: None,
173 docs, 168 docs: None,
174 } 169 }
175 } 170 }
176} 171}
@@ -238,12 +233,11 @@ where
238{ 233{
239 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 234 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
240 let src = self.source(db); 235 let src = self.source(db);
241 NavigationTarget::from_named( 236 let mut res =
242 db, 237 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
243 src.as_ref().map(|it| it as &dyn ast::NameOwner), 238 res.docs = src.value.doc_comment_text();
244 src.value.doc_comment_text(), 239 res.description = src.value.short_label();
245 src.value.short_label(), 240 res
246 )
247 } 241 }
248} 242}
249 243
@@ -258,15 +252,7 @@ impl ToNav for hir::Module {
258 } 252 }
259 }; 253 };
260 let frange = original_range(db, src.with_value(syntax)); 254 let frange = original_range(db, src.with_value(syntax));
261 NavigationTarget::from_syntax( 255 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind())
262 frange.file_id,
263 name,
264 focus,
265 frange.range,
266 syntax.kind(),
267 None,
268 None,
269 )
270 } 256 }
271} 257}
272 258
@@ -285,8 +271,6 @@ impl ToNav for hir::ImplDef {
285 None, 271 None,
286 frange.range, 272 frange.range,
287 src.value.syntax().kind(), 273 src.value.syntax().kind(),
288 None,
289 None,
290 ) 274 )
291 } 275 }
292} 276}
@@ -296,12 +280,12 @@ impl ToNav for hir::Field {
296 let src = self.source(db); 280 let src = self.source(db);
297 281
298 match &src.value { 282 match &src.value {
299 FieldSource::Named(it) => NavigationTarget::from_named( 283 FieldSource::Named(it) => {
300 db, 284 let mut res = NavigationTarget::from_named(db, src.with_value(it));
301 src.with_value(it), 285 res.docs = it.doc_comment_text();
302 it.doc_comment_text(), 286 res.description = it.short_label();
303 it.short_label(), 287 res
304 ), 288 }
305 FieldSource::Pos(it) => { 289 FieldSource::Pos(it) => {
306 let frange = original_range(db, src.with_value(it.syntax())); 290 let frange = original_range(db, src.with_value(it.syntax()));
307 NavigationTarget::from_syntax( 291 NavigationTarget::from_syntax(
@@ -310,8 +294,6 @@ impl ToNav for hir::Field {
310 None, 294 None,
311 frange.range, 295 frange.range,
312 it.syntax().kind(), 296 it.syntax().kind(),
313 None,
314 None,
315 ) 297 )
316 } 298 }
317 } 299 }
@@ -322,12 +304,10 @@ impl ToNav for hir::MacroDef {
322 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 304 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
323 let src = self.source(db); 305 let src = self.source(db);
324 log::debug!("nav target {:#?}", src.value.syntax()); 306 log::debug!("nav target {:#?}", src.value.syntax());
325 NavigationTarget::from_named( 307 let mut res =
326 db, 308 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
327 src.as_ref().map(|it| it as &dyn ast::NameOwner), 309 res.docs = src.value.doc_comment_text();
328 src.value.doc_comment_text(), 310 res
329 None,
330 )
331 } 311 }
332} 312}
333 313
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index a6c86e99c..0798d2c36 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -1,6 +1,6 @@
1use hir::Semantics; 1use hir::Semantics;
2use ra_ide_db::{ 2use ra_ide_db::{
3 defs::{classify_name, classify_name_ref}, 3 defs::{classify_name, classify_name_ref, NameClass},
4 symbol_index, RootDatabase, 4 symbol_index, RootDatabase,
5}; 5};
6use ra_syntax::{ 6use ra_syntax::{
@@ -39,7 +39,10 @@ pub(crate) fn goto_definition(
39 reference_definition(&sema, &name_ref).to_vec() 39 reference_definition(&sema, &name_ref).to_vec()
40 }, 40 },
41 ast::Name(name) => { 41 ast::Name(name) => {
42 let def = classify_name(&sema, &name)?.definition(); 42 let def = match classify_name(&sema, &name)? {
43 NameClass::Definition(def) | NameClass::ConstReference(def) => def,
44 NameClass::FieldShorthand { local: _, field } => field,
45 };
43 let nav = def.try_to_nav(sema.db)?; 46 let nav = def.try_to_nav(sema.db)?;
44 vec![nav] 47 vec![nav]
45 }, 48 },
@@ -886,4 +889,23 @@ mod tests {
886 "x", 889 "x",
887 ) 890 )
888 } 891 }
892
893 #[test]
894 fn goto_def_for_enum_variant_field() {
895 check_goto(
896 "
897 //- /lib.rs
898 enum Foo {
899 Bar { x: i32 }
900 }
901 fn baz(foo: Foo) {
902 match foo {
903 Foo::Bar { x<|> } => x
904 };
905 }
906 ",
907 "x RECORD_FIELD_DEF FileId(1) 21..27 21..22",
908 "x: i32|x",
909 );
910 }
889} 911}
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index d96cb5596..ad78b7671 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,8 +1,8 @@
1use std::iter::once; 1use std::iter::once;
2 2
3use hir::{ 3use hir::{
4 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, 4 Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay,
5 ModuleSource, Semantics, 5 ModuleDef, ModuleSource, Semantics,
6}; 6};
7use itertools::Itertools; 7use itertools::Itertools;
8use ra_db::SourceDatabase; 8use ra_db::SourceDatabase;
@@ -10,22 +10,55 @@ use ra_ide_db::{
10 defs::{classify_name, classify_name_ref, Definition}, 10 defs::{classify_name, classify_name_ref, Definition},
11 RootDatabase, 11 RootDatabase,
12}; 12};
13use ra_syntax::{ 13use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
14 ast::{self, DocCommentsOwner},
15 match_ast, AstNode,
16 SyntaxKind::*,
17 SyntaxToken, TokenAtOffset,
18};
19 14
20use crate::{ 15use crate::{
21 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, 16 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav},
22 FilePosition, RangeInfo, 17 runnables::runnable,
18 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
23}; 19};
20use test_utils::mark;
21
22#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct HoverConfig {
24 pub implementations: bool,
25 pub run: bool,
26 pub debug: bool,
27}
28
29impl Default for HoverConfig {
30 fn default() -> Self {
31 Self { implementations: true, run: true, debug: true }
32 }
33}
34
35impl HoverConfig {
36 pub const NO_ACTIONS: Self = Self { implementations: false, run: false, debug: false };
37
38 pub fn any(&self) -> bool {
39 self.implementations || self.runnable()
40 }
41
42 pub fn none(&self) -> bool {
43 !self.any()
44 }
45
46 pub fn runnable(&self) -> bool {
47 self.run || self.debug
48 }
49}
50
51#[derive(Debug, Clone)]
52pub enum HoverAction {
53 Runnable(Runnable),
54 Implementaion(FilePosition),
55}
24 56
25/// Contains the results when hovering over an item 57/// Contains the results when hovering over an item
26#[derive(Debug, Default)] 58#[derive(Debug, Default)]
27pub struct HoverResult { 59pub struct HoverResult {
28 results: Vec<String>, 60 results: Vec<String>,
61 actions: Vec<HoverAction>,
29} 62}
30 63
31impl HoverResult { 64impl HoverResult {
@@ -53,10 +86,20 @@ impl HoverResult {
53 &self.results 86 &self.results
54 } 87 }
55 88
89 pub fn actions(&self) -> &[HoverAction] {
90 &self.actions
91 }
92
93 pub fn push_action(&mut self, action: HoverAction) {
94 self.actions.push(action);
95 }
96
56 /// Returns the results converted into markup 97 /// Returns the results converted into markup
57 /// for displaying in a UI 98 /// for displaying in a UI
99 ///
100 /// Does not process actions!
58 pub fn to_markup(&self) -> String { 101 pub fn to_markup(&self) -> String {
59 self.results.join("\n\n---\n") 102 self.results.join("\n\n___\n")
60 } 103 }
61} 104}
62 105
@@ -87,6 +130,14 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
87 res.extend(hover_text_from_name_kind(db, name_kind)); 130 res.extend(hover_text_from_name_kind(db, name_kind));
88 131
89 if !res.is_empty() { 132 if !res.is_empty() {
133 if let Some(action) = show_implementations_action(db, name_kind) {
134 res.push_action(action);
135 }
136
137 if let Some(action) = runnable_action(&sema, name_kind, position.file_id) {
138 res.push_action(action);
139 }
140
90 return Some(RangeInfo::new(range, res)); 141 return Some(RangeInfo::new(range, res));
91 } 142 }
92 } 143 }
@@ -117,6 +168,56 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
117 Some(RangeInfo::new(range, res)) 168 Some(RangeInfo::new(range, res))
118} 169}
119 170
171fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
172 fn to_action(nav_target: NavigationTarget) -> HoverAction {
173 HoverAction::Implementaion(FilePosition {
174 file_id: nav_target.file_id(),
175 offset: nav_target.range().start(),
176 })
177 }
178
179 match def {
180 Definition::ModuleDef(it) => match it {
181 ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))),
182 ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))),
183 ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))),
184 ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))),
185 _ => None,
186 },
187 _ => None,
188 }
189}
190
191fn runnable_action(
192 sema: &Semantics<RootDatabase>,
193 def: Definition,
194 file_id: FileId,
195) -> Option<HoverAction> {
196 match def {
197 Definition::ModuleDef(it) => match it {
198 ModuleDef::Module(it) => match it.definition_source(sema.db).value {
199 ModuleSource::Module(it) => runnable(&sema, it.syntax().clone(), file_id)
200 .map(|it| HoverAction::Runnable(it)),
201 _ => None,
202 },
203 ModuleDef::Function(it) => {
204 let src = it.source(sema.db);
205 if src.file_id != file_id.into() {
206 mark::hit!(hover_macro_generated_struct_fn_doc_comment);
207 mark::hit!(hover_macro_generated_struct_fn_doc_attr);
208
209 return None;
210 }
211
212 runnable(&sema, src.value.syntax().clone(), file_id)
213 .map(|it| HoverAction::Runnable(it))
214 }
215 _ => None,
216 },
217 _ => None,
218 }
219}
220
120fn hover_text( 221fn hover_text(
121 docs: Option<String>, 222 docs: Option<String>,
122 desc: Option<String>, 223 desc: Option<String>,
@@ -169,13 +270,15 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
169 return match def { 270 return match def {
170 Definition::Macro(it) => { 271 Definition::Macro(it) => {
171 let src = it.source(db); 272 let src = it.source(db);
172 hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path) 273 let docs = Documentation::from_ast(&src.value).map(Into::into);
274 hover_text(docs, Some(macro_label(&src.value)), mod_path)
173 } 275 }
174 Definition::Field(it) => { 276 Definition::Field(it) => {
175 let src = it.source(db); 277 let src = it.source(db);
176 match src.value { 278 match src.value {
177 FieldSource::Named(it) => { 279 FieldSource::Named(it) => {
178 hover_text(it.doc_comment_text(), it.short_label(), mod_path) 280 let docs = Documentation::from_ast(&it).map(Into::into);
281 hover_text(docs, it.short_label(), mod_path)
179 } 282 }
180 _ => None, 283 _ => None,
181 } 284 }
@@ -183,7 +286,8 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
183 Definition::ModuleDef(it) => match it { 286 Definition::ModuleDef(it) => match it {
184 ModuleDef::Module(it) => match it.definition_source(db).value { 287 ModuleDef::Module(it) => match it.definition_source(db).value {
185 ModuleSource::Module(it) => { 288 ModuleSource::Module(it) => {
186 hover_text(it.doc_comment_text(), it.short_label(), mod_path) 289 let docs = Documentation::from_ast(&it).map(Into::into);
290 hover_text(docs, it.short_label(), mod_path)
187 } 291 }
188 _ => None, 292 _ => None,
189 }, 293 },
@@ -208,10 +312,11 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
208 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String> 312 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String>
209 where 313 where
210 D: HasSource<Ast = A>, 314 D: HasSource<Ast = A>,
211 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, 315 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner,
212 { 316 {
213 let src = def.source(db); 317 let src = def.source(db);
214 hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path) 318 let docs = Documentation::from_ast(&src.value).map(Into::into);
319 hover_text(docs, src.value.short_label(), mod_path)
215 } 320 }
216} 321}
217 322
@@ -229,6 +334,9 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
229 334
230#[cfg(test)] 335#[cfg(test)]
231mod tests { 336mod tests {
337 use super::*;
338 use insta::assert_debug_snapshot;
339
232 use ra_db::FileLoader; 340 use ra_db::FileLoader;
233 use ra_syntax::TextRange; 341 use ra_syntax::TextRange;
234 342
@@ -242,7 +350,15 @@ mod tests {
242 s.map(trim_markup) 350 s.map(trim_markup)
243 } 351 }
244 352
245 fn check_hover_result(fixture: &str, expected: &[&str]) -> String { 353 fn assert_impl_action(action: &HoverAction, position: u32) {
354 let offset = match action {
355 HoverAction::Implementaion(pos) => pos.offset,
356 it => panic!("Unexpected hover action: {:#?}", it),
357 };
358 assert_eq!(offset, position.into());
359 }
360
361 fn check_hover_result(fixture: &str, expected: &[&str]) -> (String, Vec<HoverAction>) {
246 let (analysis, position) = analysis_and_position(fixture); 362 let (analysis, position) = analysis_and_position(fixture);
247 let hover = analysis.hover(position).unwrap().unwrap(); 363 let hover = analysis.hover(position).unwrap().unwrap();
248 let mut results = Vec::from(hover.info.results()); 364 let mut results = Vec::from(hover.info.results());
@@ -257,7 +373,7 @@ mod tests {
257 assert_eq!(hover.info.len(), expected.len()); 373 assert_eq!(hover.info.len(), expected.len());
258 374
259 let content = analysis.db.file_text(position.file_id); 375 let content = analysis.db.file_text(position.file_id);
260 content[hover.range].to_string() 376 (content[hover.range].to_string(), hover.info.actions().to_vec())
261 } 377 }
262 378
263 fn check_hover_no_result(fixture: &str) { 379 fn check_hover_no_result(fixture: &str) {
@@ -458,7 +574,7 @@ struct Test<K, T = u8> {
458} 574}
459 575
460fn main() { 576fn main() {
461 let zz<|> = Test { t: 23, k: 33 }; 577 let zz<|> = Test { t: 23u8, k: 33 };
462}"#, 578}"#,
463 &["Test<i32, u8>"], 579 &["Test<i32, u8>"],
464 ); 580 );
@@ -747,7 +863,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
747 863
748 #[test] 864 #[test]
749 fn test_hover_through_macro() { 865 fn test_hover_through_macro() {
750 let hover_on = check_hover_result( 866 let (hover_on, _) = check_hover_result(
751 " 867 "
752 //- /lib.rs 868 //- /lib.rs
753 macro_rules! id { 869 macro_rules! id {
@@ -768,7 +884,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
768 884
769 #[test] 885 #[test]
770 fn test_hover_through_expr_in_macro() { 886 fn test_hover_through_expr_in_macro() {
771 let hover_on = check_hover_result( 887 let (hover_on, _) = check_hover_result(
772 " 888 "
773 //- /lib.rs 889 //- /lib.rs
774 macro_rules! id { 890 macro_rules! id {
@@ -786,7 +902,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
786 902
787 #[test] 903 #[test]
788 fn test_hover_through_expr_in_macro_recursive() { 904 fn test_hover_through_expr_in_macro_recursive() {
789 let hover_on = check_hover_result( 905 let (hover_on, _) = check_hover_result(
790 " 906 "
791 //- /lib.rs 907 //- /lib.rs
792 macro_rules! id_deep { 908 macro_rules! id_deep {
@@ -807,7 +923,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
807 923
808 #[test] 924 #[test]
809 fn test_hover_through_func_in_macro_recursive() { 925 fn test_hover_through_func_in_macro_recursive() {
810 let hover_on = check_hover_result( 926 let (hover_on, _) = check_hover_result(
811 " 927 "
812 //- /lib.rs 928 //- /lib.rs
813 macro_rules! id_deep { 929 macro_rules! id_deep {
@@ -831,7 +947,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
831 947
832 #[test] 948 #[test]
833 fn test_hover_through_literal_string_in_macro() { 949 fn test_hover_through_literal_string_in_macro() {
834 let hover_on = check_hover_result( 950 let (hover_on, _) = check_hover_result(
835 r#" 951 r#"
836 //- /lib.rs 952 //- /lib.rs
837 macro_rules! arr { 953 macro_rules! arr {
@@ -850,7 +966,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
850 966
851 #[test] 967 #[test]
852 fn test_hover_through_assert_macro() { 968 fn test_hover_through_assert_macro() {
853 let hover_on = check_hover_result( 969 let (hover_on, _) = check_hover_result(
854 r#" 970 r#"
855 //- /lib.rs 971 //- /lib.rs
856 #[rustc_builtin_macro] 972 #[rustc_builtin_macro]
@@ -926,13 +1042,14 @@ fn func(foo: i32) { if true { <|>foo; }; }
926 1042
927 #[test] 1043 #[test]
928 fn test_hover_trait_show_qualifiers() { 1044 fn test_hover_trait_show_qualifiers() {
929 check_hover_result( 1045 let (_, actions) = check_hover_result(
930 " 1046 "
931 //- /lib.rs 1047 //- /lib.rs
932 unsafe trait foo<|>() {} 1048 unsafe trait foo<|>() {}
933 ", 1049 ",
934 &["unsafe trait foo"], 1050 &["unsafe trait foo"],
935 ); 1051 );
1052 assert_impl_action(&actions[0], 13);
936 } 1053 }
937 1054
938 #[test] 1055 #[test]
@@ -951,4 +1068,246 @@ fn func(foo: i32) { if true { <|>foo; }; }
951 &["mod my"], 1068 &["mod my"],
952 ); 1069 );
953 } 1070 }
1071
1072 #[test]
1073 fn test_hover_struct_doc_comment() {
1074 check_hover_result(
1075 r#"
1076 //- /lib.rs
1077 /// bar docs
1078 struct Bar;
1079
1080 fn foo() {
1081 let bar = Ba<|>r;
1082 }
1083 "#,
1084 &["struct Bar\n```\n___\n\nbar docs"],
1085 );
1086 }
1087
1088 #[test]
1089 fn test_hover_struct_doc_attr() {
1090 check_hover_result(
1091 r#"
1092 //- /lib.rs
1093 #[doc = "bar docs"]
1094 struct Bar;
1095
1096 fn foo() {
1097 let bar = Ba<|>r;
1098 }
1099 "#,
1100 &["struct Bar\n```\n___\n\nbar docs"],
1101 );
1102 }
1103
1104 #[test]
1105 fn test_hover_struct_doc_attr_multiple_and_mixed() {
1106 check_hover_result(
1107 r#"
1108 //- /lib.rs
1109 /// bar docs 0
1110 #[doc = "bar docs 1"]
1111 #[doc = "bar docs 2"]
1112 struct Bar;
1113
1114 fn foo() {
1115 let bar = Ba<|>r;
1116 }
1117 "#,
1118 &["struct Bar\n```\n___\n\nbar docs 0\n\nbar docs 1\n\nbar docs 2"],
1119 );
1120 }
1121
1122 #[test]
1123 fn test_hover_macro_generated_struct_fn_doc_comment() {
1124 mark::check!(hover_macro_generated_struct_fn_doc_comment);
1125
1126 check_hover_result(
1127 r#"
1128 //- /lib.rs
1129 macro_rules! bar {
1130 () => {
1131 struct Bar;
1132 impl Bar {
1133 /// Do the foo
1134 fn foo(&self) {}
1135 }
1136 }
1137 }
1138
1139 bar!();
1140
1141 fn foo() {
1142 let bar = Bar;
1143 bar.fo<|>o();
1144 }
1145 "#,
1146 &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\n Do the foo"],
1147 );
1148 }
1149
1150 #[test]
1151 fn test_hover_macro_generated_struct_fn_doc_attr() {
1152 mark::check!(hover_macro_generated_struct_fn_doc_attr);
1153
1154 check_hover_result(
1155 r#"
1156 //- /lib.rs
1157 macro_rules! bar {
1158 () => {
1159 struct Bar;
1160 impl Bar {
1161 #[doc = "Do the foo"]
1162 fn foo(&self) {}
1163 }
1164 }
1165 }
1166
1167 bar!();
1168
1169 fn foo() {
1170 let bar = Bar;
1171 bar.fo<|>o();
1172 }
1173 "#,
1174 &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"],
1175 );
1176 }
1177
1178 #[test]
1179 fn test_hover_trait_has_impl_action() {
1180 let (_, actions) = check_hover_result(
1181 "
1182 //- /lib.rs
1183 trait foo<|>() {}
1184 ",
1185 &["trait foo"],
1186 );
1187 assert_impl_action(&actions[0], 6);
1188 }
1189
1190 #[test]
1191 fn test_hover_struct_has_impl_action() {
1192 let (_, actions) = check_hover_result(
1193 "
1194 //- /lib.rs
1195 struct foo<|>() {}
1196 ",
1197 &["struct foo"],
1198 );
1199 assert_impl_action(&actions[0], 7);
1200 }
1201
1202 #[test]
1203 fn test_hover_union_has_impl_action() {
1204 let (_, actions) = check_hover_result(
1205 "
1206 //- /lib.rs
1207 union foo<|>() {}
1208 ",
1209 &["union foo"],
1210 );
1211 assert_impl_action(&actions[0], 6);
1212 }
1213
1214 #[test]
1215 fn test_hover_enum_has_impl_action() {
1216 let (_, actions) = check_hover_result(
1217 "
1218 //- /lib.rs
1219 enum foo<|>() {
1220 A,
1221 B
1222 }
1223 ",
1224 &["enum foo"],
1225 );
1226 assert_impl_action(&actions[0], 5);
1227 }
1228
1229 #[test]
1230 fn test_hover_test_has_action() {
1231 let (_, actions) = check_hover_result(
1232 "
1233 //- /lib.rs
1234 #[test]
1235 fn foo_<|>test() {}
1236 ",
1237 &["fn foo_test()"],
1238 );
1239 assert_debug_snapshot!(actions,
1240 @r###"
1241 [
1242 Runnable(
1243 Runnable {
1244 nav: NavigationTarget {
1245 file_id: FileId(
1246 1,
1247 ),
1248 full_range: 0..24,
1249 name: "foo_test",
1250 kind: FN_DEF,
1251 focus_range: Some(
1252 11..19,
1253 ),
1254 container_name: None,
1255 description: None,
1256 docs: None,
1257 },
1258 kind: Test {
1259 test_id: Path(
1260 "foo_test",
1261 ),
1262 attr: TestAttr {
1263 ignore: false,
1264 },
1265 },
1266 cfg_exprs: [],
1267 },
1268 ),
1269 ]
1270 "###);
1271 }
1272
1273 #[test]
1274 fn test_hover_test_mod_has_action() {
1275 let (_, actions) = check_hover_result(
1276 "
1277 //- /lib.rs
1278 mod tests<|> {
1279 #[test]
1280 fn foo_test() {}
1281 }
1282 ",
1283 &["mod tests"],
1284 );
1285 assert_debug_snapshot!(actions,
1286 @r###"
1287 [
1288 Runnable(
1289 Runnable {
1290 nav: NavigationTarget {
1291 file_id: FileId(
1292 1,
1293 ),
1294 full_range: 0..46,
1295 name: "tests",
1296 kind: MODULE,
1297 focus_range: Some(
1298 4..9,
1299 ),
1300 container_name: None,
1301 description: None,
1302 docs: None,
1303 },
1304 kind: TestMod {
1305 path: "tests",
1306 },
1307 cfg_exprs: [],
1308 },
1309 ),
1310 ]
1311 "###);
1312 }
954} 1313}
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 75bd3c96b..7eb2cef73 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -149,11 +149,10 @@ fn get_param_name_hints(
149 ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(), 149 ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(),
150 _ => return None, 150 _ => return None,
151 }; 151 };
152 let args_count = args.clone().count();
153 152
154 let fn_signature = get_fn_signature(sema, &expr)?; 153 let fn_signature = get_fn_signature(sema, &expr)?;
155 let n_params_to_skip = 154 let n_params_to_skip =
156 if fn_signature.has_self_param && fn_signature.parameter_names.len() > args_count { 155 if fn_signature.has_self_param && matches!(&expr, ast::Expr::MethodCallExpr(_)) {
157 1 156 1
158 } else { 157 } else {
159 0 158 0
@@ -416,7 +415,7 @@ struct Test<K, T = u8> {
416} 415}
417 416
418fn main() { 417fn main() {
419 let zz = Test { t: 23, k: 33 }; 418 let zz = Test { t: 23u8, k: 33 };
420 let zz_ref = &zz; 419 let zz_ref = &zz;
421}"#, 420}"#,
422 ); 421 );
@@ -429,7 +428,7 @@ fn main() {
429 label: "Test<i32>", 428 label: "Test<i32>",
430 }, 429 },
431 InlayHint { 430 InlayHint {
432 range: 105..111, 431 range: 107..113,
433 kind: TypeHint, 432 kind: TypeHint,
434 label: "&Test<i32>", 433 label: "&Test<i32>",
435 }, 434 },
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 12d5716e8..28f686767 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -66,7 +66,7 @@ pub use crate::{
66 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 66 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
67 expand_macro::ExpandedMacro, 67 expand_macro::ExpandedMacro,
68 folding_ranges::{Fold, FoldKind}, 68 folding_ranges::{Fold, FoldKind},
69 hover::HoverResult, 69 hover::{HoverAction, HoverConfig, HoverResult},
70 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, 70 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
71 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, 71 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
72 runnables::{Runnable, RunnableKind, TestId}, 72 runnables::{Runnable, RunnableKind, TestId},
@@ -77,7 +77,7 @@ pub use crate::{
77}; 77};
78 78
79pub use hir::Documentation; 79pub use hir::Documentation;
80pub use ra_assists::{AssistConfig, AssistId}; 80pub use ra_assists::{Assist, AssistConfig, AssistId, ResolvedAssist};
81pub use ra_db::{ 81pub use ra_db::{
82 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, 82 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId,
83}; 83};
@@ -142,14 +142,6 @@ pub struct AnalysisHost {
142 db: RootDatabase, 142 db: RootDatabase,
143} 143}
144 144
145#[derive(Debug)]
146pub struct Assist {
147 pub id: AssistId,
148 pub label: String,
149 pub group_label: Option<String>,
150 pub source_change: SourceChange,
151}
152
153impl AnalysisHost { 145impl AnalysisHost {
154 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost { 146 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost {
155 AnalysisHost { db: RootDatabase::new(lru_capacity) } 147 AnalysisHost { db: RootDatabase::new(lru_capacity) }
@@ -470,20 +462,23 @@ impl Analysis {
470 self.with_db(|db| completion::completions(db, config, position).map(Into::into)) 462 self.with_db(|db| completion::completions(db, config, position).map(Into::into))
471 } 463 }
472 464
473 /// Computes assists (aka code actions aka intentions) for the given 465 /// Computes resolved assists with source changes for the given position.
466 pub fn resolved_assists(
467 &self,
468 config: &AssistConfig,
469 frange: FileRange,
470 ) -> Cancelable<Vec<ResolvedAssist>> {
471 self.with_db(|db| ra_assists::Assist::resolved(db, config, frange))
472 }
473
474 /// Computes unresolved assists (aka code actions aka intentions) for the given
474 /// position. 475 /// position.
475 pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable<Vec<Assist>> { 476 pub fn unresolved_assists(
476 self.with_db(|db| { 477 &self,
477 ra_assists::Assist::resolved(db, config, frange) 478 config: &AssistConfig,
478 .into_iter() 479 frange: FileRange,
479 .map(|assist| Assist { 480 ) -> Cancelable<Vec<Assist>> {
480 id: assist.assist.id, 481 self.with_db(|db| Assist::unresolved(db, config, frange))
481 label: assist.assist.label,
482 group_label: assist.assist.group.map(|it| it.0),
483 source_change: assist.source_change,
484 })
485 .collect()
486 })
487 } 482 }
488 483
489 /// Computes the set of diagnostics for the given file. 484 /// Computes the set of diagnostics for the given file.
@@ -508,7 +503,7 @@ impl Analysis {
508 ) -> Cancelable<Result<SourceChange, SsrError>> { 503 ) -> Cancelable<Result<SourceChange, SsrError>> {
509 self.with_db(|db| { 504 self.with_db(|db| {
510 let edits = ssr::parse_search_replace(query, parse_only, db)?; 505 let edits = ssr::parse_search_replace(query, parse_only, db)?;
511 Ok(SourceChange::source_file_edits(edits)) 506 Ok(SourceChange::from(edits))
512 }) 507 })
513 } 508 }
514 509
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 28c6349b1..915d4f4d3 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -171,7 +171,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo
171 ), 171 ),
172 }); 172 });
173 173
174 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) 174 Some(RangeInfo::new(range, SourceChange::from(edits)))
175} 175}
176 176
177fn text_edit_from_self_param( 177fn text_edit_from_self_param(
@@ -234,7 +234,7 @@ fn rename_self_to_param(
234 let range = ast::SelfParam::cast(self_token.parent()) 234 let range = ast::SelfParam::cast(self_token.parent())
235 .map_or(self_token.text_range(), |p| p.syntax().text_range()); 235 .map_or(self_token.text_range(), |p| p.syntax().text_range());
236 236
237 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) 237 Some(RangeInfo::new(range, SourceChange::from(edits)))
238} 238}
239 239
240fn rename_reference( 240fn rename_reference(
@@ -253,7 +253,7 @@ fn rename_reference(
253 return None; 253 return None;
254 } 254 }
255 255
256 Some(RangeInfo::new(range, SourceChange::source_file_edits(edit))) 256 Some(RangeInfo::new(range, SourceChange::from(edit)))
257} 257}
258 258
259#[cfg(test)] 259#[cfg(test)]
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 286d45eee..fc57dc33d 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,31 +1,31 @@
1use std::fmt;
2
1use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; 3use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
2use itertools::Itertools; 4use itertools::Itertools;
5use ra_cfg::CfgExpr;
3use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
4use ra_syntax::{ 7use ra_syntax::{
5 ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, 8 ast::{self, AstNode, AttrsOwner, DocCommentsOwner, ModuleItemOwner, NameOwner},
6 match_ast, SyntaxNode, TextRange, 9 match_ast, SyntaxNode,
7}; 10};
8 11
9use crate::FileId; 12use crate::{display::ToNav, FileId, NavigationTarget};
10use ast::DocCommentsOwner;
11use ra_cfg::CfgExpr;
12use std::fmt::Display;
13 13
14#[derive(Debug)] 14#[derive(Debug, Clone)]
15pub struct Runnable { 15pub struct Runnable {
16 pub range: TextRange, 16 pub nav: NavigationTarget,
17 pub kind: RunnableKind, 17 pub kind: RunnableKind,
18 pub cfg_exprs: Vec<CfgExpr>, 18 pub cfg_exprs: Vec<CfgExpr>,
19} 19}
20 20
21#[derive(Debug)] 21#[derive(Debug, Clone)]
22pub enum TestId { 22pub enum TestId {
23 Name(String), 23 Name(String),
24 Path(String), 24 Path(String),
25} 25}
26 26
27impl Display for TestId { 27impl fmt::Display for TestId {
28 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 28 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29 match self { 29 match self {
30 TestId::Name(name) => write!(f, "{}", name), 30 TestId::Name(name) => write!(f, "{}", name),
31 TestId::Path(path) => write!(f, "{}", path), 31 TestId::Path(path) => write!(f, "{}", path),
@@ -33,7 +33,7 @@ impl Display for TestId {
33 } 33 }
34} 34}
35 35
36#[derive(Debug)] 36#[derive(Debug, Clone)]
37pub enum RunnableKind { 37pub enum RunnableKind {
38 Test { test_id: TestId, attr: TestAttr }, 38 Test { test_id: TestId, attr: TestAttr },
39 TestMod { path: String }, 39 TestMod { path: String },
@@ -42,6 +42,42 @@ pub enum RunnableKind {
42 Bin, 42 Bin,
43} 43}
44 44
45#[derive(Debug, Eq, PartialEq)]
46pub struct RunnableAction {
47 pub run_title: &'static str,
48 pub debugee: bool,
49}
50
51const TEST: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Test", debugee: true };
52const DOCTEST: RunnableAction =
53 RunnableAction { run_title: "▶\u{fe0e} Run Doctest", debugee: false };
54const BENCH: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Bench", debugee: true };
55const BIN: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run", debugee: true };
56
57impl Runnable {
58 // test package::module::testname
59 pub fn label(&self, target: Option<String>) -> String {
60 match &self.kind {
61 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
62 RunnableKind::TestMod { path } => format!("test-mod {}", path),
63 RunnableKind::Bench { test_id } => format!("bench {}", test_id),
64 RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
65 RunnableKind::Bin => {
66 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
67 }
68 }
69 }
70
71 pub fn action(&self) -> &'static RunnableAction {
72 match &self.kind {
73 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => &TEST,
74 RunnableKind::DocTest { .. } => &DOCTEST,
75 RunnableKind::Bench { .. } => &BENCH,
76 RunnableKind::Bin => &BIN,
77 }
78 }
79}
80
45// Feature: Run 81// Feature: Run
46// 82//
47// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor 83// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
@@ -59,7 +95,11 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
59 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect() 95 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect()
60} 96}
61 97
62fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> { 98pub(crate) fn runnable(
99 sema: &Semantics<RootDatabase>,
100 item: SyntaxNode,
101 file_id: FileId,
102) -> Option<Runnable> {
63 match_ast! { 103 match_ast! {
64 match item { 104 match item {
65 ast::FnDef(it) => runnable_fn(sema, it, file_id), 105 ast::FnDef(it) => runnable_fn(sema, it, file_id),
@@ -131,10 +171,11 @@ fn runnable_fn(
131 let cfg_exprs = 171 let cfg_exprs =
132 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 172 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
133 173
134 Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs }) 174 let nav = NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def));
175 Some(Runnable { nav, kind, cfg_exprs })
135} 176}
136 177
137#[derive(Debug)] 178#[derive(Debug, Copy, Clone)]
138pub struct TestAttr { 179pub struct TestAttr {
139 pub ignore: bool, 180 pub ignore: bool,
140} 181}
@@ -183,7 +224,6 @@ fn runnable_mod(
183 if !has_test_function { 224 if !has_test_function {
184 return None; 225 return None;
185 } 226 }
186 let range = module.syntax().text_range();
187 let module_def = sema.to_def(&module)?; 227 let module_def = sema.to_def(&module)?;
188 228
189 let path = module_def 229 let path = module_def
@@ -197,7 +237,8 @@ fn runnable_mod(
197 let cfg_exprs = 237 let cfg_exprs =
198 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 238 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
199 239
200 Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs }) 240 let nav = module_def.to_nav(sema.db);
241 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
201} 242}
202 243
203#[cfg(test)] 244#[cfg(test)]
@@ -206,6 +247,15 @@ mod tests {
206 247
207 use crate::mock_analysis::analysis_and_position; 248 use crate::mock_analysis::analysis_and_position;
208 249
250 use super::{Runnable, RunnableAction, BENCH, BIN, DOCTEST, TEST};
251
252 fn assert_actions(runnables: &[Runnable], actions: &[&RunnableAction]) {
253 assert_eq!(
254 actions,
255 runnables.into_iter().map(|it| it.action()).collect::<Vec<_>>().as_slice()
256 );
257 }
258
209 #[test] 259 #[test]
210 fn test_runnables() { 260 fn test_runnables() {
211 let (analysis, pos) = analysis_and_position( 261 let (analysis, pos) = analysis_and_position(
@@ -220,6 +270,9 @@ mod tests {
220 #[test] 270 #[test]
221 #[ignore] 271 #[ignore]
222 fn test_foo() {} 272 fn test_foo() {}
273
274 #[bench]
275 fn bench() {}
223 "#, 276 "#,
224 ); 277 );
225 let runnables = analysis.runnables(pos.file_id).unwrap(); 278 let runnables = analysis.runnables(pos.file_id).unwrap();
@@ -227,12 +280,38 @@ mod tests {
227 @r###" 280 @r###"
228 [ 281 [
229 Runnable { 282 Runnable {
230 range: 1..21, 283 nav: NavigationTarget {
284 file_id: FileId(
285 1,
286 ),
287 full_range: 1..21,
288 name: "main",
289 kind: FN_DEF,
290 focus_range: Some(
291 12..16,
292 ),
293 container_name: None,
294 description: None,
295 docs: None,
296 },
231 kind: Bin, 297 kind: Bin,
232 cfg_exprs: [], 298 cfg_exprs: [],
233 }, 299 },
234 Runnable { 300 Runnable {
235 range: 22..46, 301 nav: NavigationTarget {
302 file_id: FileId(
303 1,
304 ),
305 full_range: 22..46,
306 name: "test_foo",
307 kind: FN_DEF,
308 focus_range: Some(
309 33..41,
310 ),
311 container_name: None,
312 description: None,
313 docs: None,
314 },
236 kind: Test { 315 kind: Test {
237 test_id: Path( 316 test_id: Path(
238 "test_foo", 317 "test_foo",
@@ -244,7 +323,20 @@ mod tests {
244 cfg_exprs: [], 323 cfg_exprs: [],
245 }, 324 },
246 Runnable { 325 Runnable {
247 range: 47..81, 326 nav: NavigationTarget {
327 file_id: FileId(
328 1,
329 ),
330 full_range: 47..81,
331 name: "test_foo",
332 kind: FN_DEF,
333 focus_range: Some(
334 68..76,
335 ),
336 container_name: None,
337 description: None,
338 docs: None,
339 },
248 kind: Test { 340 kind: Test {
249 test_id: Path( 341 test_id: Path(
250 "test_foo", 342 "test_foo",
@@ -255,9 +347,32 @@ mod tests {
255 }, 347 },
256 cfg_exprs: [], 348 cfg_exprs: [],
257 }, 349 },
350 Runnable {
351 nav: NavigationTarget {
352 file_id: FileId(
353 1,
354 ),
355 full_range: 82..104,
356 name: "bench",
357 kind: FN_DEF,
358 focus_range: Some(
359 94..99,
360 ),
361 container_name: None,
362 description: None,
363 docs: None,
364 },
365 kind: Bench {
366 test_id: Path(
367 "bench",
368 ),
369 },
370 cfg_exprs: [],
371 },
258 ] 372 ]
259 "### 373 "###
260 ); 374 );
375 assert_actions(&runnables, &[&BIN, &TEST, &TEST, &BENCH]);
261 } 376 }
262 377
263 #[test] 378 #[test]
@@ -279,12 +394,38 @@ mod tests {
279 @r###" 394 @r###"
280 [ 395 [
281 Runnable { 396 Runnable {
282 range: 1..21, 397 nav: NavigationTarget {
398 file_id: FileId(
399 1,
400 ),
401 full_range: 1..21,
402 name: "main",
403 kind: FN_DEF,
404 focus_range: Some(
405 12..16,
406 ),
407 container_name: None,
408 description: None,
409 docs: None,
410 },
283 kind: Bin, 411 kind: Bin,
284 cfg_exprs: [], 412 cfg_exprs: [],
285 }, 413 },
286 Runnable { 414 Runnable {
287 range: 22..64, 415 nav: NavigationTarget {
416 file_id: FileId(
417 1,
418 ),
419 full_range: 22..64,
420 name: "foo",
421 kind: FN_DEF,
422 focus_range: Some(
423 56..59,
424 ),
425 container_name: None,
426 description: None,
427 docs: None,
428 },
288 kind: DocTest { 429 kind: DocTest {
289 test_id: Path( 430 test_id: Path(
290 "foo", 431 "foo",
@@ -295,6 +436,7 @@ mod tests {
295 ] 436 ]
296 "### 437 "###
297 ); 438 );
439 assert_actions(&runnables, &[&BIN, &DOCTEST]);
298 } 440 }
299 441
300 #[test] 442 #[test]
@@ -319,12 +461,38 @@ mod tests {
319 @r###" 461 @r###"
320 [ 462 [
321 Runnable { 463 Runnable {
322 range: 1..21, 464 nav: NavigationTarget {
465 file_id: FileId(
466 1,
467 ),
468 full_range: 1..21,
469 name: "main",
470 kind: FN_DEF,
471 focus_range: Some(
472 12..16,
473 ),
474 container_name: None,
475 description: None,
476 docs: None,
477 },
323 kind: Bin, 478 kind: Bin,
324 cfg_exprs: [], 479 cfg_exprs: [],
325 }, 480 },
326 Runnable { 481 Runnable {
327 range: 51..105, 482 nav: NavigationTarget {
483 file_id: FileId(
484 1,
485 ),
486 full_range: 51..105,
487 name: "foo",
488 kind: FN_DEF,
489 focus_range: Some(
490 97..100,
491 ),
492 container_name: None,
493 description: None,
494 docs: None,
495 },
328 kind: DocTest { 496 kind: DocTest {
329 test_id: Path( 497 test_id: Path(
330 "Data::foo", 498 "Data::foo",
@@ -335,6 +503,7 @@ mod tests {
335 ] 503 ]
336 "### 504 "###
337 ); 505 );
506 assert_actions(&runnables, &[&BIN, &DOCTEST]);
338 } 507 }
339 508
340 #[test] 509 #[test]
@@ -354,14 +523,40 @@ mod tests {
354 @r###" 523 @r###"
355 [ 524 [
356 Runnable { 525 Runnable {
357 range: 1..59, 526 nav: NavigationTarget {
527 file_id: FileId(
528 1,
529 ),
530 full_range: 1..59,
531 name: "test_mod",
532 kind: MODULE,
533 focus_range: Some(
534 13..21,
535 ),
536 container_name: None,
537 description: None,
538 docs: None,
539 },
358 kind: TestMod { 540 kind: TestMod {
359 path: "test_mod", 541 path: "test_mod",
360 }, 542 },
361 cfg_exprs: [], 543 cfg_exprs: [],
362 }, 544 },
363 Runnable { 545 Runnable {
364 range: 28..57, 546 nav: NavigationTarget {
547 file_id: FileId(
548 1,
549 ),
550 full_range: 28..57,
551 name: "test_foo1",
552 kind: FN_DEF,
553 focus_range: Some(
554 43..52,
555 ),
556 container_name: None,
557 description: None,
558 docs: None,
559 },
365 kind: Test { 560 kind: Test {
366 test_id: Path( 561 test_id: Path(
367 "test_mod::test_foo1", 562 "test_mod::test_foo1",
@@ -375,6 +570,7 @@ mod tests {
375 ] 570 ]
376 "### 571 "###
377 ); 572 );
573 assert_actions(&runnables, &[&TEST, &TEST]);
378 } 574 }
379 575
380 #[test] 576 #[test]
@@ -396,14 +592,40 @@ mod tests {
396 @r###" 592 @r###"
397 [ 593 [
398 Runnable { 594 Runnable {
399 range: 23..85, 595 nav: NavigationTarget {
596 file_id: FileId(
597 1,
598 ),
599 full_range: 23..85,
600 name: "test_mod",
601 kind: MODULE,
602 focus_range: Some(
603 27..35,
604 ),
605 container_name: None,
606 description: None,
607 docs: None,
608 },
400 kind: TestMod { 609 kind: TestMod {
401 path: "foo::test_mod", 610 path: "foo::test_mod",
402 }, 611 },
403 cfg_exprs: [], 612 cfg_exprs: [],
404 }, 613 },
405 Runnable { 614 Runnable {
406 range: 46..79, 615 nav: NavigationTarget {
616 file_id: FileId(
617 1,
618 ),
619 full_range: 46..79,
620 name: "test_foo1",
621 kind: FN_DEF,
622 focus_range: Some(
623 65..74,
624 ),
625 container_name: None,
626 description: None,
627 docs: None,
628 },
407 kind: Test { 629 kind: Test {
408 test_id: Path( 630 test_id: Path(
409 "foo::test_mod::test_foo1", 631 "foo::test_mod::test_foo1",
@@ -417,6 +639,7 @@ mod tests {
417 ] 639 ]
418 "### 640 "###
419 ); 641 );
642 assert_actions(&runnables, &[&TEST, &TEST]);
420 } 643 }
421 644
422 #[test] 645 #[test]
@@ -440,14 +663,40 @@ mod tests {
440 @r###" 663 @r###"
441 [ 664 [
442 Runnable { 665 Runnable {
443 range: 41..115, 666 nav: NavigationTarget {
667 file_id: FileId(
668 1,
669 ),
670 full_range: 41..115,
671 name: "test_mod",
672 kind: MODULE,
673 focus_range: Some(
674 45..53,
675 ),
676 container_name: None,
677 description: None,
678 docs: None,
679 },
444 kind: TestMod { 680 kind: TestMod {
445 path: "foo::bar::test_mod", 681 path: "foo::bar::test_mod",
446 }, 682 },
447 cfg_exprs: [], 683 cfg_exprs: [],
448 }, 684 },
449 Runnable { 685 Runnable {
450 range: 68..105, 686 nav: NavigationTarget {
687 file_id: FileId(
688 1,
689 ),
690 full_range: 68..105,
691 name: "test_foo1",
692 kind: FN_DEF,
693 focus_range: Some(
694 91..100,
695 ),
696 container_name: None,
697 description: None,
698 docs: None,
699 },
451 kind: Test { 700 kind: Test {
452 test_id: Path( 701 test_id: Path(
453 "foo::bar::test_mod::test_foo1", 702 "foo::bar::test_mod::test_foo1",
@@ -461,6 +710,7 @@ mod tests {
461 ] 710 ]
462 "### 711 "###
463 ); 712 );
713 assert_actions(&runnables, &[&TEST, &TEST]);
464 } 714 }
465 715
466 #[test] 716 #[test]
@@ -479,7 +729,20 @@ mod tests {
479 @r###" 729 @r###"
480 [ 730 [
481 Runnable { 731 Runnable {
482 range: 1..58, 732 nav: NavigationTarget {
733 file_id: FileId(
734 1,
735 ),
736 full_range: 1..58,
737 name: "test_foo1",
738 kind: FN_DEF,
739 focus_range: Some(
740 44..53,
741 ),
742 container_name: None,
743 description: None,
744 docs: None,
745 },
483 kind: Test { 746 kind: Test {
484 test_id: Path( 747 test_id: Path(
485 "test_foo1", 748 "test_foo1",
@@ -498,6 +761,7 @@ mod tests {
498 ] 761 ]
499 "### 762 "###
500 ); 763 );
764 assert_actions(&runnables, &[&TEST]);
501 } 765 }
502 766
503 #[test] 767 #[test]
@@ -516,7 +780,20 @@ mod tests {
516 @r###" 780 @r###"
517 [ 781 [
518 Runnable { 782 Runnable {
519 range: 1..80, 783 nav: NavigationTarget {
784 file_id: FileId(
785 1,
786 ),
787 full_range: 1..80,
788 name: "test_foo1",
789 kind: FN_DEF,
790 focus_range: Some(
791 66..75,
792 ),
793 container_name: None,
794 description: None,
795 docs: None,
796 },
520 kind: Test { 797 kind: Test {
521 test_id: Path( 798 test_id: Path(
522 "test_foo1", 799 "test_foo1",
@@ -543,6 +820,7 @@ mod tests {
543 ] 820 ]
544 "### 821 "###
545 ); 822 );
823 assert_actions(&runnables, &[&TEST]);
546 } 824 }
547 825
548 #[test] 826 #[test]
diff --git a/crates/ra_ide/src/snapshots/highlight_doctest.html b/crates/ra_ide/src/snapshots/highlight_doctest.html
new file mode 100644
index 000000000..0ae8c7efc
--- /dev/null
+++ b/crates/ra_ide/src/snapshots/highlight_doctest.html
@@ -0,0 +1,71 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.function.unsafe { color: #BC8383; }
14.operator.unsafe { color: #BC8383; }
15.parameter { color: #94BFF3; }
16.text { color: #DCDCCC; }
17.type { color: #7CB8BB; }
18.builtin_type { color: #8CD0D3; }
19.type_param { color: #DFAF8F; }
20.attribute { color: #94BFF3; }
21.numeric_literal { color: #BFEBBF; }
22.bool_literal { color: #BFE6EB; }
23.macro { color: #94BFF3; }
24.module { color: #AFD8AF; }
25.variable { color: #DCDCCC; }
26.format_specifier { color: #CC696B; }
27.mutable { text-decoration: underline; }
28
29.keyword { color: #F0DFAF; font-weight: bold; }
30.keyword.unsafe { color: #BC8383; font-weight: bold; }
31.control { font-style: italic; }
32</style>
33<pre><code><span class="keyword">impl</span> <span class="unresolved_reference">Foo</span> {
34 <span class="comment">/// Constructs a new `Foo`.</span>
35 <span class="comment">///</span>
36 <span class="comment">/// # Examples</span>
37 <span class="comment">///</span>
38 <span class="comment">/// ```</span>
39 <span class="comment">/// #</span> <span class="attribute">#![</span><span class="function attribute">allow</span><span class="attribute">(unused_mut)]</span>
40 <span class="comment">/// </span><span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span>: <span class="unresolved_reference">Foo</span> = <span class="unresolved_reference">Foo</span>::<span class="unresolved_reference">new</span>();
41 <span class="comment">/// ```</span>
42 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span>() -&gt; <span class="unresolved_reference">Foo</span> {
43 <span class="unresolved_reference">Foo</span> { }
44 }
45
46 <span class="comment">/// `bar` method on `Foo`.</span>
47 <span class="comment">///</span>
48 <span class="comment">/// # Examples</span>
49 <span class="comment">///</span>
50 <span class="comment">/// ```</span>
51 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foo</span> = <span class="unresolved_reference">Foo</span>::<span class="unresolved_reference">new</span>();
52 <span class="comment">///</span>
53 <span class="comment">/// </span><span class="comment">// calls bar on foo</span>
54 <span class="comment">/// </span><span class="macro">assert!</span>(foo.bar());
55 <span class="comment">///</span>
56 <span class="comment">/// </span><span class="comment">/* multi-line
57 </span><span class="comment">/// </span><span class="comment"> comment */</span>
58 <span class="comment">///</span>
59 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">multi_line_string</span> = <span class="string_literal">"Foo
60 </span><span class="comment">/// </span><span class="string_literal"> bar
61 </span><span class="comment">/// </span><span class="string_literal"> "</span>;
62 <span class="comment">///</span>
63 <span class="comment">/// ```</span>
64 <span class="comment">///</span>
65 <span class="comment">/// ```</span>
66 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foobar</span> = <span class="unresolved_reference">Foo</span>::<span class="unresolved_reference">new</span>().<span class="unresolved_reference">bar</span>();
67 <span class="comment">/// ```</span>
68 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">bool</span> {
69 <span class="bool_literal">true</span>
70 }
71}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
index 68fc589bc..dec06eb51 100644
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ b/crates/ra_ide/src/snapshots/highlight_injection.html
@@ -10,6 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.function.unsafe { color: #BC8383; }
14.operator.unsafe { color: #BC8383; }
13.parameter { color: #94BFF3; } 15.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 16.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 17.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
index 326744361..849eb3b73 100644
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ b/crates/ra_ide/src/snapshots/highlight_strings.html
@@ -10,6 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.function.unsafe { color: #BC8383; }
14.operator.unsafe { color: #BC8383; }
13.parameter { color: #94BFF3; } 15.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 16.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 17.type { color: #7CB8BB; }
@@ -52,6 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
52 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// =&gt; "test"</span> 54 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// =&gt; "test"</span>
53 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1"</span> 55 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1"</span>
54 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// =&gt; "a 3 b"</span> 56 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// =&gt; "a 3 b"</span>
57 <span class="macro">println!</span>(<span class="string_literal">"{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">}}"</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "{2}"</span>
55 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); 58 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
56 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>); 59 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>);
57 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>); 60 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>);
@@ -61,7 +64,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
61 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); 64 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
62 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); 65 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
63 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); 66 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
64 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="string_literal">}!"</span>, <span class="numeric_literal">27</span>); 67 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>);
65 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); 68 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
66 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>); 69 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>);
67 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>); 70 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>);
diff --git a/crates/ra_ide/src/snapshots/highlight_unsafe.html b/crates/ra_ide/src/snapshots/highlight_unsafe.html
new file mode 100644
index 000000000..bd24e6e38
--- /dev/null
+++ b/crates/ra_ide/src/snapshots/highlight_unsafe.html
@@ -0,0 +1,49 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.function.unsafe { color: #BC8383; }
14.operator.unsafe { color: #BC8383; }
15.parameter { color: #94BFF3; }
16.text { color: #DCDCCC; }
17.type { color: #7CB8BB; }
18.builtin_type { color: #8CD0D3; }
19.type_param { color: #DFAF8F; }
20.attribute { color: #94BFF3; }
21.numeric_literal { color: #BFEBBF; }
22.bool_literal { color: #BFE6EB; }
23.macro { color: #94BFF3; }
24.module { color: #AFD8AF; }
25.variable { color: #DCDCCC; }
26.format_specifier { color: #CC696B; }
27.mutable { text-decoration: underline; }
28
29.keyword { color: #F0DFAF; font-weight: bold; }
30.keyword.unsafe { color: #BC8383; font-weight: bold; }
31.control { font-style: italic; }
32</style>
33<pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span>() {}
34
35<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span>;
36
37<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> {
38 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span>(&<span class="self_keyword">self</span>) {}
39}
40
41<span class="keyword">fn</span> <span class="function declaration">main</span>() {
42 <span class="keyword">let</span> <span class="variable declaration">x</span> = &<span class="numeric_literal">5</span> <span class="keyword">as</span> *<span class="keyword">const</span> <span class="builtin_type">usize</span>;
43 <span class="keyword unsafe">unsafe</span> {
44 <span class="function unsafe">unsafe_fn</span>();
45 <span class="struct">HasUnsafeFn</span>.<span class="function unsafe">unsafe_method</span>();
46 <span class="keyword">let</span> <span class="variable declaration">y</span> = <span class="operator unsafe">*</span>(<span class="variable">x</span>);
47 <span class="keyword">let</span> <span class="variable declaration">z</span> = -<span class="variable">x</span>;
48 }
49}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 352e35095..33548d43c 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -10,6 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.function.unsafe { color: #BC8383; }
14.operator.unsafe { color: #BC8383; }
13.parameter { color: #94BFF3; } 15.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 16.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 17.type { color: #7CB8BB; }
@@ -82,7 +84,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
82 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>; 84 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>;
83 <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>; 85 <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>;
84 86
85 <span class="variable mutable">y</span>; 87 <span class="keyword">let</span> <span class="struct">Foo</span> { <span class="field">x</span>: <span class="variable declaration">z</span>, <span class="field">y</span> } = <span class="struct">Foo</span> { <span class="field">x</span>: <span class="variable">z</span>, <span class="field">y</span> };
88
89 <span class="variable">y</span>;
86} 90}
87 91
88<span class="keyword">enum</span> <span class="enum declaration">Option</span>&lt;<span class="type_param declaration">T</span>&gt; { 92<span class="keyword">enum</span> <span class="enum declaration">Option</span>&lt;<span class="type_param declaration">T</span>&gt; {
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 2a0294f71..1ab06182c 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -10,6 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.function.unsafe { color: #BC8383; }
14.operator.unsafe { color: #BC8383; }
13.parameter { color: #94BFF3; } 15.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 16.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 17.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 0b53ebe69..ab45c364a 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -1,5 +1,6 @@
1mod tags; 1mod tags;
2mod html; 2mod html;
3mod injection;
3#[cfg(test)] 4#[cfg(test)]
4mod tests; 5mod tests;
5 6
@@ -10,14 +11,14 @@ use ra_ide_db::{
10}; 11};
11use ra_prof::profile; 12use ra_prof::profile;
12use ra_syntax::{ 13use ra_syntax::{
13 ast::{self, HasFormatSpecifier, HasQuotes, HasStringValue}, 14 ast::{self, HasFormatSpecifier},
14 AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, 15 AstNode, AstToken, Direction, NodeOrToken, SyntaxElement,
15 SyntaxKind::*, 16 SyntaxKind::*,
16 SyntaxToken, TextRange, WalkEvent, T, 17 TextRange, WalkEvent, T,
17}; 18};
18use rustc_hash::FxHashMap; 19use rustc_hash::FxHashMap;
19 20
20use crate::{call_info::ActiveParameter, Analysis, FileId}; 21use crate::FileId;
21 22
22use ast::FormatSpecifier; 23use ast::FormatSpecifier;
23pub(crate) use html::highlight_as_html; 24pub(crate) use html::highlight_as_html;
@@ -123,6 +124,23 @@ pub(crate) fn highlight(
123 _ => (), 124 _ => (),
124 } 125 }
125 126
127 // Check for Rust code in documentation
128 match &event {
129 WalkEvent::Leave(NodeOrToken::Node(node)) => {
130 if let Some((doctest, range_mapping, new_comments)) =
131 injection::extract_doc_comments(node)
132 {
133 injection::highlight_doc_comment(
134 doctest,
135 range_mapping,
136 new_comments,
137 &mut stack,
138 );
139 }
140 }
141 _ => (),
142 }
143
126 let element = match event { 144 let element = match event {
127 WalkEvent::Enter(it) => it, 145 WalkEvent::Enter(it) => it,
128 WalkEvent::Leave(_) => continue, 146 WalkEvent::Leave(_) => continue,
@@ -173,7 +191,7 @@ pub(crate) fn highlight(
173 191
174 if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) { 192 if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) {
175 let expanded = element_to_highlight.as_token().unwrap().clone(); 193 let expanded = element_to_highlight.as_token().unwrap().clone();
176 if highlight_injection(&mut stack, &sema, token, expanded).is_some() { 194 if injection::highlight_injection(&mut stack, &sema, token, expanded).is_some() {
177 continue; 195 continue;
178 } 196 }
179 } 197 }
@@ -259,9 +277,8 @@ impl HighlightedRangeStack {
259 let mut parent = prev.pop().unwrap(); 277 let mut parent = prev.pop().unwrap();
260 for ele in children { 278 for ele in children {
261 assert!(parent.range.contains_range(ele.range)); 279 assert!(parent.range.contains_range(ele.range));
262 let mut cloned = parent.clone(); 280
263 parent.range = TextRange::new(parent.range.start(), ele.range.start()); 281 let cloned = Self::intersect(&mut parent, &ele);
264 cloned.range = TextRange::new(ele.range.end(), cloned.range.end());
265 if !parent.range.is_empty() { 282 if !parent.range.is_empty() {
266 prev.push(parent); 283 prev.push(parent);
267 } 284 }
@@ -274,6 +291,62 @@ impl HighlightedRangeStack {
274 } 291 }
275 } 292 }
276 293
294 /// Intersects the `HighlightedRange` `parent` with `child`.
295 /// `parent` is mutated in place, becoming the range before `child`.
296 /// Returns the range (of the same type as `parent`) *after* `child`.
297 fn intersect(parent: &mut HighlightedRange, child: &HighlightedRange) -> HighlightedRange {
298 assert!(parent.range.contains_range(child.range));
299
300 let mut cloned = parent.clone();
301 parent.range = TextRange::new(parent.range.start(), child.range.start());
302 cloned.range = TextRange::new(child.range.end(), cloned.range.end());
303
304 cloned
305 }
306
307 /// Similar to `pop`, but can modify arbitrary prior ranges (where `pop`)
308 /// can only modify the last range currently on the stack.
309 /// Can be used to do injections that span multiple ranges, like the
310 /// doctest injection below.
311 /// If `delete` is set to true, the parent range is deleted instead of
312 /// intersected.
313 ///
314 /// Note that `pop` can be simulated by `pop_and_inject(false)` but the
315 /// latter is computationally more expensive.
316 fn pop_and_inject(&mut self, delete: bool) {
317 let mut children = self.stack.pop().unwrap();
318 let prev = self.stack.last_mut().unwrap();
319 children.sort_by_key(|range| range.range.start());
320 prev.sort_by_key(|range| range.range.start());
321
322 for child in children {
323 if let Some(idx) =
324 prev.iter().position(|parent| parent.range.contains_range(child.range))
325 {
326 let cloned = Self::intersect(&mut prev[idx], &child);
327 let insert_idx = if delete || prev[idx].range.is_empty() {
328 prev.remove(idx);
329 idx
330 } else {
331 idx + 1
332 };
333 prev.insert(insert_idx, child);
334 if !delete && !cloned.range.is_empty() {
335 prev.insert(insert_idx + 1, cloned);
336 }
337 } else if let Some(_idx) =
338 prev.iter().position(|parent| parent.range.contains(child.range.start()))
339 {
340 unreachable!("child range should be completely contained in parent range");
341 } else {
342 let idx = prev
343 .binary_search_by_key(&child.range.start(), |range| range.range.start())
344 .unwrap_or_else(|x| x);
345 prev.insert(idx, child);
346 }
347 }
348 }
349
277 fn add(&mut self, range: HighlightedRange) { 350 fn add(&mut self, range: HighlightedRange) {
278 self.stack 351 self.stack
279 .last_mut() 352 .last_mut()
@@ -363,6 +436,7 @@ fn highlight_element(
363 highlight_name(db, def) | HighlightModifier::Definition 436 highlight_name(db, def) | HighlightModifier::Definition
364 } 437 }
365 Some(NameClass::ConstReference(def)) => highlight_name(db, def), 438 Some(NameClass::ConstReference(def)) => highlight_name(db, def),
439 Some(NameClass::FieldShorthand { .. }) => HighlightTag::Field.into(),
366 None => highlight_name_by_syntax(name) | HighlightModifier::Definition, 440 None => highlight_name_by_syntax(name) | HighlightModifier::Definition,
367 } 441 }
368 } 442 }
@@ -406,6 +480,19 @@ fn highlight_element(
406 _ => h, 480 _ => h,
407 } 481 }
408 } 482 }
483 T![*] => {
484 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
485
486 let expr = prefix_expr.expr()?;
487 let ty = sema.type_of_expr(&expr)?;
488 if !ty.is_raw_ptr() {
489 return None;
490 }
491
492 let mut h = Highlight::new(HighlightTag::Operator);
493 h |= HighlightModifier::Unsafe;
494 h
495 }
409 496
410 k if k.is_keyword() => { 497 k if k.is_keyword() => {
411 let h = Highlight::new(HighlightTag::Keyword); 498 let h = Highlight::new(HighlightTag::Keyword);
@@ -458,7 +545,13 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
458 Definition::Field(_) => HighlightTag::Field, 545 Definition::Field(_) => HighlightTag::Field,
459 Definition::ModuleDef(def) => match def { 546 Definition::ModuleDef(def) => match def {
460 hir::ModuleDef::Module(_) => HighlightTag::Module, 547 hir::ModuleDef::Module(_) => HighlightTag::Module,
461 hir::ModuleDef::Function(_) => HighlightTag::Function, 548 hir::ModuleDef::Function(func) => {
549 let mut h = HighlightTag::Function.into();
550 if func.is_unsafe(db) {
551 h |= HighlightModifier::Unsafe;
552 }
553 return h;
554 }
462 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, 555 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
463 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, 556 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum,
464 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, 557 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union,
@@ -516,42 +609,3 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
516 609
517 tag.into() 610 tag.into()
518} 611}
519
520fn highlight_injection(
521 acc: &mut HighlightedRangeStack,
522 sema: &Semantics<RootDatabase>,
523 literal: ast::RawString,
524 expanded: SyntaxToken,
525) -> Option<()> {
526 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
527 if !active_parameter.name.starts_with("ra_fixture") {
528 return None;
529 }
530 let value = literal.value()?;
531 let (analysis, tmp_file_id) = Analysis::from_single_file(value);
532
533 if let Some(range) = literal.open_quote_text_range() {
534 acc.add(HighlightedRange {
535 range,
536 highlight: HighlightTag::StringLiteral.into(),
537 binding_hash: None,
538 })
539 }
540
541 for mut h in analysis.highlight(tmp_file_id).unwrap() {
542 if let Some(r) = literal.map_range_up(h.range) {
543 h.range = r;
544 acc.add(h)
545 }
546 }
547
548 if let Some(range) = literal.close_quote_text_range() {
549 acc.add(HighlightedRange {
550 range,
551 highlight: HighlightTag::StringLiteral.into(),
552 binding_hash: None,
553 })
554 }
555
556 Some(())
557}
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index edfe61f39..5bada6252 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -69,6 +69,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
69.string_literal { color: #CC9393; } 69.string_literal { color: #CC9393; }
70.field { color: #94BFF3; } 70.field { color: #94BFF3; }
71.function { color: #93E0E3; } 71.function { color: #93E0E3; }
72.function.unsafe { color: #BC8383; }
73.operator.unsafe { color: #BC8383; }
72.parameter { color: #94BFF3; } 74.parameter { color: #94BFF3; }
73.text { color: #DCDCCC; } 75.text { color: #DCDCCC; }
74.type { color: #7CB8BB; } 76.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/syntax_highlighting/injection.rs b/crates/ra_ide/src/syntax_highlighting/injection.rs
new file mode 100644
index 000000000..3575a0fc6
--- /dev/null
+++ b/crates/ra_ide/src/syntax_highlighting/injection.rs
@@ -0,0 +1,168 @@
1//! Syntax highlighting injections such as highlighting of documentation tests.
2
3use std::{collections::BTreeMap, convert::TryFrom};
4
5use ast::{HasQuotes, HasStringValue};
6use hir::Semantics;
7use ra_syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
8use stdx::SepBy;
9
10use crate::{call_info::ActiveParameter, Analysis, HighlightTag, HighlightedRange, RootDatabase};
11
12use super::HighlightedRangeStack;
13
14pub(super) fn highlight_injection(
15 acc: &mut HighlightedRangeStack,
16 sema: &Semantics<RootDatabase>,
17 literal: ast::RawString,
18 expanded: SyntaxToken,
19) -> Option<()> {
20 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
21 if !active_parameter.name.starts_with("ra_fixture") {
22 return None;
23 }
24 let value = literal.value()?;
25 let (analysis, tmp_file_id) = Analysis::from_single_file(value);
26
27 if let Some(range) = literal.open_quote_text_range() {
28 acc.add(HighlightedRange {
29 range,
30 highlight: HighlightTag::StringLiteral.into(),
31 binding_hash: None,
32 })
33 }
34
35 for mut h in analysis.highlight(tmp_file_id).unwrap() {
36 if let Some(r) = literal.map_range_up(h.range) {
37 h.range = r;
38 acc.add(h)
39 }
40 }
41
42 if let Some(range) = literal.close_quote_text_range() {
43 acc.add(HighlightedRange {
44 range,
45 highlight: HighlightTag::StringLiteral.into(),
46 binding_hash: None,
47 })
48 }
49
50 Some(())
51}
52
53/// Mapping from extracted documentation code to original code
54type RangesMap = BTreeMap<TextSize, TextSize>;
55
56/// Extracts Rust code from documentation comments as well as a mapping from
57/// the extracted source code back to the original source ranges.
58/// Lastly, a vector of new comment highlight ranges (spanning only the
59/// comment prefix) is returned which is used in the syntax highlighting
60/// injection to replace the previous (line-spanning) comment ranges.
61pub(super) fn extract_doc_comments(
62 node: &SyntaxNode,
63) -> Option<(String, RangesMap, Vec<HighlightedRange>)> {
64 // wrap the doctest into function body to get correct syntax highlighting
65 let prefix = "fn doctest() {\n";
66 let suffix = "}\n";
67 // Mapping from extracted documentation code to original code
68 let mut range_mapping: RangesMap = BTreeMap::new();
69 let mut line_start = TextSize::try_from(prefix.len()).unwrap();
70 let mut is_doctest = false;
71 // Replace the original, line-spanning comment ranges by new, only comment-prefix
72 // spanning comment ranges.
73 let mut new_comments = Vec::new();
74 let doctest = node
75 .children_with_tokens()
76 .filter_map(|el| el.into_token().and_then(ast::Comment::cast))
77 .filter(|comment| comment.kind().doc.is_some())
78 .filter(|comment| {
79 if comment.text().contains("```") {
80 is_doctest = !is_doctest;
81 false
82 } else {
83 is_doctest
84 }
85 })
86 .map(|comment| {
87 let prefix_len = comment.prefix().len();
88 let line: &str = comment.text().as_str();
89 let range = comment.syntax().text_range();
90
91 // whitespace after comment is ignored
92 let pos = if let Some(ws) = line.chars().nth(prefix_len).filter(|c| c.is_whitespace()) {
93 prefix_len + ws.len_utf8()
94 } else {
95 prefix_len
96 };
97
98 // lines marked with `#` should be ignored in output, we skip the `#` char
99 let pos = if let Some(ws) = line.chars().nth(pos).filter(|&c| c == '#') {
100 pos + ws.len_utf8()
101 } else {
102 pos
103 };
104
105 range_mapping.insert(line_start, range.start() + TextSize::try_from(pos).unwrap());
106 new_comments.push(HighlightedRange {
107 range: TextRange::new(
108 range.start(),
109 range.start() + TextSize::try_from(pos).unwrap(),
110 ),
111 highlight: HighlightTag::Comment.into(),
112 binding_hash: None,
113 });
114 line_start += range.len() - TextSize::try_from(pos).unwrap();
115 line_start += TextSize::try_from('\n'.len_utf8()).unwrap();
116
117 line[pos..].to_owned()
118 })
119 .sep_by("\n")
120 .to_string();
121
122 if doctest.is_empty() {
123 return None;
124 }
125
126 let doctest = format!("{}{}{}", prefix, doctest, suffix);
127 Some((doctest, range_mapping, new_comments))
128}
129
130/// Injection of syntax highlighting of doctests.
131pub(super) fn highlight_doc_comment(
132 text: String,
133 range_mapping: RangesMap,
134 new_comments: Vec<HighlightedRange>,
135 stack: &mut HighlightedRangeStack,
136) {
137 let (analysis, tmp_file_id) = Analysis::from_single_file(text);
138
139 stack.push();
140 for mut h in analysis.highlight(tmp_file_id).unwrap() {
141 // Determine start offset and end offset in case of multi-line ranges
142 let mut start_offset = None;
143 let mut end_offset = None;
144 for (line_start, orig_line_start) in range_mapping.range(..h.range.end()).rev() {
145 if line_start <= &h.range.start() {
146 start_offset.get_or_insert(orig_line_start - line_start);
147 break;
148 } else {
149 end_offset.get_or_insert(orig_line_start - line_start);
150 }
151 }
152 if let Some(start_offset) = start_offset {
153 h.range = TextRange::new(
154 h.range.start() + start_offset,
155 h.range.end() + end_offset.unwrap_or(start_offset),
156 );
157 stack.add(h);
158 }
159 }
160
161 // Inject the comment prefix highlight ranges
162 stack.push();
163 for comment in new_comments {
164 stack.add(comment);
165 }
166 stack.pop_and_inject(false);
167 stack.pop_and_inject(true);
168}
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 1514531de..94f466966 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -24,12 +24,14 @@ pub enum HighlightTag {
24 Enum, 24 Enum,
25 EnumVariant, 25 EnumVariant,
26 Field, 26 Field,
27 FormatSpecifier,
27 Function, 28 Function,
28 Keyword, 29 Keyword,
29 Lifetime, 30 Lifetime,
30 Macro, 31 Macro,
31 Module, 32 Module,
32 NumericLiteral, 33 NumericLiteral,
34 Operator,
33 SelfKeyword, 35 SelfKeyword,
34 SelfType, 36 SelfType,
35 Static, 37 Static,
@@ -41,8 +43,6 @@ pub enum HighlightTag {
41 Union, 43 Union,
42 Local, 44 Local,
43 UnresolvedReference, 45 UnresolvedReference,
44 FormatSpecifier,
45 Operator,
46} 46}
47 47
48#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 48#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -72,12 +72,14 @@ impl HighlightTag {
72 HighlightTag::Enum => "enum", 72 HighlightTag::Enum => "enum",
73 HighlightTag::EnumVariant => "enum_variant", 73 HighlightTag::EnumVariant => "enum_variant",
74 HighlightTag::Field => "field", 74 HighlightTag::Field => "field",
75 HighlightTag::FormatSpecifier => "format_specifier",
75 HighlightTag::Function => "function", 76 HighlightTag::Function => "function",
76 HighlightTag::Keyword => "keyword", 77 HighlightTag::Keyword => "keyword",
77 HighlightTag::Lifetime => "lifetime", 78 HighlightTag::Lifetime => "lifetime",
78 HighlightTag::Macro => "macro", 79 HighlightTag::Macro => "macro",
79 HighlightTag::Module => "module", 80 HighlightTag::Module => "module",
80 HighlightTag::NumericLiteral => "numeric_literal", 81 HighlightTag::NumericLiteral => "numeric_literal",
82 HighlightTag::Operator => "operator",
81 HighlightTag::SelfKeyword => "self_keyword", 83 HighlightTag::SelfKeyword => "self_keyword",
82 HighlightTag::SelfType => "self_type", 84 HighlightTag::SelfType => "self_type",
83 HighlightTag::Static => "static", 85 HighlightTag::Static => "static",
@@ -89,8 +91,6 @@ impl HighlightTag {
89 HighlightTag::Union => "union", 91 HighlightTag::Union => "union",
90 HighlightTag::Local => "variable", 92 HighlightTag::Local => "variable",
91 HighlightTag::UnresolvedReference => "unresolved_reference", 93 HighlightTag::UnresolvedReference => "unresolved_reference",
92 HighlightTag::FormatSpecifier => "format_specifier",
93 HighlightTag::Operator => "operator",
94 } 94 }
95 } 95 }
96} 96}
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index eb43a23da..949bf59a0 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -9,7 +9,7 @@ use crate::{
9 9
10#[test] 10#[test]
11fn test_highlighting() { 11fn test_highlighting() {
12 let (analysis, file_id) = single_file( 12 check_highlighting(
13 r#" 13 r#"
14#[derive(Clone, Debug)] 14#[derive(Clone, Debug)]
15struct Foo { 15struct Foo {
@@ -65,6 +65,8 @@ fn main() {
65 let y = &mut x; 65 let y = &mut x;
66 let z = &y; 66 let z = &y;
67 67
68 let Foo { x: z, y } = Foo { x: z, y };
69
68 y; 70 y;
69} 71}
70 72
@@ -84,17 +86,14 @@ impl<T> Option<T> {
84} 86}
85"# 87"#
86 .trim(), 88 .trim(),
89 "crates/ra_ide/src/snapshots/highlighting.html",
90 false,
87 ); 91 );
88 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlighting.html");
89 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
90 let expected_html = &read_text(&dst_file);
91 fs::write(dst_file, &actual_html).unwrap();
92 assert_eq_text!(expected_html, actual_html);
93} 92}
94 93
95#[test] 94#[test]
96fn test_rainbow_highlighting() { 95fn test_rainbow_highlighting() {
97 let (analysis, file_id) = single_file( 96 check_highlighting(
98 r#" 97 r#"
99fn main() { 98fn main() {
100 let hello = "hello"; 99 let hello = "hello";
@@ -110,12 +109,9 @@ fn bar() {
110} 109}
111"# 110"#
112 .trim(), 111 .trim(),
112 "crates/ra_ide/src/snapshots/rainbow_highlighting.html",
113 true,
113 ); 114 );
114 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/rainbow_highlighting.html");
115 let actual_html = &analysis.highlight_as_html(file_id, true).unwrap();
116 let expected_html = &read_text(&dst_file);
117 fs::write(dst_file, &actual_html).unwrap();
118 assert_eq_text!(expected_html, actual_html);
119} 115}
120 116
121#[test] 117#[test]
@@ -153,7 +149,7 @@ fn test_ranges() {
153 149
154#[test] 150#[test]
155fn test_flattening() { 151fn test_flattening() {
156 let (analysis, file_id) = single_file( 152 check_highlighting(
157 r##" 153 r##"
158fn fixture(ra_fixture: &str) {} 154fn fixture(ra_fixture: &str) {}
159 155
@@ -167,13 +163,9 @@ fn main() {
167 ); 163 );
168}"## 164}"##
169 .trim(), 165 .trim(),
166 "crates/ra_ide/src/snapshots/highlight_injection.html",
167 false,
170 ); 168 );
171
172 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_injection.html");
173 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
174 let expected_html = &read_text(&dst_file);
175 fs::write(dst_file, &actual_html).unwrap();
176 assert_eq_text!(expected_html, actual_html);
177} 169}
178 170
179#[test] 171#[test]
@@ -192,7 +184,7 @@ macro_rules! test {}
192fn test_string_highlighting() { 184fn test_string_highlighting() {
193 // The format string detection is based on macro-expansion, 185 // The format string detection is based on macro-expansion,
194 // thus, we have to copy the macro definition from `std` 186 // thus, we have to copy the macro definition from `std`
195 let (analysis, file_id) = single_file( 187 check_highlighting(
196 r#" 188 r#"
197macro_rules! println { 189macro_rules! println {
198 ($($arg:tt)*) => ({ 190 ($($arg:tt)*) => ({
@@ -218,6 +210,7 @@ fn main() {
218 println!("{argument}", argument = "test"); // => "test" 210 println!("{argument}", argument = "test"); // => "test"
219 println!("{name} {}", 1, name = 2); // => "2 1" 211 println!("{name} {}", 1, name = 2); // => "2 1"
220 println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" 212 println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b"
213 println!("{{{}}}", 2); // => "{2}"
221 println!("Hello {:5}!", "x"); 214 println!("Hello {:5}!", "x");
222 println!("Hello {:1$}!", "x", 5); 215 println!("Hello {:1$}!", "x", 5);
223 println!("Hello {1:0$}!", 5, "x"); 216 println!("Hello {1:0$}!", 5, "x");
@@ -249,10 +242,96 @@ fn main() {
249 println!("{ничоси}", ничоси = 92); 242 println!("{ничоси}", ничоси = 92);
250}"# 243}"#
251 .trim(), 244 .trim(),
245 "crates/ra_ide/src/snapshots/highlight_strings.html",
246 false,
252 ); 247 );
248}
249
250#[test]
251fn test_unsafe_highlighting() {
252 check_highlighting(
253 r#"
254unsafe fn unsafe_fn() {}
255
256struct HasUnsafeFn;
257
258impl HasUnsafeFn {
259 unsafe fn unsafe_method(&self) {}
260}
261
262fn main() {
263 let x = &5 as *const usize;
264 unsafe {
265 unsafe_fn();
266 HasUnsafeFn.unsafe_method();
267 let y = *(x);
268 let z = -x;
269 }
270}
271"#
272 .trim(),
273 "crates/ra_ide/src/snapshots/highlight_unsafe.html",
274 false,
275 );
276}
277
278#[test]
279fn test_highlight_doctest() {
280 check_highlighting(
281 r#"
282impl Foo {
283 /// Constructs a new `Foo`.
284 ///
285 /// # Examples
286 ///
287 /// ```
288 /// # #![allow(unused_mut)]
289 /// let mut foo: Foo = Foo::new();
290 /// ```
291 pub const fn new() -> Foo {
292 Foo { }
293 }
294
295 /// `bar` method on `Foo`.
296 ///
297 /// # Examples
298 ///
299 /// ```
300 /// let foo = Foo::new();
301 ///
302 /// // calls bar on foo
303 /// assert!(foo.bar());
304 ///
305 /// /* multi-line
306 /// comment */
307 ///
308 /// let multi_line_string = "Foo
309 /// bar
310 /// ";
311 ///
312 /// ```
313 ///
314 /// ```
315 /// let foobar = Foo::new().bar();
316 /// ```
317 pub fn foo(&self) -> bool {
318 true
319 }
320}
321"#
322 .trim(),
323 "crates/ra_ide/src/snapshots/highlight_doctest.html",
324 false,
325 )
326}
253 327
254 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_strings.html"); 328/// Highlights the code given by the `ra_fixture` argument, renders the
255 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); 329/// result as HTML, and compares it with the HTML file given as `snapshot`.
330/// Note that the `snapshot` file is overwritten by the rendered HTML.
331fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) {
332 let (analysis, file_id) = single_file(ra_fixture);
333 let dst_file = project_dir().join(snapshot);
334 let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
256 let expected_html = &read_text(&dst_file); 335 let expected_html = &read_text(&dst_file);
257 fs::write(dst_file, &actual_html).unwrap(); 336 fs::write(dst_file, &actual_html).unwrap();
258 assert_eq_text!(expected_html, actual_html); 337 assert_eq_text!(expected_html, actual_html);
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index 67e2c33a0..83776d2b6 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -17,11 +17,13 @@ mod on_enter;
17 17
18use ra_db::{FilePosition, SourceDatabase}; 18use ra_db::{FilePosition, SourceDatabase};
19use ra_fmt::leading_indent; 19use ra_fmt::leading_indent;
20use ra_ide_db::RootDatabase; 20use ra_ide_db::{source_change::SourceFileEdit, RootDatabase};
21use ra_syntax::{ 21use ra_syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
23 ast::{self, AstToken}, 23 ast::{self, AstToken},
24 AstNode, SourceFile, TextRange, TextSize, 24 AstNode, SourceFile,
25 SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR},
26 TextRange, TextSize,
25}; 27};
26 28
27use ra_text_edit::TextEdit; 29use ra_text_edit::TextEdit;
@@ -47,8 +49,8 @@ pub(crate) fn on_char_typed(
47 assert!(TRIGGER_CHARS.contains(char_typed)); 49 assert!(TRIGGER_CHARS.contains(char_typed));
48 let file = &db.parse(position.file_id).tree(); 50 let file = &db.parse(position.file_id).tree();
49 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); 51 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed));
50 let text_edit = on_char_typed_inner(file, position.offset, char_typed)?; 52 let edit = on_char_typed_inner(file, position.offset, char_typed)?;
51 Some(SourceChange::source_file_edit_from(position.file_id, text_edit)) 53 Some(SourceFileEdit { file_id: position.file_id, edit }.into())
52} 54}
53 55
54fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> { 56fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> {
@@ -98,9 +100,12 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
98 }; 100 };
99 let current_indent_len = TextSize::of(current_indent); 101 let current_indent_len = TextSize::of(current_indent);
100 102
103 let parent = whitespace.syntax().parent();
101 // Make sure dot is a part of call chain 104 // Make sure dot is a part of call chain
102 let field_expr = ast::FieldExpr::cast(whitespace.syntax().parent())?; 105 if !matches!(parent.kind(), FIELD_EXPR | METHOD_CALL_EXPR) {
103 let prev_indent = leading_indent(field_expr.syntax())?; 106 return None;
107 }
108 let prev_indent = leading_indent(&parent)?;
104 let target_indent = format!(" {}", prev_indent); 109 let target_indent = format!(" {}", prev_indent);
105 let target_indent_len = TextSize::of(&target_indent); 110 let target_indent_len = TextSize::of(&target_indent);
106 if current_indent_len == target_indent_len { 111 if current_indent_len == target_indent_len {
@@ -143,11 +148,11 @@ mod tests {
143 }) 148 })
144 } 149 }
145 150
146 fn type_char(char_typed: char, before: &str, after: &str) { 151 fn type_char(char_typed: char, ra_fixture_before: &str, ra_fixture_after: &str) {
147 let actual = do_type_char(char_typed, before) 152 let actual = do_type_char(char_typed, ra_fixture_before)
148 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); 153 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));
149 154
150 assert_eq_text!(after, &actual); 155 assert_eq_text!(ra_fixture_after, &actual);
151 } 156 }
152 157
153 fn type_char_noop(char_typed: char, before: &str) { 158 fn type_char_noop(char_typed: char, before: &str) {
@@ -249,6 +254,27 @@ fn foo() {
249 } 254 }
250 255
251 #[test] 256 #[test]
257 fn indents_new_chain_call_with_let() {
258 type_char(
259 '.',
260 r#"
261fn main() {
262 let _ = foo
263 <|>
264 bar()
265}
266"#,
267 r#"
268fn main() {
269 let _ = foo
270 .
271 bar()
272}
273"#,
274 );
275 }
276
277 #[test]
252 fn indents_continued_chain_call() { 278 fn indents_continued_chain_call() {
253 type_char( 279 type_char(
254 '.', 280 '.',
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs
index 8446ef88e..2fc796a85 100644
--- a/crates/ra_ide_db/src/change.rs
+++ b/crates/ra_ide_db/src/change.rs
@@ -16,7 +16,7 @@ use rustc_hash::FxHashMap;
16 16
17use crate::{ 17use crate::{
18 symbol_index::{SymbolIndex, SymbolsDatabase}, 18 symbol_index::{SymbolIndex, SymbolsDatabase},
19 DebugData, RootDatabase, 19 RootDatabase,
20}; 20};
21 21
22#[derive(Default)] 22#[derive(Default)]
@@ -26,7 +26,6 @@ pub struct AnalysisChange {
26 files_changed: Vec<(FileId, Arc<String>)>, 26 files_changed: Vec<(FileId, Arc<String>)>,
27 libraries_added: Vec<LibraryData>, 27 libraries_added: Vec<LibraryData>,
28 crate_graph: Option<CrateGraph>, 28 crate_graph: Option<CrateGraph>,
29 debug_data: DebugData,
30} 29}
31 30
32impl fmt::Debug for AnalysisChange { 31impl fmt::Debug for AnalysisChange {
@@ -87,10 +86,6 @@ impl AnalysisChange {
87 pub fn set_crate_graph(&mut self, graph: CrateGraph) { 86 pub fn set_crate_graph(&mut self, graph: CrateGraph) {
88 self.crate_graph = Some(graph); 87 self.crate_graph = Some(graph);
89 } 88 }
90
91 pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) {
92 self.debug_data.root_paths.insert(source_root_id, path);
93 }
94} 89}
95 90
96#[derive(Debug)] 91#[derive(Debug)]
@@ -218,8 +213,6 @@ impl RootDatabase {
218 if let Some(crate_graph) = change.crate_graph { 213 if let Some(crate_graph) = change.crate_graph {
219 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) 214 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
220 } 215 }
221
222 Arc::make_mut(&mut self.debug_data).merge(change.debug_data)
223 } 216 }
224 217
225 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) { 218 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) {
@@ -334,6 +327,7 @@ impl RootDatabase {
334 hir::db::CrateLangItemsQuery 327 hir::db::CrateLangItemsQuery
335 hir::db::LangItemQuery 328 hir::db::LangItemQuery
336 hir::db::DocumentationQuery 329 hir::db::DocumentationQuery
330 hir::db::ImportMapQuery
337 331
338 // InternDatabase 332 // InternDatabase
339 hir::db::InternFunctionQuery 333 hir::db::InternFunctionQuery
@@ -369,6 +363,7 @@ impl RootDatabase {
369 hir::db::ImplDatumQuery 363 hir::db::ImplDatumQuery
370 hir::db::AssociatedTyValueQuery 364 hir::db::AssociatedTyValueQuery
371 hir::db::TraitSolveQuery 365 hir::db::TraitSolveQuery
366 hir::db::ReturnTypeImplTraitsQuery
372 367
373 // SymbolsDatabase 368 // SymbolsDatabase
374 crate::symbol_index::FileSymbolsQuery 369 crate::symbol_index::FileSymbolsQuery
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index 8b06cbfc5..3ef5e74b6 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -18,7 +18,7 @@ use ra_syntax::{
18use crate::RootDatabase; 18use crate::RootDatabase;
19 19
20// FIXME: a more precise name would probably be `Symbol`? 20// FIXME: a more precise name would probably be `Symbol`?
21#[derive(Debug, PartialEq, Eq)] 21#[derive(Debug, PartialEq, Eq, Copy, Clone)]
22pub enum Definition { 22pub enum Definition {
23 Macro(MacroDef), 23 Macro(MacroDef),
24 Field(Field), 24 Field(Field),
@@ -78,10 +78,15 @@ impl Definition {
78 } 78 }
79} 79}
80 80
81#[derive(Debug)]
81pub enum NameClass { 82pub enum NameClass {
82 Definition(Definition), 83 Definition(Definition),
83 /// `None` in `if let None = Some(82) {}` 84 /// `None` in `if let None = Some(82) {}`
84 ConstReference(Definition), 85 ConstReference(Definition),
86 FieldShorthand {
87 local: Local,
88 field: Definition,
89 },
85} 90}
86 91
87impl NameClass { 92impl NameClass {
@@ -89,12 +94,14 @@ impl NameClass {
89 match self { 94 match self {
90 NameClass::Definition(it) => Some(it), 95 NameClass::Definition(it) => Some(it),
91 NameClass::ConstReference(_) => None, 96 NameClass::ConstReference(_) => None,
97 NameClass::FieldShorthand { local, field: _ } => Some(Definition::Local(local)),
92 } 98 }
93 } 99 }
94 100
95 pub fn definition(self) -> Definition { 101 pub fn definition(self) -> Definition {
96 match self { 102 match self {
97 NameClass::Definition(it) | NameClass::ConstReference(it) => it, 103 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
104 NameClass::FieldShorthand { local: _, field } => field,
98 } 105 }
99 } 106 }
100} 107}
@@ -102,18 +109,14 @@ impl NameClass {
102pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> { 109pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
103 let _p = profile("classify_name"); 110 let _p = profile("classify_name");
104 111
105 if let Some(bind_pat) = name.syntax().parent().and_then(ast::BindPat::cast) { 112 let parent = name.syntax().parent()?;
113
114 if let Some(bind_pat) = ast::BindPat::cast(parent.clone()) {
106 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) { 115 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
107 return Some(NameClass::ConstReference(Definition::ModuleDef(def))); 116 return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
108 } 117 }
109 } 118 }
110 119
111 classify_name_inner(sema, name).map(NameClass::Definition)
112}
113
114fn classify_name_inner(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<Definition> {
115 let parent = name.syntax().parent()?;
116
117 match_ast! { 120 match_ast! {
118 match parent { 121 match parent {
119 ast::Alias(it) => { 122 ast::Alias(it) => {
@@ -123,63 +126,73 @@ fn classify_name_inner(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Opti
123 let name_ref = path_segment.name_ref()?; 126 let name_ref = path_segment.name_ref()?;
124 let name_ref_class = classify_name_ref(sema, &name_ref)?; 127 let name_ref_class = classify_name_ref(sema, &name_ref)?;
125 128
126 Some(name_ref_class.definition()) 129 Some(NameClass::Definition(name_ref_class.definition()))
127 }, 130 },
128 ast::BindPat(it) => { 131 ast::BindPat(it) => {
129 let local = sema.to_def(&it)?; 132 let local = sema.to_def(&it)?;
130 Some(Definition::Local(local)) 133
134 if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordFieldPat::cast) {
135 if record_field_pat.name_ref().is_none() {
136 if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
137 let field = Definition::Field(field);
138 return Some(NameClass::FieldShorthand { local, field });
139 }
140 }
141 }
142
143 Some(NameClass::Definition(Definition::Local(local)))
131 }, 144 },
132 ast::RecordFieldDef(it) => { 145 ast::RecordFieldDef(it) => {
133 let field: hir::Field = sema.to_def(&it)?; 146 let field: hir::Field = sema.to_def(&it)?;
134 Some(Definition::Field(field)) 147 Some(NameClass::Definition(Definition::Field(field)))
135 }, 148 },
136 ast::Module(it) => { 149 ast::Module(it) => {
137 let def = sema.to_def(&it)?; 150 let def = sema.to_def(&it)?;
138 Some(Definition::ModuleDef(def.into())) 151 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
139 }, 152 },
140 ast::StructDef(it) => { 153 ast::StructDef(it) => {
141 let def: hir::Struct = sema.to_def(&it)?; 154 let def: hir::Struct = sema.to_def(&it)?;
142 Some(Definition::ModuleDef(def.into())) 155 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
143 }, 156 },
144 ast::UnionDef(it) => { 157 ast::UnionDef(it) => {
145 let def: hir::Union = sema.to_def(&it)?; 158 let def: hir::Union = sema.to_def(&it)?;
146 Some(Definition::ModuleDef(def.into())) 159 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
147 }, 160 },
148 ast::EnumDef(it) => { 161 ast::EnumDef(it) => {
149 let def: hir::Enum = sema.to_def(&it)?; 162 let def: hir::Enum = sema.to_def(&it)?;
150 Some(Definition::ModuleDef(def.into())) 163 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
151 }, 164 },
152 ast::TraitDef(it) => { 165 ast::TraitDef(it) => {
153 let def: hir::Trait = sema.to_def(&it)?; 166 let def: hir::Trait = sema.to_def(&it)?;
154 Some(Definition::ModuleDef(def.into())) 167 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
155 }, 168 },
156 ast::StaticDef(it) => { 169 ast::StaticDef(it) => {
157 let def: hir::Static = sema.to_def(&it)?; 170 let def: hir::Static = sema.to_def(&it)?;
158 Some(Definition::ModuleDef(def.into())) 171 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
159 }, 172 },
160 ast::EnumVariant(it) => { 173 ast::EnumVariant(it) => {
161 let def: hir::EnumVariant = sema.to_def(&it)?; 174 let def: hir::EnumVariant = sema.to_def(&it)?;
162 Some(Definition::ModuleDef(def.into())) 175 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
163 }, 176 },
164 ast::FnDef(it) => { 177 ast::FnDef(it) => {
165 let def: hir::Function = sema.to_def(&it)?; 178 let def: hir::Function = sema.to_def(&it)?;
166 Some(Definition::ModuleDef(def.into())) 179 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
167 }, 180 },
168 ast::ConstDef(it) => { 181 ast::ConstDef(it) => {
169 let def: hir::Const = sema.to_def(&it)?; 182 let def: hir::Const = sema.to_def(&it)?;
170 Some(Definition::ModuleDef(def.into())) 183 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
171 }, 184 },
172 ast::TypeAliasDef(it) => { 185 ast::TypeAliasDef(it) => {
173 let def: hir::TypeAlias = sema.to_def(&it)?; 186 let def: hir::TypeAlias = sema.to_def(&it)?;
174 Some(Definition::ModuleDef(def.into())) 187 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
175 }, 188 },
176 ast::MacroCall(it) => { 189 ast::MacroCall(it) => {
177 let def = sema.to_def(&it)?; 190 let def = sema.to_def(&it)?;
178 Some(Definition::Macro(def)) 191 Some(NameClass::Definition(Definition::Macro(def)))
179 }, 192 },
180 ast::TypeParam(it) => { 193 ast::TypeParam(it) => {
181 let def = sema.to_def(&it)?; 194 let def = sema.to_def(&it)?;
182 Some(Definition::TypeParam(def)) 195 Some(NameClass::Definition(Definition::TypeParam(def)))
183 }, 196 },
184 _ => None, 197 _ => None,
185 } 198 }
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs
index bf0d8db60..fff112e66 100644
--- a/crates/ra_ide_db/src/imports_locator.rs
+++ b/crates/ra_ide_db/src/imports_locator.rs
@@ -1,7 +1,7 @@
1//! This module contains an import search funcionality that is provided to the ra_assists module. 1//! This module contains an import search funcionality that is provided to the ra_assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.
3 3
4use hir::{MacroDef, ModuleDef, Semantics}; 4use hir::{Crate, MacroDef, ModuleDef, Semantics};
5use ra_prof::profile; 5use ra_prof::profile;
6use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; 6use ra_syntax::{ast, AstNode, SyntaxKind::NAME};
7 7
@@ -11,44 +11,46 @@ use crate::{
11 RootDatabase, 11 RootDatabase,
12}; 12};
13use either::Either; 13use either::Either;
14use rustc_hash::FxHashSet;
14 15
15pub struct ImportsLocator<'a> { 16pub struct ImportsLocator<'a> {
16 sema: Semantics<'a, RootDatabase>, 17 sema: Semantics<'a, RootDatabase>,
18 krate: Crate,
17} 19}
18 20
19impl<'a> ImportsLocator<'a> { 21impl<'a> ImportsLocator<'a> {
20 pub fn new(db: &'a RootDatabase) -> Self { 22 pub fn new(db: &'a RootDatabase, krate: Crate) -> Self {
21 Self { sema: Semantics::new(db) } 23 Self { sema: Semantics::new(db), krate }
22 } 24 }
23 25
24 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> { 26 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> {
25 let _p = profile("search_for_imports"); 27 let _p = profile("search_for_imports");
26 let db = self.sema.db; 28 let db = self.sema.db;
27 29
28 let project_results = { 30 // Query dependencies first.
29 let mut query = Query::new(name_to_import.to_string()); 31 let mut candidates: FxHashSet<_> =
30 query.exact(); 32 self.krate.query_external_importables(db, name_to_import).collect();
31 query.limit(40); 33
32 symbol_index::world_symbols(db, query) 34 // Query the local crate using the symbol index.
33 }; 35 let local_results = {
34 let lib_results = {
35 let mut query = Query::new(name_to_import.to_string()); 36 let mut query = Query::new(name_to_import.to_string());
36 query.libs();
37 query.exact(); 37 query.exact();
38 query.limit(40); 38 query.limit(40);
39 symbol_index::world_symbols(db, query) 39 symbol_index::crate_symbols(db, self.krate.into(), query)
40 }; 40 };
41 41
42 project_results 42 candidates.extend(
43 .into_iter() 43 local_results
44 .chain(lib_results.into_iter()) 44 .into_iter()
45 .filter_map(|import_candidate| self.get_name_definition(&import_candidate)) 45 .filter_map(|import_candidate| self.get_name_definition(&import_candidate))
46 .filter_map(|name_definition_to_import| match name_definition_to_import { 46 .filter_map(|name_definition_to_import| match name_definition_to_import {
47 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), 47 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
48 Definition::Macro(macro_def) => Some(Either::Right(macro_def)), 48 Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
49 _ => None, 49 _ => None,
50 }) 50 }),
51 .collect() 51 );
52
53 candidates.into_iter().collect()
52 } 54 }
53 55
54 fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<Definition> { 56 fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<Definition> {
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index 1b74e6558..a808de4f1 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -16,10 +16,10 @@ use std::sync::Arc;
16use hir::db::{AstDatabase, DefDatabase}; 16use hir::db::{AstDatabase, DefDatabase};
17use ra_db::{ 17use ra_db::{
18 salsa::{self, Database, Durability}, 18 salsa::{self, Database, Durability},
19 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, 19 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase,
20 SourceDatabase, SourceRootId, Upcast, 20 Upcast,
21}; 21};
22use rustc_hash::FxHashMap; 22use rustc_hash::FxHashSet;
23 23
24use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase}; 24use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
25 25
@@ -36,7 +36,6 @@ use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
36#[derive(Debug)] 36#[derive(Debug)]
37pub struct RootDatabase { 37pub struct RootDatabase {
38 runtime: salsa::Runtime<RootDatabase>, 38 runtime: salsa::Runtime<RootDatabase>,
39 pub(crate) debug_data: Arc<DebugData>,
40 pub last_gc: crate::wasm_shims::Instant, 39 pub last_gc: crate::wasm_shims::Instant,
41 pub last_gc_check: crate::wasm_shims::Instant, 40 pub last_gc_check: crate::wasm_shims::Instant,
42} 41}
@@ -57,23 +56,12 @@ impl FileLoader for RootDatabase {
57 fn file_text(&self, file_id: FileId) -> Arc<String> { 56 fn file_text(&self, file_id: FileId) -> Arc<String> {
58 FileLoaderDelegate(self).file_text(file_id) 57 FileLoaderDelegate(self).file_text(file_id)
59 } 58 }
60 fn resolve_relative_path( 59 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
61 &self, 60 FileLoaderDelegate(self).resolve_path(anchor, path)
62 anchor: FileId,
63 relative_path: &RelativePath,
64 ) -> Option<FileId> {
65 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
66 } 61 }
67 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 62 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
68 FileLoaderDelegate(self).relevant_crates(file_id) 63 FileLoaderDelegate(self).relevant_crates(file_id)
69 } 64 }
70 fn resolve_extern_path(
71 &self,
72 extern_id: ra_db::ExternSourceId,
73 relative_path: &RelativePath,
74 ) -> Option<FileId> {
75 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
76 }
77} 65}
78 66
79impl salsa::Database for RootDatabase { 67impl salsa::Database for RootDatabase {
@@ -109,7 +97,6 @@ impl RootDatabase {
109 runtime: salsa::Runtime::default(), 97 runtime: salsa::Runtime::default(),
110 last_gc: crate::wasm_shims::Instant::now(), 98 last_gc: crate::wasm_shims::Instant::now(),
111 last_gc_check: crate::wasm_shims::Instant::now(), 99 last_gc_check: crate::wasm_shims::Instant::now(),
112 debug_data: Default::default(),
113 }; 100 };
114 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); 101 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
115 db.set_local_roots_with_durability(Default::default(), Durability::HIGH); 102 db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
@@ -132,7 +119,6 @@ impl salsa::ParallelDatabase for RootDatabase {
132 runtime: self.runtime.snapshot(self), 119 runtime: self.runtime.snapshot(self),
133 last_gc: self.last_gc, 120 last_gc: self.last_gc,
134 last_gc_check: self.last_gc_check, 121 last_gc_check: self.last_gc_check,
135 debug_data: Arc::clone(&self.debug_data),
136 }) 122 })
137 } 123 }
138} 124}
@@ -146,14 +132,3 @@ fn line_index(db: &impl LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
146 let text = db.file_text(file_id); 132 let text = db.file_text(file_id);
147 Arc::new(LineIndex::new(&*text)) 133 Arc::new(LineIndex::new(&*text))
148} 134}
149
150#[derive(Debug, Default, Clone)]
151pub(crate) struct DebugData {
152 pub(crate) root_paths: FxHashMap<SourceRootId, String>,
153}
154
155impl DebugData {
156 pub(crate) fn merge(&mut self, other: DebugData) {
157 self.root_paths.extend(other.root_paths.into_iter());
158 }
159}
diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs
index e713f4b7e..f40ae8304 100644
--- a/crates/ra_ide_db/src/source_change.rs
+++ b/crates/ra_ide_db/src/source_change.rs
@@ -22,17 +22,6 @@ impl SourceChange {
22 ) -> Self { 22 ) -> Self {
23 SourceChange { source_file_edits, file_system_edits, is_snippet: false } 23 SourceChange { source_file_edits, file_system_edits, is_snippet: false }
24 } 24 }
25
26 /// Creates a new SourceChange with the given label,
27 /// containing only the given `SourceFileEdits`.
28 pub fn source_file_edits(edits: Vec<SourceFileEdit>) -> Self {
29 SourceChange { source_file_edits: edits, file_system_edits: vec![], is_snippet: false }
30 }
31 /// Creates a new SourceChange with the given label
32 /// from the given `FileId` and `TextEdit`
33 pub fn source_file_edit_from(file_id: FileId, edit: TextEdit) -> Self {
34 SourceFileEdit { file_id, edit }.into()
35 }
36} 25}
37 26
38#[derive(Debug, Clone)] 27#[derive(Debug, Clone)]
@@ -43,11 +32,13 @@ pub struct SourceFileEdit {
43 32
44impl From<SourceFileEdit> for SourceChange { 33impl From<SourceFileEdit> for SourceChange {
45 fn from(edit: SourceFileEdit) -> SourceChange { 34 fn from(edit: SourceFileEdit) -> SourceChange {
46 SourceChange { 35 vec![edit].into()
47 source_file_edits: vec![edit], 36 }
48 file_system_edits: Vec::new(), 37}
49 is_snippet: false, 38
50 } 39impl From<Vec<SourceFileEdit>> for SourceChange {
40 fn from(source_file_edits: Vec<SourceFileEdit>) -> SourceChange {
41 SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false }
51 } 42 }
52} 43}
53 44
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs
index acc31fe3b..aab918973 100644
--- a/crates/ra_ide_db/src/symbol_index.rs
+++ b/crates/ra_ide_db/src/symbol_index.rs
@@ -29,9 +29,10 @@ use std::{
29}; 29};
30 30
31use fst::{self, Streamer}; 31use fst::{self, Streamer};
32use hir::db::DefDatabase;
32use ra_db::{ 33use ra_db::{
33 salsa::{self, ParallelDatabase}, 34 salsa::{self, ParallelDatabase},
34 FileId, SourceDatabaseExt, SourceRootId, 35 CrateId, FileId, SourceDatabaseExt, SourceRootId,
35}; 36};
36use ra_syntax::{ 37use ra_syntax::{
37 ast::{self, NameOwner}, 38 ast::{self, NameOwner},
@@ -110,6 +111,14 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex>
110 Arc::new(SymbolIndex::new(symbols)) 111 Arc::new(SymbolIndex::new(symbols))
111} 112}
112 113
114/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
115struct Snap(salsa::Snapshot<RootDatabase>);
116impl Clone for Snap {
117 fn clone(&self) -> Snap {
118 Snap(self.0.snapshot())
119 }
120}
121
113// Feature: Workspace Symbol 122// Feature: Workspace Symbol
114// 123//
115// Uses fuzzy-search to find types, modules and functions by name across your 124// Uses fuzzy-search to find types, modules and functions by name across your
@@ -132,13 +141,7 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex>
132// | VS Code | kbd:[Ctrl+T] 141// | VS Code | kbd:[Ctrl+T]
133// |=== 142// |===
134pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { 143pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
135 /// Need to wrap Snapshot to provide `Clone` impl for `map_with` 144 let _p = ra_prof::profile("world_symbols").detail(|| query.query.clone());
136 struct Snap(salsa::Snapshot<RootDatabase>);
137 impl Clone for Snap {
138 fn clone(&self) -> Snap {
139 Snap(self.0.snapshot())
140 }
141 }
142 145
143 let buf: Vec<Arc<SymbolIndex>> = if query.libs { 146 let buf: Vec<Arc<SymbolIndex>> = if query.libs {
144 let snap = Snap(db.snapshot()); 147 let snap = Snap(db.snapshot());
@@ -173,6 +176,33 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
173 query.search(&buf) 176 query.search(&buf)
174} 177}
175 178
179pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> {
180 // FIXME(#4842): This now depends on CrateDefMap, why not build the entire symbol index from
181 // that instead?
182
183 let def_map = db.crate_def_map(krate);
184 let mut files = Vec::new();
185 let mut modules = vec![def_map.root];
186 while let Some(module) = modules.pop() {
187 let data = &def_map[module];
188 files.extend(data.origin.file_id());
189 modules.extend(data.children.values());
190 }
191
192 let snap = Snap(db.snapshot());
193
194 #[cfg(not(feature = "wasm"))]
195 let buf = files
196 .par_iter()
197 .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
198 .collect::<Vec<_>>();
199
200 #[cfg(feature = "wasm")]
201 let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect::<Vec<_>>();
202
203 query.search(&buf)
204}
205
176pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> { 206pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> {
177 let name = name_ref.text(); 207 let name = name_ref.text();
178 let mut query = Query::new(name.to_string()); 208 let mut query = Query::new(name.to_string());
@@ -298,9 +328,6 @@ impl Query {
298 let mut stream = op.union(); 328 let mut stream = op.union();
299 let mut res = Vec::new(); 329 let mut res = Vec::new();
300 while let Some((_, indexed_values)) = stream.next() { 330 while let Some((_, indexed_values)) = stream.next() {
301 if res.len() >= self.limit {
302 break;
303 }
304 for indexed_value in indexed_values { 331 for indexed_value in indexed_values {
305 let symbol_index = &indices[indexed_value.index]; 332 let symbol_index = &indices[indexed_value.index];
306 let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value); 333 let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);
@@ -312,7 +339,11 @@ impl Query {
312 if self.exact && symbol.name != self.query { 339 if self.exact && symbol.name != self.query {
313 continue; 340 continue;
314 } 341 }
342
315 res.push(symbol.clone()); 343 res.push(symbol.clone());
344 if res.len() >= self.limit {
345 return res;
346 }
316 } 347 }
317 } 348 }
318 } 349 }
diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs
index be0cd5661..293baecf6 100644
--- a/crates/ra_parser/src/grammar.rs
+++ b/crates/ra_parser/src/grammar.rs
@@ -18,9 +18,10 @@
18//! // fn foo() {} 18//! // fn foo() {}
19//! ``` 19//! ```
20//! 20//!
21//! After adding a new inline-test, run `cargo collect-tests` to extract 21//! After adding a new inline-test, run `cargo xtask codegen` to
22//! it as a standalone text-fixture into `tests/data/parser/inline`, and 22//! extract it as a standalone text-fixture into
23//! run `cargo test` once to create the "gold" value. 23//! `crates/ra_syntax/test_data/parser/`, and run `cargo test` once to
24//! create the "gold" value.
24//! 25//!
25//! Coding convention: rules like `where_clause` always produce either a 26//! Coding convention: rules like `where_clause` always produce either a
26//! node or an error, rules like `opt_where_clause` may produce nothing. 27//! node or an error, rules like `opt_where_clause` may produce nothing.
diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs
index 67a924de5..97642bc24 100644
--- a/crates/ra_parser/src/grammar/items.rs
+++ b/crates/ra_parser/src/grammar/items.rs
@@ -118,7 +118,22 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul
118 && p.at_contextual_kw("default") 118 && p.at_contextual_kw("default")
119 && (match p.nth(1) { 119 && (match p.nth(1) {
120 T![impl] => true, 120 T![impl] => true,
121 T![fn] | T![type] => { 121 T![unsafe] => {
122 // test default_unsafe_impl
123 // default unsafe impl Foo {}
124
125 // test default_unsafe_fn
126 // impl T for Foo {
127 // default unsafe fn foo() {}
128 // }
129 if p.nth(2) == T![impl] || p.nth(2) == T![fn] {
130 p.bump_remap(T![default]);
131 p.bump(T![unsafe]);
132 has_mods = true;
133 }
134 false
135 }
136 T![fn] | T![type] | T![const] => {
122 if let ItemFlavor::Mod = flavor { 137 if let ItemFlavor::Mod = flavor {
123 true 138 true
124 } else { 139 } else {
@@ -198,6 +213,9 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul
198 // default type T = Bar; 213 // default type T = Bar;
199 // default fn foo() {} 214 // default fn foo() {}
200 // } 215 // }
216 T![const] => {
217 consts::const_def(p, m);
218 }
201 219
202 // test unsafe_default_impl 220 // test unsafe_default_impl
203 // unsafe default impl Foo {} 221 // unsafe default impl Foo {}
diff --git a/crates/ra_parser/src/grammar/paths.rs b/crates/ra_parser/src/grammar/paths.rs
index 332acc1a0..428aa711e 100644
--- a/crates/ra_parser/src/grammar/paths.rs
+++ b/crates/ra_parser/src/grammar/paths.rs
@@ -3,7 +3,7 @@
3use super::*; 3use super::*;
4 4
5pub(super) const PATH_FIRST: TokenSet = 5pub(super) const PATH_FIRST: TokenSet =
6 token_set![IDENT, SELF_KW, SUPER_KW, CRATE_KW, COLON, L_ANGLE]; 6 token_set![IDENT, T![self], T![super], T![crate], T![:], T![<]];
7 7
8pub(super) fn is_path_start(p: &Parser) -> bool { 8pub(super) fn is_path_start(p: &Parser) -> bool {
9 is_use_path_start(p) || p.at(T![<]) 9 is_use_path_start(p) || p.at(T![<])
diff --git a/crates/ra_parser/src/grammar/patterns.rs b/crates/ra_parser/src/grammar/patterns.rs
index 68fb2fc73..427c0eb49 100644
--- a/crates/ra_parser/src/grammar/patterns.rs
+++ b/crates/ra_parser/src/grammar/patterns.rs
@@ -4,7 +4,7 @@ use super::*;
4 4
5pub(super) const PATTERN_FIRST: TokenSet = expressions::LITERAL_FIRST 5pub(super) const PATTERN_FIRST: TokenSet = expressions::LITERAL_FIRST
6 .union(paths::PATH_FIRST) 6 .union(paths::PATH_FIRST)
7 .union(token_set![BOX_KW, REF_KW, MUT_KW, L_PAREN, L_BRACK, AMP, UNDERSCORE, MINUS, DOT]); 7 .union(token_set![T![box], T![ref], T![mut], T!['('], T!['['], T![&], T![_], T![-], T![.]]);
8 8
9pub(crate) fn pattern(p: &mut Parser) { 9pub(crate) fn pattern(p: &mut Parser) {
10 pattern_r(p, PAT_RECOVERY_SET); 10 pattern_r(p, PAT_RECOVERY_SET);
@@ -88,7 +88,9 @@ fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
88 _ => bind_pat(p, true), 88 _ => bind_pat(p, true),
89 }, 89 },
90 90
91 _ if paths::is_use_path_start(p) => path_or_macro_pat(p), 91 // test type_path_in_pattern
92 // fn main() { let <_>::Foo = (); }
93 _ if paths::is_path_start(p) => path_or_macro_pat(p),
92 _ if is_literal_pat_start(p) => literal_pat(p), 94 _ if is_literal_pat_start(p) => literal_pat(p),
93 95
94 T![.] if p.at(T![..]) => dot_dot_pat(p), 96 T![.] if p.at(T![..]) => dot_dot_pat(p),
@@ -138,7 +140,7 @@ fn literal_pat(p: &mut Parser) -> CompletedMarker {
138// let Bar(..) = (); 140// let Bar(..) = ();
139// } 141// }
140fn path_or_macro_pat(p: &mut Parser) -> CompletedMarker { 142fn path_or_macro_pat(p: &mut Parser) -> CompletedMarker {
141 assert!(paths::is_use_path_start(p)); 143 assert!(paths::is_path_start(p));
142 let m = p.start(); 144 let m = p.start();
143 paths::expr_path(p); 145 paths::expr_path(p);
144 let kind = match p.current() { 146 let kind = match p.current() {
diff --git a/crates/ra_parser/src/grammar/type_params.rs b/crates/ra_parser/src/grammar/type_params.rs
index 50e4900c3..325d566ad 100644
--- a/crates/ra_parser/src/grammar/type_params.rs
+++ b/crates/ra_parser/src/grammar/type_params.rs
@@ -191,10 +191,14 @@ fn where_predicate(p: &mut Parser) {
191 } 191 }
192 _ => { 192 _ => {
193 // test where_pred_for 193 // test where_pred_for
194 // fn test<F>() 194 // fn for_trait<F>()
195 // where 195 // where
196 // for<'a> F: Fn(&'a str) 196 // for<'a> F: Fn(&'a str)
197 // { } 197 // { }
198 if p.at(T![for]) {
199 types::for_binder(p);
200 }
201
198 types::type_(p); 202 types::type_(p);
199 203
200 if p.at(T![:]) { 204 if p.at(T![:]) {
diff --git a/crates/ra_parser/src/grammar/types.rs b/crates/ra_parser/src/grammar/types.rs
index fe1a039cb..9e8e3bd97 100644
--- a/crates/ra_parser/src/grammar/types.rs
+++ b/crates/ra_parser/src/grammar/types.rs
@@ -216,19 +216,21 @@ pub(super) fn for_binder(p: &mut Parser) {
216 216
217// test for_type 217// test for_type
218// type A = for<'a> fn() -> (); 218// type A = for<'a> fn() -> ();
219// fn foo<T>(_t: &T) where for<'a> &'a T: Iterator {} 219// type B = for<'a> unsafe extern "C" fn(&'a ()) -> ();
220// fn bar<T>(_t: &T) where for<'a> &'a mut T: Iterator {} 220// type Obj = for<'a> PartialEq<&'a i32>;
221// fn baz<T>(_t: &T) where for<'a> <&'a T as Baz>::Foo: Iterator {}
222pub(super) fn for_type(p: &mut Parser) { 221pub(super) fn for_type(p: &mut Parser) {
223 assert!(p.at(T![for])); 222 assert!(p.at(T![for]));
224 let m = p.start(); 223 let m = p.start();
225 for_binder(p); 224 for_binder(p);
226 match p.current() { 225 match p.current() {
227 T![fn] | T![unsafe] | T![extern] => fn_pointer_type(p), 226 T![fn] | T![unsafe] | T![extern] => {}
228 T![&] => reference_type(p), 227 // OK: legacy trait object format
229 _ if paths::is_path_start(p) => path_type_(p, false), 228 _ if paths::is_use_path_start(p) => {}
230 _ => p.error("expected a path"), 229 _ => {
230 p.error("expected a function pointer or path");
231 }
231 } 232 }
233 type_no_bounds(p);
232 m.complete(p, FOR_TYPE); 234 m.complete(p, FOR_TYPE);
233} 235}
234 236
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml
index bb3003278..582102945 100644
--- a/crates/ra_proc_macro_srv/Cargo.toml
+++ b/crates/ra_proc_macro_srv/Cargo.toml
@@ -22,3 +22,4 @@ cargo_metadata = "0.10.0"
22difference = "2.0.0" 22difference = "2.0.0"
23# used as proc macro test target 23# used as proc macro test target
24serde_derive = "1.0.106" 24serde_derive = "1.0.106"
25ra_toolchain = { path = "../ra_toolchain" }
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs
index 84348b5de..8d85f2d8a 100644
--- a/crates/ra_proc_macro_srv/src/tests/utils.rs
+++ b/crates/ra_proc_macro_srv/src/tests/utils.rs
@@ -2,7 +2,6 @@
2 2
3use crate::dylib; 3use crate::dylib;
4use crate::ProcMacroSrv; 4use crate::ProcMacroSrv;
5pub use difference::Changeset as __Changeset;
6use ra_proc_macro::ListMacrosTask; 5use ra_proc_macro::ListMacrosTask;
7use std::str::FromStr; 6use std::str::FromStr;
8use test_utils::assert_eq_text; 7use test_utils::assert_eq_text;
@@ -13,7 +12,7 @@ mod fixtures {
13 12
14 // Use current project metadata to get the proc-macro dylib path 13 // Use current project metadata to get the proc-macro dylib path
15 pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf { 14 pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf {
16 let command = Command::new("cargo") 15 let command = Command::new(ra_toolchain::cargo())
17 .args(&["check", "--message-format", "json"]) 16 .args(&["check", "--message-format", "json"])
18 .output() 17 .output()
19 .unwrap() 18 .unwrap()
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index a306ce95f..4b7444039 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -64,7 +64,7 @@ impl Default for CargoConfig {
64 fn default() -> Self { 64 fn default() -> Self {
65 CargoConfig { 65 CargoConfig {
66 no_default_features: false, 66 no_default_features: false,
67 all_features: true, 67 all_features: false,
68 features: Vec::new(), 68 features: Vec::new(),
69 load_out_dirs_from_check: false, 69 load_out_dirs_from_check: false,
70 target: None, 70 target: None,
diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs
index b030c8a6a..ee2de4c25 100644
--- a/crates/ra_project_model/src/json_project.rs
+++ b/crates/ra_project_model/src/json_project.rs
@@ -2,9 +2,16 @@
2 2
3use std::path::PathBuf; 3use std::path::PathBuf;
4 4
5use rustc_hash::{FxHashMap, FxHashSet}; 5use rustc_hash::FxHashSet;
6use serde::Deserialize; 6use serde::Deserialize;
7 7
8/// Roots and crates that compose this Rust project.
9#[derive(Clone, Debug, Deserialize)]
10pub struct JsonProject {
11 pub(crate) roots: Vec<Root>,
12 pub(crate) crates: Vec<Crate>,
13}
14
8/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in 15/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in
9/// all roots. Roots might be nested. 16/// all roots. Roots might be nested.
10#[derive(Clone, Debug, Deserialize)] 17#[derive(Clone, Debug, Deserialize)]
@@ -20,8 +27,10 @@ pub struct Crate {
20 pub(crate) root_module: PathBuf, 27 pub(crate) root_module: PathBuf,
21 pub(crate) edition: Edition, 28 pub(crate) edition: Edition,
22 pub(crate) deps: Vec<Dep>, 29 pub(crate) deps: Vec<Dep>,
23 pub(crate) atom_cfgs: FxHashSet<String>, 30
24 pub(crate) key_value_cfgs: FxHashMap<String, String>, 31 #[serde(default)]
32 pub(crate) cfg: FxHashSet<String>,
33
25 pub(crate) out_dir: Option<PathBuf>, 34 pub(crate) out_dir: Option<PathBuf>,
26 pub(crate) proc_macro_dylib_path: Option<PathBuf>, 35 pub(crate) proc_macro_dylib_path: Option<PathBuf>,
27} 36}
@@ -48,9 +57,39 @@ pub struct Dep {
48 pub(crate) name: String, 57 pub(crate) name: String,
49} 58}
50 59
51/// Roots and crates that compose this Rust project. 60#[cfg(test)]
52#[derive(Clone, Debug, Deserialize)] 61mod tests {
53pub struct JsonProject { 62 use super::*;
54 pub(crate) roots: Vec<Root>, 63 use serde_json::json;
55 pub(crate) crates: Vec<Crate>, 64
65 #[test]
66 fn test_crate_deserialization() {
67 let raw_json = json!( {
68 "crate_id": 2,
69 "root_module": "this/is/a/file/path.rs",
70 "deps": [
71 {
72 "crate": 1,
73 "name": "some_dep_crate"
74 },
75 ],
76 "edition": "2015",
77 "cfg": [
78 "atom_1",
79 "atom_2",
80 "feature=feature_1",
81 "feature=feature_2",
82 "other=value",
83 ],
84
85 });
86
87 let krate: Crate = serde_json::from_value(raw_json).unwrap();
88
89 assert!(krate.cfg.contains(&"atom_1".to_string()));
90 assert!(krate.cfg.contains(&"atom_2".to_string()));
91 assert!(krate.cfg.contains(&"feature=feature_1".to_string()));
92 assert!(krate.cfg.contains(&"feature=feature_2".to_string()));
93 assert!(krate.cfg.contains(&"other=value".to_string()));
94 }
56} 95}
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index a2e9f65ef..cb0e27dce 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -14,7 +14,7 @@ use std::{
14use anyhow::{bail, Context, Result}; 14use anyhow::{bail, Context, Result};
15use ra_cfg::CfgOptions; 15use ra_cfg::CfgOptions;
16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; 16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId};
17use rustc_hash::FxHashMap; 17use rustc_hash::{FxHashMap, FxHashSet};
18use serde_json::from_reader; 18use serde_json::from_reader;
19 19
20pub use crate::{ 20pub use crate::{
@@ -32,6 +32,12 @@ pub enum ProjectWorkspace {
32 Json { project: JsonProject }, 32 Json { project: JsonProject },
33} 33}
34 34
35impl From<JsonProject> for ProjectWorkspace {
36 fn from(project: JsonProject) -> ProjectWorkspace {
37 ProjectWorkspace::Json { project }
38 }
39}
40
35/// `PackageRoot` describes a package root folder. 41/// `PackageRoot` describes a package root folder.
36/// Which may be an external dependency, or a member of 42/// Which may be an external dependency, or a member of
37/// the current workspace. 43/// the current workspace.
@@ -41,41 +47,45 @@ pub struct PackageRoot {
41 path: PathBuf, 47 path: PathBuf,
42 /// Is a member of the current workspace 48 /// Is a member of the current workspace
43 is_member: bool, 49 is_member: bool,
50 out_dir: Option<PathBuf>,
44} 51}
45impl PackageRoot { 52impl PackageRoot {
46 pub fn new_member(path: PathBuf) -> PackageRoot { 53 pub fn new_member(path: PathBuf) -> PackageRoot {
47 Self { path, is_member: true } 54 Self { path, is_member: true, out_dir: None }
48 } 55 }
49 pub fn new_non_member(path: PathBuf) -> PackageRoot { 56 pub fn new_non_member(path: PathBuf) -> PackageRoot {
50 Self { path, is_member: false } 57 Self { path, is_member: false, out_dir: None }
51 } 58 }
52 pub fn path(&self) -> &Path { 59 pub fn path(&self) -> &Path {
53 &self.path 60 &self.path
54 } 61 }
62 pub fn out_dir(&self) -> Option<&Path> {
63 self.out_dir.as_deref()
64 }
55 pub fn is_member(&self) -> bool { 65 pub fn is_member(&self) -> bool {
56 self.is_member 66 self.is_member
57 } 67 }
58} 68}
59 69
60#[derive(Debug, Clone, PartialEq, Eq, Hash)] 70#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
61pub enum ProjectRoot { 71pub enum ProjectManifest {
62 ProjectJson(PathBuf), 72 ProjectJson(PathBuf),
63 CargoToml(PathBuf), 73 CargoToml(PathBuf),
64} 74}
65 75
66impl ProjectRoot { 76impl ProjectManifest {
67 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> { 77 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> {
68 if path.ends_with("rust-project.json") { 78 if path.ends_with("rust-project.json") {
69 return Ok(ProjectRoot::ProjectJson(path)); 79 return Ok(ProjectManifest::ProjectJson(path));
70 } 80 }
71 if path.ends_with("Cargo.toml") { 81 if path.ends_with("Cargo.toml") {
72 return Ok(ProjectRoot::CargoToml(path)); 82 return Ok(ProjectManifest::CargoToml(path));
73 } 83 }
74 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) 84 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
75 } 85 }
76 86
77 pub fn discover_single(path: &Path) -> Result<ProjectRoot> { 87 pub fn discover_single(path: &Path) -> Result<ProjectManifest> {
78 let mut candidates = ProjectRoot::discover(path)?; 88 let mut candidates = ProjectManifest::discover(path)?;
79 let res = match candidates.pop() { 89 let res = match candidates.pop() {
80 None => bail!("no projects"), 90 None => bail!("no projects"),
81 Some(it) => it, 91 Some(it) => it,
@@ -87,12 +97,12 @@ impl ProjectRoot {
87 Ok(res) 97 Ok(res)
88 } 98 }
89 99
90 pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { 100 pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> {
91 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { 101 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
92 return Ok(vec![ProjectRoot::ProjectJson(project_json)]); 102 return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
93 } 103 }
94 return find_cargo_toml(path) 104 return find_cargo_toml(path)
95 .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); 105 .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());
96 106
97 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { 107 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
98 match find_in_parent_dirs(path, "Cargo.toml") { 108 match find_in_parent_dirs(path, "Cargo.toml") {
@@ -128,16 +138,28 @@ impl ProjectRoot {
128 .collect() 138 .collect()
129 } 139 }
130 } 140 }
141
142 pub fn discover_all(paths: &[impl AsRef<Path>]) -> Vec<ProjectManifest> {
143 let mut res = paths
144 .iter()
145 .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
146 .flatten()
147 .collect::<FxHashSet<_>>()
148 .into_iter()
149 .collect::<Vec<_>>();
150 res.sort();
151 res
152 }
131} 153}
132 154
133impl ProjectWorkspace { 155impl ProjectWorkspace {
134 pub fn load( 156 pub fn load(
135 root: ProjectRoot, 157 manifest: ProjectManifest,
136 cargo_features: &CargoConfig, 158 cargo_features: &CargoConfig,
137 with_sysroot: bool, 159 with_sysroot: bool,
138 ) -> Result<ProjectWorkspace> { 160 ) -> Result<ProjectWorkspace> {
139 let res = match root { 161 let res = match manifest {
140 ProjectRoot::ProjectJson(project_json) => { 162 ProjectManifest::ProjectJson(project_json) => {
141 let file = File::open(&project_json).with_context(|| { 163 let file = File::open(&project_json).with_context(|| {
142 format!("Failed to open json file {}", project_json.display()) 164 format!("Failed to open json file {}", project_json.display())
143 })?; 165 })?;
@@ -148,7 +170,7 @@ impl ProjectWorkspace {
148 })?, 170 })?,
149 } 171 }
150 } 172 }
151 ProjectRoot::CargoToml(cargo_toml) => { 173 ProjectManifest::CargoToml(cargo_toml) => {
152 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) 174 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
153 .with_context(|| { 175 .with_context(|| {
154 format!( 176 format!(
@@ -186,6 +208,7 @@ impl ProjectWorkspace {
186 .map(|pkg| PackageRoot { 208 .map(|pkg| PackageRoot {
187 path: cargo[pkg].root().to_path_buf(), 209 path: cargo[pkg].root().to_path_buf(),
188 is_member: cargo[pkg].is_member, 210 is_member: cargo[pkg].is_member,
211 out_dir: cargo[pkg].out_dir.clone(),
189 }) 212 })
190 .chain(sysroot.crates().map(|krate| { 213 .chain(sysroot.crates().map(|krate| {
191 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) 214 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf())
@@ -194,17 +217,6 @@ impl ProjectWorkspace {
194 } 217 }
195 } 218 }
196 219
197 pub fn out_dirs(&self) -> Vec<PathBuf> {
198 match self {
199 ProjectWorkspace::Json { project } => {
200 project.crates.iter().filter_map(|krate| krate.out_dir.as_ref()).cloned().collect()
201 }
202 ProjectWorkspace::Cargo { cargo, sysroot: _ } => {
203 cargo.packages().filter_map(|pkg| cargo[pkg].out_dir.as_ref()).cloned().collect()
204 }
205 }
206 }
207
208 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> { 220 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> {
209 match self { 221 match self {
210 ProjectWorkspace::Json { project } => project 222 ProjectWorkspace::Json { project } => project
@@ -232,7 +244,7 @@ impl ProjectWorkspace {
232 244
233 pub fn to_crate_graph( 245 pub fn to_crate_graph(
234 &self, 246 &self,
235 default_cfg_options: &CfgOptions, 247 target: Option<&str>,
236 extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>, 248 extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>,
237 proc_macro_client: &ProcMacroClient, 249 proc_macro_client: &ProcMacroClient,
238 load: &mut dyn FnMut(&Path) -> Option<FileId>, 250 load: &mut dyn FnMut(&Path) -> Option<FileId>,
@@ -251,12 +263,16 @@ impl ProjectWorkspace {
251 json_project::Edition::Edition2018 => Edition::Edition2018, 263 json_project::Edition::Edition2018 => Edition::Edition2018,
252 }; 264 };
253 let cfg_options = { 265 let cfg_options = {
254 let mut opts = default_cfg_options.clone(); 266 let mut opts = CfgOptions::default();
255 for name in &krate.atom_cfgs { 267 for cfg in &krate.cfg {
256 opts.insert_atom(name.into()); 268 match cfg.find('=') {
257 } 269 None => opts.insert_atom(cfg.into()),
258 for (key, value) in &krate.key_value_cfgs { 270 Some(pos) => {
259 opts.insert_key_value(key.into(), value.into()); 271 let key = &cfg[..pos];
272 let value = cfg[pos + 1..].trim_matches('"');
273 opts.insert_key_value(key.into(), value.into());
274 }
275 }
260 } 276 }
261 opts 277 opts
262 }; 278 };
@@ -315,18 +331,13 @@ impl ProjectWorkspace {
315 } 331 }
316 } 332 }
317 ProjectWorkspace::Cargo { cargo, sysroot } => { 333 ProjectWorkspace::Cargo { cargo, sysroot } => {
334 let mut cfg_options = get_rustc_cfg_options(target);
335
318 let sysroot_crates: FxHashMap<_, _> = sysroot 336 let sysroot_crates: FxHashMap<_, _> = sysroot
319 .crates() 337 .crates()
320 .filter_map(|krate| { 338 .filter_map(|krate| {
321 let file_id = load(&sysroot[krate].root)?; 339 let file_id = load(&sysroot[krate].root)?;
322 340
323 // Crates from sysroot have `cfg(test)` disabled
324 let cfg_options = {
325 let mut opts = default_cfg_options.clone();
326 opts.remove_atom("test");
327 opts
328 };
329
330 let env = Env::default(); 341 let env = Env::default();
331 let extern_source = ExternSource::default(); 342 let extern_source = ExternSource::default();
332 let proc_macro = vec![]; 343 let proc_macro = vec![];
@@ -337,7 +348,7 @@ impl ProjectWorkspace {
337 file_id, 348 file_id,
338 Edition::Edition2018, 349 Edition::Edition2018,
339 Some(crate_name), 350 Some(crate_name),
340 cfg_options, 351 cfg_options.clone(),
341 env, 352 env,
342 extern_source, 353 extern_source,
343 proc_macro, 354 proc_macro,
@@ -368,6 +379,10 @@ impl ProjectWorkspace {
368 379
369 let mut pkg_to_lib_crate = FxHashMap::default(); 380 let mut pkg_to_lib_crate = FxHashMap::default();
370 let mut pkg_crates = FxHashMap::default(); 381 let mut pkg_crates = FxHashMap::default();
382
383 // Add test cfg for non-sysroot crates
384 cfg_options.insert_atom("test".into());
385
371 // Next, create crates for each package, target pair 386 // Next, create crates for each package, target pair
372 for pkg in cargo.packages() { 387 for pkg in cargo.packages() {
373 let mut lib_tgt = None; 388 let mut lib_tgt = None;
@@ -376,7 +391,7 @@ impl ProjectWorkspace {
376 if let Some(file_id) = load(root) { 391 if let Some(file_id) = load(root) {
377 let edition = cargo[pkg].edition; 392 let edition = cargo[pkg].edition;
378 let cfg_options = { 393 let cfg_options = {
379 let mut opts = default_cfg_options.clone(); 394 let mut opts = cfg_options.clone();
380 for feature in cargo[pkg].features.iter() { 395 for feature in cargo[pkg].features.iter() {
381 opts.insert_key_value("feature".into(), feature.into()); 396 opts.insert_key_value("feature".into(), feature.into());
382 } 397 }
@@ -533,7 +548,7 @@ impl ProjectWorkspace {
533 } 548 }
534} 549}
535 550
536pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { 551fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions {
537 let mut cfg_options = CfgOptions::default(); 552 let mut cfg_options = CfgOptions::default();
538 553
539 // Some nightly-only cfgs, which are required for stdlib 554 // Some nightly-only cfgs, which are required for stdlib
@@ -551,7 +566,7 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions {
551 let mut cmd = Command::new(ra_toolchain::rustc()); 566 let mut cmd = Command::new(ra_toolchain::rustc());
552 cmd.args(&["--print", "cfg", "-O"]); 567 cmd.args(&["--print", "cfg", "-O"]);
553 if let Some(target) = target { 568 if let Some(target) = target {
554 cmd.args(&["--target", target.as_str()]); 569 cmd.args(&["--target", target]);
555 } 570 }
556 let output = output(cmd)?; 571 let output = output(cmd)?;
557 Ok(String::from_utf8(output.stdout)?) 572 Ok(String::from_utf8(output.stdout)?)
@@ -573,6 +588,8 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions {
573 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), 588 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
574 } 589 }
575 590
591 cfg_options.insert_atom("debug_assertions".into());
592
576 cfg_options 593 cfg_options
577} 594}
578 595
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 1876afe95..9d02aeef3 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -75,7 +75,7 @@ impl<N> AstChildren<N> {
75impl<N: AstNode> Iterator for AstChildren<N> { 75impl<N: AstNode> Iterator for AstChildren<N> {
76 type Item = N; 76 type Item = N;
77 fn next(&mut self) -> Option<N> { 77 fn next(&mut self) -> Option<N> {
78 self.inner.by_ref().find_map(N::cast) 78 self.inner.find_map(N::cast)
79 } 79 }
80} 80}
81 81
@@ -285,6 +285,8 @@ where
285 let pred = predicates.next().unwrap(); 285 let pred = predicates.next().unwrap();
286 let mut bounds = pred.type_bound_list().unwrap().bounds(); 286 let mut bounds = pred.type_bound_list().unwrap().bounds();
287 287
288 assert!(pred.for_token().is_none());
289 assert!(pred.type_param_list().is_none());
288 assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string()); 290 assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string());
289 assert_bound("Clone", bounds.next()); 291 assert_bound("Clone", bounds.next());
290 assert_bound("Copy", bounds.next()); 292 assert_bound("Copy", bounds.next());
@@ -322,6 +324,8 @@ where
322 let pred = predicates.next().unwrap(); 324 let pred = predicates.next().unwrap();
323 let mut bounds = pred.type_bound_list().unwrap().bounds(); 325 let mut bounds = pred.type_bound_list().unwrap().bounds();
324 326
325 assert_eq!("for<'a> F", pred.type_ref().unwrap().syntax().text().to_string()); 327 assert!(pred.for_token().is_some());
328 assert_eq!("<'a>", pred.type_param_list().unwrap().syntax().text().to_string());
329 assert_eq!("F", pred.type_ref().unwrap().syntax().text().to_string());
326 assert_bound("Fn(&'a str)", bounds.next()); 330 assert_bound("Fn(&'a str)", bounds.next());
327} 331}
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index 29eb3fcb9..2ef173a03 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -579,12 +579,17 @@ pub trait AstNodeEdit: AstNode + Clone + Sized {
579 rewriter.rewrite_ast(self) 579 rewriter.rewrite_ast(self)
580 } 580 }
581 #[must_use] 581 #[must_use]
582 fn indent(&self, indent: IndentLevel) -> Self { 582 fn indent(&self, level: IndentLevel) -> Self {
583 Self::cast(indent.increase_indent(self.syntax().clone())).unwrap() 583 Self::cast(level.increase_indent(self.syntax().clone())).unwrap()
584 } 584 }
585 #[must_use] 585 #[must_use]
586 fn dedent(&self, indent: IndentLevel) -> Self { 586 fn dedent(&self, level: IndentLevel) -> Self {
587 Self::cast(indent.decrease_indent(self.syntax().clone())).unwrap() 587 Self::cast(level.decrease_indent(self.syntax().clone())).unwrap()
588 }
589 #[must_use]
590 fn reset_indent(&self) -> Self {
591 let level = IndentLevel::from_node(self.syntax());
592 self.dedent(level)
588 } 593 }
589} 594}
590 595
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs
index cb430ca01..58141da11 100644
--- a/crates/ra_syntax/src/ast/generated/nodes.rs
+++ b/crates/ra_syntax/src/ast/generated/nodes.rs
@@ -2052,6 +2052,8 @@ pub struct WherePred {
2052} 2052}
2053impl ast::TypeBoundsOwner for WherePred {} 2053impl ast::TypeBoundsOwner for WherePred {}
2054impl WherePred { 2054impl WherePred {
2055 pub fn for_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![for]) }
2056 pub fn type_param_list(&self) -> Option<TypeParamList> { support::child(&self.syntax) }
2055 pub fn lifetime_token(&self) -> Option<SyntaxToken> { 2057 pub fn lifetime_token(&self) -> Option<SyntaxToken> {
2056 support::token(&self.syntax, T![lifetime]) 2058 support::token(&self.syntax, T![lifetime])
2057 } 2059 }
@@ -4849,687 +4851,687 @@ impl AstNode for FieldDefList {
4849 } 4851 }
4850} 4852}
4851impl std::fmt::Display for NominalDef { 4853impl std::fmt::Display for NominalDef {
4852 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4854 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4853 std::fmt::Display::fmt(self.syntax(), f) 4855 std::fmt::Display::fmt(self.syntax(), f)
4854 } 4856 }
4855} 4857}
4856impl std::fmt::Display for GenericParam { 4858impl std::fmt::Display for GenericParam {
4857 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4859 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4858 std::fmt::Display::fmt(self.syntax(), f) 4860 std::fmt::Display::fmt(self.syntax(), f)
4859 } 4861 }
4860} 4862}
4861impl std::fmt::Display for GenericArg { 4863impl std::fmt::Display for GenericArg {
4862 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4864 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4863 std::fmt::Display::fmt(self.syntax(), f) 4865 std::fmt::Display::fmt(self.syntax(), f)
4864 } 4866 }
4865} 4867}
4866impl std::fmt::Display for TypeRef { 4868impl std::fmt::Display for TypeRef {
4867 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4869 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4868 std::fmt::Display::fmt(self.syntax(), f) 4870 std::fmt::Display::fmt(self.syntax(), f)
4869 } 4871 }
4870} 4872}
4871impl std::fmt::Display for ModuleItem { 4873impl std::fmt::Display for ModuleItem {
4872 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4874 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4873 std::fmt::Display::fmt(self.syntax(), f) 4875 std::fmt::Display::fmt(self.syntax(), f)
4874 } 4876 }
4875} 4877}
4876impl std::fmt::Display for AssocItem { 4878impl std::fmt::Display for AssocItem {
4877 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4879 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4878 std::fmt::Display::fmt(self.syntax(), f) 4880 std::fmt::Display::fmt(self.syntax(), f)
4879 } 4881 }
4880} 4882}
4881impl std::fmt::Display for ExternItem { 4883impl std::fmt::Display for ExternItem {
4882 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4884 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4883 std::fmt::Display::fmt(self.syntax(), f) 4885 std::fmt::Display::fmt(self.syntax(), f)
4884 } 4886 }
4885} 4887}
4886impl std::fmt::Display for Expr { 4888impl std::fmt::Display for Expr {
4887 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4889 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4888 std::fmt::Display::fmt(self.syntax(), f) 4890 std::fmt::Display::fmt(self.syntax(), f)
4889 } 4891 }
4890} 4892}
4891impl std::fmt::Display for Pat { 4893impl std::fmt::Display for Pat {
4892 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4894 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4893 std::fmt::Display::fmt(self.syntax(), f) 4895 std::fmt::Display::fmt(self.syntax(), f)
4894 } 4896 }
4895} 4897}
4896impl std::fmt::Display for RecordInnerPat { 4898impl std::fmt::Display for RecordInnerPat {
4897 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4899 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4898 std::fmt::Display::fmt(self.syntax(), f) 4900 std::fmt::Display::fmt(self.syntax(), f)
4899 } 4901 }
4900} 4902}
4901impl std::fmt::Display for AttrInput { 4903impl std::fmt::Display for AttrInput {
4902 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4904 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4903 std::fmt::Display::fmt(self.syntax(), f) 4905 std::fmt::Display::fmt(self.syntax(), f)
4904 } 4906 }
4905} 4907}
4906impl std::fmt::Display for Stmt { 4908impl std::fmt::Display for Stmt {
4907 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4909 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4908 std::fmt::Display::fmt(self.syntax(), f) 4910 std::fmt::Display::fmt(self.syntax(), f)
4909 } 4911 }
4910} 4912}
4911impl std::fmt::Display for FieldDefList { 4913impl std::fmt::Display for FieldDefList {
4912 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4914 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4913 std::fmt::Display::fmt(self.syntax(), f) 4915 std::fmt::Display::fmt(self.syntax(), f)
4914 } 4916 }
4915} 4917}
4916impl std::fmt::Display for SourceFile { 4918impl std::fmt::Display for SourceFile {
4917 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4919 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4918 std::fmt::Display::fmt(self.syntax(), f) 4920 std::fmt::Display::fmt(self.syntax(), f)
4919 } 4921 }
4920} 4922}
4921impl std::fmt::Display for FnDef { 4923impl std::fmt::Display for FnDef {
4922 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4924 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4923 std::fmt::Display::fmt(self.syntax(), f) 4925 std::fmt::Display::fmt(self.syntax(), f)
4924 } 4926 }
4925} 4927}
4926impl std::fmt::Display for RetType { 4928impl std::fmt::Display for RetType {
4927 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4929 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4928 std::fmt::Display::fmt(self.syntax(), f) 4930 std::fmt::Display::fmt(self.syntax(), f)
4929 } 4931 }
4930} 4932}
4931impl std::fmt::Display for StructDef { 4933impl std::fmt::Display for StructDef {
4932 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4934 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4933 std::fmt::Display::fmt(self.syntax(), f) 4935 std::fmt::Display::fmt(self.syntax(), f)
4934 } 4936 }
4935} 4937}
4936impl std::fmt::Display for UnionDef { 4938impl std::fmt::Display for UnionDef {
4937 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4939 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4938 std::fmt::Display::fmt(self.syntax(), f) 4940 std::fmt::Display::fmt(self.syntax(), f)
4939 } 4941 }
4940} 4942}
4941impl std::fmt::Display for RecordFieldDefList { 4943impl std::fmt::Display for RecordFieldDefList {
4942 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4944 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4943 std::fmt::Display::fmt(self.syntax(), f) 4945 std::fmt::Display::fmt(self.syntax(), f)
4944 } 4946 }
4945} 4947}
4946impl std::fmt::Display for RecordFieldDef { 4948impl std::fmt::Display for RecordFieldDef {
4947 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4949 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4948 std::fmt::Display::fmt(self.syntax(), f) 4950 std::fmt::Display::fmt(self.syntax(), f)
4949 } 4951 }
4950} 4952}
4951impl std::fmt::Display for TupleFieldDefList { 4953impl std::fmt::Display for TupleFieldDefList {
4952 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4954 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4953 std::fmt::Display::fmt(self.syntax(), f) 4955 std::fmt::Display::fmt(self.syntax(), f)
4954 } 4956 }
4955} 4957}
4956impl std::fmt::Display for TupleFieldDef { 4958impl std::fmt::Display for TupleFieldDef {
4957 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4959 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4958 std::fmt::Display::fmt(self.syntax(), f) 4960 std::fmt::Display::fmt(self.syntax(), f)
4959 } 4961 }
4960} 4962}
4961impl std::fmt::Display for EnumDef { 4963impl std::fmt::Display for EnumDef {
4962 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4964 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4963 std::fmt::Display::fmt(self.syntax(), f) 4965 std::fmt::Display::fmt(self.syntax(), f)
4964 } 4966 }
4965} 4967}
4966impl std::fmt::Display for EnumVariantList { 4968impl std::fmt::Display for EnumVariantList {
4967 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4969 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4968 std::fmt::Display::fmt(self.syntax(), f) 4970 std::fmt::Display::fmt(self.syntax(), f)
4969 } 4971 }
4970} 4972}
4971impl std::fmt::Display for EnumVariant { 4973impl std::fmt::Display for EnumVariant {
4972 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4974 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4973 std::fmt::Display::fmt(self.syntax(), f) 4975 std::fmt::Display::fmt(self.syntax(), f)
4974 } 4976 }
4975} 4977}
4976impl std::fmt::Display for TraitDef { 4978impl std::fmt::Display for TraitDef {
4977 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4979 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4978 std::fmt::Display::fmt(self.syntax(), f) 4980 std::fmt::Display::fmt(self.syntax(), f)
4979 } 4981 }
4980} 4982}
4981impl std::fmt::Display for Module { 4983impl std::fmt::Display for Module {
4982 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4984 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4983 std::fmt::Display::fmt(self.syntax(), f) 4985 std::fmt::Display::fmt(self.syntax(), f)
4984 } 4986 }
4985} 4987}
4986impl std::fmt::Display for ItemList { 4988impl std::fmt::Display for ItemList {
4987 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4989 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4988 std::fmt::Display::fmt(self.syntax(), f) 4990 std::fmt::Display::fmt(self.syntax(), f)
4989 } 4991 }
4990} 4992}
4991impl std::fmt::Display for ConstDef { 4993impl std::fmt::Display for ConstDef {
4992 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4994 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4993 std::fmt::Display::fmt(self.syntax(), f) 4995 std::fmt::Display::fmt(self.syntax(), f)
4994 } 4996 }
4995} 4997}
4996impl std::fmt::Display for StaticDef { 4998impl std::fmt::Display for StaticDef {
4997 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4999 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4998 std::fmt::Display::fmt(self.syntax(), f) 5000 std::fmt::Display::fmt(self.syntax(), f)
4999 } 5001 }
5000} 5002}
5001impl std::fmt::Display for TypeAliasDef { 5003impl std::fmt::Display for TypeAliasDef {
5002 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5004 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5003 std::fmt::Display::fmt(self.syntax(), f) 5005 std::fmt::Display::fmt(self.syntax(), f)
5004 } 5006 }
5005} 5007}
5006impl std::fmt::Display for ImplDef { 5008impl std::fmt::Display for ImplDef {
5007 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5009 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5008 std::fmt::Display::fmt(self.syntax(), f) 5010 std::fmt::Display::fmt(self.syntax(), f)
5009 } 5011 }
5010} 5012}
5011impl std::fmt::Display for ParenType { 5013impl std::fmt::Display for ParenType {
5012 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5014 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5013 std::fmt::Display::fmt(self.syntax(), f) 5015 std::fmt::Display::fmt(self.syntax(), f)
5014 } 5016 }
5015} 5017}
5016impl std::fmt::Display for TupleType { 5018impl std::fmt::Display for TupleType {
5017 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5019 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5018 std::fmt::Display::fmt(self.syntax(), f) 5020 std::fmt::Display::fmt(self.syntax(), f)
5019 } 5021 }
5020} 5022}
5021impl std::fmt::Display for NeverType { 5023impl std::fmt::Display for NeverType {
5022 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5024 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5023 std::fmt::Display::fmt(self.syntax(), f) 5025 std::fmt::Display::fmt(self.syntax(), f)
5024 } 5026 }
5025} 5027}
5026impl std::fmt::Display for PathType { 5028impl std::fmt::Display for PathType {
5027 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5029 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5028 std::fmt::Display::fmt(self.syntax(), f) 5030 std::fmt::Display::fmt(self.syntax(), f)
5029 } 5031 }
5030} 5032}
5031impl std::fmt::Display for PointerType { 5033impl std::fmt::Display for PointerType {
5032 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5034 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5033 std::fmt::Display::fmt(self.syntax(), f) 5035 std::fmt::Display::fmt(self.syntax(), f)
5034 } 5036 }
5035} 5037}
5036impl std::fmt::Display for ArrayType { 5038impl std::fmt::Display for ArrayType {
5037 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5039 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5038 std::fmt::Display::fmt(self.syntax(), f) 5040 std::fmt::Display::fmt(self.syntax(), f)
5039 } 5041 }
5040} 5042}
5041impl std::fmt::Display for SliceType { 5043impl std::fmt::Display for SliceType {
5042 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5044 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5043 std::fmt::Display::fmt(self.syntax(), f) 5045 std::fmt::Display::fmt(self.syntax(), f)
5044 } 5046 }
5045} 5047}
5046impl std::fmt::Display for ReferenceType { 5048impl std::fmt::Display for ReferenceType {
5047 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5049 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5048 std::fmt::Display::fmt(self.syntax(), f) 5050 std::fmt::Display::fmt(self.syntax(), f)
5049 } 5051 }
5050} 5052}
5051impl std::fmt::Display for PlaceholderType { 5053impl std::fmt::Display for PlaceholderType {
5052 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5054 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5053 std::fmt::Display::fmt(self.syntax(), f) 5055 std::fmt::Display::fmt(self.syntax(), f)
5054 } 5056 }
5055} 5057}
5056impl std::fmt::Display for FnPointerType { 5058impl std::fmt::Display for FnPointerType {
5057 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5059 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5058 std::fmt::Display::fmt(self.syntax(), f) 5060 std::fmt::Display::fmt(self.syntax(), f)
5059 } 5061 }
5060} 5062}
5061impl std::fmt::Display for ForType { 5063impl std::fmt::Display for ForType {
5062 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5064 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5063 std::fmt::Display::fmt(self.syntax(), f) 5065 std::fmt::Display::fmt(self.syntax(), f)
5064 } 5066 }
5065} 5067}
5066impl std::fmt::Display for ImplTraitType { 5068impl std::fmt::Display for ImplTraitType {
5067 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5069 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5068 std::fmt::Display::fmt(self.syntax(), f) 5070 std::fmt::Display::fmt(self.syntax(), f)
5069 } 5071 }
5070} 5072}
5071impl std::fmt::Display for DynTraitType { 5073impl std::fmt::Display for DynTraitType {
5072 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5074 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5073 std::fmt::Display::fmt(self.syntax(), f) 5075 std::fmt::Display::fmt(self.syntax(), f)
5074 } 5076 }
5075} 5077}
5076impl std::fmt::Display for TupleExpr { 5078impl std::fmt::Display for TupleExpr {
5077 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5079 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5078 std::fmt::Display::fmt(self.syntax(), f) 5080 std::fmt::Display::fmt(self.syntax(), f)
5079 } 5081 }
5080} 5082}
5081impl std::fmt::Display for ArrayExpr { 5083impl std::fmt::Display for ArrayExpr {
5082 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5084 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5083 std::fmt::Display::fmt(self.syntax(), f) 5085 std::fmt::Display::fmt(self.syntax(), f)
5084 } 5086 }
5085} 5087}
5086impl std::fmt::Display for ParenExpr { 5088impl std::fmt::Display for ParenExpr {
5087 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5089 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5088 std::fmt::Display::fmt(self.syntax(), f) 5090 std::fmt::Display::fmt(self.syntax(), f)
5089 } 5091 }
5090} 5092}
5091impl std::fmt::Display for PathExpr { 5093impl std::fmt::Display for PathExpr {
5092 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5094 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5093 std::fmt::Display::fmt(self.syntax(), f) 5095 std::fmt::Display::fmt(self.syntax(), f)
5094 } 5096 }
5095} 5097}
5096impl std::fmt::Display for LambdaExpr { 5098impl std::fmt::Display for LambdaExpr {
5097 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5099 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5098 std::fmt::Display::fmt(self.syntax(), f) 5100 std::fmt::Display::fmt(self.syntax(), f)
5099 } 5101 }
5100} 5102}
5101impl std::fmt::Display for IfExpr { 5103impl std::fmt::Display for IfExpr {
5102 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5103 std::fmt::Display::fmt(self.syntax(), f) 5105 std::fmt::Display::fmt(self.syntax(), f)
5104 } 5106 }
5105} 5107}
5106impl std::fmt::Display for LoopExpr { 5108impl std::fmt::Display for LoopExpr {
5107 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5108 std::fmt::Display::fmt(self.syntax(), f) 5110 std::fmt::Display::fmt(self.syntax(), f)
5109 } 5111 }
5110} 5112}
5111impl std::fmt::Display for EffectExpr { 5113impl std::fmt::Display for EffectExpr {
5112 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5113 std::fmt::Display::fmt(self.syntax(), f) 5115 std::fmt::Display::fmt(self.syntax(), f)
5114 } 5116 }
5115} 5117}
5116impl std::fmt::Display for ForExpr { 5118impl std::fmt::Display for ForExpr {
5117 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5118 std::fmt::Display::fmt(self.syntax(), f) 5120 std::fmt::Display::fmt(self.syntax(), f)
5119 } 5121 }
5120} 5122}
5121impl std::fmt::Display for WhileExpr { 5123impl std::fmt::Display for WhileExpr {
5122 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5123 std::fmt::Display::fmt(self.syntax(), f) 5125 std::fmt::Display::fmt(self.syntax(), f)
5124 } 5126 }
5125} 5127}
5126impl std::fmt::Display for ContinueExpr { 5128impl std::fmt::Display for ContinueExpr {
5127 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5128 std::fmt::Display::fmt(self.syntax(), f) 5130 std::fmt::Display::fmt(self.syntax(), f)
5129 } 5131 }
5130} 5132}
5131impl std::fmt::Display for BreakExpr { 5133impl std::fmt::Display for BreakExpr {
5132 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5133 std::fmt::Display::fmt(self.syntax(), f) 5135 std::fmt::Display::fmt(self.syntax(), f)
5134 } 5136 }
5135} 5137}
5136impl std::fmt::Display for Label { 5138impl std::fmt::Display for Label {
5137 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5138 std::fmt::Display::fmt(self.syntax(), f) 5140 std::fmt::Display::fmt(self.syntax(), f)
5139 } 5141 }
5140} 5142}
5141impl std::fmt::Display for BlockExpr { 5143impl std::fmt::Display for BlockExpr {
5142 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5143 std::fmt::Display::fmt(self.syntax(), f) 5145 std::fmt::Display::fmt(self.syntax(), f)
5144 } 5146 }
5145} 5147}
5146impl std::fmt::Display for ReturnExpr { 5148impl std::fmt::Display for ReturnExpr {
5147 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5148 std::fmt::Display::fmt(self.syntax(), f) 5150 std::fmt::Display::fmt(self.syntax(), f)
5149 } 5151 }
5150} 5152}
5151impl std::fmt::Display for CallExpr { 5153impl std::fmt::Display for CallExpr {
5152 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5153 std::fmt::Display::fmt(self.syntax(), f) 5155 std::fmt::Display::fmt(self.syntax(), f)
5154 } 5156 }
5155} 5157}
5156impl std::fmt::Display for MethodCallExpr { 5158impl std::fmt::Display for MethodCallExpr {
5157 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5158 std::fmt::Display::fmt(self.syntax(), f) 5160 std::fmt::Display::fmt(self.syntax(), f)
5159 } 5161 }
5160} 5162}
5161impl std::fmt::Display for IndexExpr { 5163impl std::fmt::Display for IndexExpr {
5162 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5163 std::fmt::Display::fmt(self.syntax(), f) 5165 std::fmt::Display::fmt(self.syntax(), f)
5164 } 5166 }
5165} 5167}
5166impl std::fmt::Display for FieldExpr { 5168impl std::fmt::Display for FieldExpr {
5167 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5168 std::fmt::Display::fmt(self.syntax(), f) 5170 std::fmt::Display::fmt(self.syntax(), f)
5169 } 5171 }
5170} 5172}
5171impl std::fmt::Display for AwaitExpr { 5173impl std::fmt::Display for AwaitExpr {
5172 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5174 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5173 std::fmt::Display::fmt(self.syntax(), f) 5175 std::fmt::Display::fmt(self.syntax(), f)
5174 } 5176 }
5175} 5177}
5176impl std::fmt::Display for TryExpr { 5178impl std::fmt::Display for TryExpr {
5177 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5178 std::fmt::Display::fmt(self.syntax(), f) 5180 std::fmt::Display::fmt(self.syntax(), f)
5179 } 5181 }
5180} 5182}
5181impl std::fmt::Display for CastExpr { 5183impl std::fmt::Display for CastExpr {
5182 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5183 std::fmt::Display::fmt(self.syntax(), f) 5185 std::fmt::Display::fmt(self.syntax(), f)
5184 } 5186 }
5185} 5187}
5186impl std::fmt::Display for RefExpr { 5188impl std::fmt::Display for RefExpr {
5187 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5188 std::fmt::Display::fmt(self.syntax(), f) 5190 std::fmt::Display::fmt(self.syntax(), f)
5189 } 5191 }
5190} 5192}
5191impl std::fmt::Display for PrefixExpr { 5193impl std::fmt::Display for PrefixExpr {
5192 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5193 std::fmt::Display::fmt(self.syntax(), f) 5195 std::fmt::Display::fmt(self.syntax(), f)
5194 } 5196 }
5195} 5197}
5196impl std::fmt::Display for BoxExpr { 5198impl std::fmt::Display for BoxExpr {
5197 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5199 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5198 std::fmt::Display::fmt(self.syntax(), f) 5200 std::fmt::Display::fmt(self.syntax(), f)
5199 } 5201 }
5200} 5202}
5201impl std::fmt::Display for RangeExpr { 5203impl std::fmt::Display for RangeExpr {
5202 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5203 std::fmt::Display::fmt(self.syntax(), f) 5205 std::fmt::Display::fmt(self.syntax(), f)
5204 } 5206 }
5205} 5207}
5206impl std::fmt::Display for BinExpr { 5208impl std::fmt::Display for BinExpr {
5207 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5208 std::fmt::Display::fmt(self.syntax(), f) 5210 std::fmt::Display::fmt(self.syntax(), f)
5209 } 5211 }
5210} 5212}
5211impl std::fmt::Display for Literal { 5213impl std::fmt::Display for Literal {
5212 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5213 std::fmt::Display::fmt(self.syntax(), f) 5215 std::fmt::Display::fmt(self.syntax(), f)
5214 } 5216 }
5215} 5217}
5216impl std::fmt::Display for MatchExpr { 5218impl std::fmt::Display for MatchExpr {
5217 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5218 std::fmt::Display::fmt(self.syntax(), f) 5220 std::fmt::Display::fmt(self.syntax(), f)
5219 } 5221 }
5220} 5222}
5221impl std::fmt::Display for MatchArmList { 5223impl std::fmt::Display for MatchArmList {
5222 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5223 std::fmt::Display::fmt(self.syntax(), f) 5225 std::fmt::Display::fmt(self.syntax(), f)
5224 } 5226 }
5225} 5227}
5226impl std::fmt::Display for MatchArm { 5228impl std::fmt::Display for MatchArm {
5227 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5228 std::fmt::Display::fmt(self.syntax(), f) 5230 std::fmt::Display::fmt(self.syntax(), f)
5229 } 5231 }
5230} 5232}
5231impl std::fmt::Display for MatchGuard { 5233impl std::fmt::Display for MatchGuard {
5232 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5233 std::fmt::Display::fmt(self.syntax(), f) 5235 std::fmt::Display::fmt(self.syntax(), f)
5234 } 5236 }
5235} 5237}
5236impl std::fmt::Display for RecordLit { 5238impl std::fmt::Display for RecordLit {
5237 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5238 std::fmt::Display::fmt(self.syntax(), f) 5240 std::fmt::Display::fmt(self.syntax(), f)
5239 } 5241 }
5240} 5242}
5241impl std::fmt::Display for RecordFieldList { 5243impl std::fmt::Display for RecordFieldList {
5242 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5243 std::fmt::Display::fmt(self.syntax(), f) 5245 std::fmt::Display::fmt(self.syntax(), f)
5244 } 5246 }
5245} 5247}
5246impl std::fmt::Display for RecordField { 5248impl std::fmt::Display for RecordField {
5247 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5248 std::fmt::Display::fmt(self.syntax(), f) 5250 std::fmt::Display::fmt(self.syntax(), f)
5249 } 5251 }
5250} 5252}
5251impl std::fmt::Display for OrPat { 5253impl std::fmt::Display for OrPat {
5252 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5254 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5253 std::fmt::Display::fmt(self.syntax(), f) 5255 std::fmt::Display::fmt(self.syntax(), f)
5254 } 5256 }
5255} 5257}
5256impl std::fmt::Display for ParenPat { 5258impl std::fmt::Display for ParenPat {
5257 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5258 std::fmt::Display::fmt(self.syntax(), f) 5260 std::fmt::Display::fmt(self.syntax(), f)
5259 } 5261 }
5260} 5262}
5261impl std::fmt::Display for RefPat { 5263impl std::fmt::Display for RefPat {
5262 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5264 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5263 std::fmt::Display::fmt(self.syntax(), f) 5265 std::fmt::Display::fmt(self.syntax(), f)
5264 } 5266 }
5265} 5267}
5266impl std::fmt::Display for BoxPat { 5268impl std::fmt::Display for BoxPat {
5267 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5269 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5268 std::fmt::Display::fmt(self.syntax(), f) 5270 std::fmt::Display::fmt(self.syntax(), f)
5269 } 5271 }
5270} 5272}
5271impl std::fmt::Display for BindPat { 5273impl std::fmt::Display for BindPat {
5272 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5273 std::fmt::Display::fmt(self.syntax(), f) 5275 std::fmt::Display::fmt(self.syntax(), f)
5274 } 5276 }
5275} 5277}
5276impl std::fmt::Display for PlaceholderPat { 5278impl std::fmt::Display for PlaceholderPat {
5277 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5278 std::fmt::Display::fmt(self.syntax(), f) 5280 std::fmt::Display::fmt(self.syntax(), f)
5279 } 5281 }
5280} 5282}
5281impl std::fmt::Display for DotDotPat { 5283impl std::fmt::Display for DotDotPat {
5282 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5284 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5283 std::fmt::Display::fmt(self.syntax(), f) 5285 std::fmt::Display::fmt(self.syntax(), f)
5284 } 5286 }
5285} 5287}
5286impl std::fmt::Display for PathPat { 5288impl std::fmt::Display for PathPat {
5287 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5289 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5288 std::fmt::Display::fmt(self.syntax(), f) 5290 std::fmt::Display::fmt(self.syntax(), f)
5289 } 5291 }
5290} 5292}
5291impl std::fmt::Display for SlicePat { 5293impl std::fmt::Display for SlicePat {
5292 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5294 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5293 std::fmt::Display::fmt(self.syntax(), f) 5295 std::fmt::Display::fmt(self.syntax(), f)
5294 } 5296 }
5295} 5297}
5296impl std::fmt::Display for RangePat { 5298impl std::fmt::Display for RangePat {
5297 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5298 std::fmt::Display::fmt(self.syntax(), f) 5300 std::fmt::Display::fmt(self.syntax(), f)
5299 } 5301 }
5300} 5302}
5301impl std::fmt::Display for LiteralPat { 5303impl std::fmt::Display for LiteralPat {
5302 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5304 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5303 std::fmt::Display::fmt(self.syntax(), f) 5305 std::fmt::Display::fmt(self.syntax(), f)
5304 } 5306 }
5305} 5307}
5306impl std::fmt::Display for MacroPat { 5308impl std::fmt::Display for MacroPat {
5307 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5309 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5308 std::fmt::Display::fmt(self.syntax(), f) 5310 std::fmt::Display::fmt(self.syntax(), f)
5309 } 5311 }
5310} 5312}
5311impl std::fmt::Display for RecordPat { 5313impl std::fmt::Display for RecordPat {
5312 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5313 std::fmt::Display::fmt(self.syntax(), f) 5315 std::fmt::Display::fmt(self.syntax(), f)
5314 } 5316 }
5315} 5317}
5316impl std::fmt::Display for RecordFieldPatList { 5318impl std::fmt::Display for RecordFieldPatList {
5317 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5319 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5318 std::fmt::Display::fmt(self.syntax(), f) 5320 std::fmt::Display::fmt(self.syntax(), f)
5319 } 5321 }
5320} 5322}
5321impl std::fmt::Display for RecordFieldPat { 5323impl std::fmt::Display for RecordFieldPat {
5322 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5324 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5323 std::fmt::Display::fmt(self.syntax(), f) 5325 std::fmt::Display::fmt(self.syntax(), f)
5324 } 5326 }
5325} 5327}
5326impl std::fmt::Display for TupleStructPat { 5328impl std::fmt::Display for TupleStructPat {
5327 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5329 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5328 std::fmt::Display::fmt(self.syntax(), f) 5330 std::fmt::Display::fmt(self.syntax(), f)
5329 } 5331 }
5330} 5332}
5331impl std::fmt::Display for TuplePat { 5333impl std::fmt::Display for TuplePat {
5332 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5333 std::fmt::Display::fmt(self.syntax(), f) 5335 std::fmt::Display::fmt(self.syntax(), f)
5334 } 5336 }
5335} 5337}
5336impl std::fmt::Display for Visibility { 5338impl std::fmt::Display for Visibility {
5337 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5339 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5338 std::fmt::Display::fmt(self.syntax(), f) 5340 std::fmt::Display::fmt(self.syntax(), f)
5339 } 5341 }
5340} 5342}
5341impl std::fmt::Display for Name { 5343impl std::fmt::Display for Name {
5342 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5344 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5343 std::fmt::Display::fmt(self.syntax(), f) 5345 std::fmt::Display::fmt(self.syntax(), f)
5344 } 5346 }
5345} 5347}
5346impl std::fmt::Display for NameRef { 5348impl std::fmt::Display for NameRef {
5347 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5348 std::fmt::Display::fmt(self.syntax(), f) 5350 std::fmt::Display::fmt(self.syntax(), f)
5349 } 5351 }
5350} 5352}
5351impl std::fmt::Display for MacroCall { 5353impl std::fmt::Display for MacroCall {
5352 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5354 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5353 std::fmt::Display::fmt(self.syntax(), f) 5355 std::fmt::Display::fmt(self.syntax(), f)
5354 } 5356 }
5355} 5357}
5356impl std::fmt::Display for Attr { 5358impl std::fmt::Display for Attr {
5357 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5359 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5358 std::fmt::Display::fmt(self.syntax(), f) 5360 std::fmt::Display::fmt(self.syntax(), f)
5359 } 5361 }
5360} 5362}
5361impl std::fmt::Display for TokenTree { 5363impl std::fmt::Display for TokenTree {
5362 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5364 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5363 std::fmt::Display::fmt(self.syntax(), f) 5365 std::fmt::Display::fmt(self.syntax(), f)
5364 } 5366 }
5365} 5367}
5366impl std::fmt::Display for TypeParamList { 5368impl std::fmt::Display for TypeParamList {
5367 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5369 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5368 std::fmt::Display::fmt(self.syntax(), f) 5370 std::fmt::Display::fmt(self.syntax(), f)
5369 } 5371 }
5370} 5372}
5371impl std::fmt::Display for TypeParam { 5373impl std::fmt::Display for TypeParam {
5372 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5374 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5373 std::fmt::Display::fmt(self.syntax(), f) 5375 std::fmt::Display::fmt(self.syntax(), f)
5374 } 5376 }
5375} 5377}
5376impl std::fmt::Display for ConstParam { 5378impl std::fmt::Display for ConstParam {
5377 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5379 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5378 std::fmt::Display::fmt(self.syntax(), f) 5380 std::fmt::Display::fmt(self.syntax(), f)
5379 } 5381 }
5380} 5382}
5381impl std::fmt::Display for LifetimeParam { 5383impl std::fmt::Display for LifetimeParam {
5382 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5383 std::fmt::Display::fmt(self.syntax(), f) 5385 std::fmt::Display::fmt(self.syntax(), f)
5384 } 5386 }
5385} 5387}
5386impl std::fmt::Display for TypeBound { 5388impl std::fmt::Display for TypeBound {
5387 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5389 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5388 std::fmt::Display::fmt(self.syntax(), f) 5390 std::fmt::Display::fmt(self.syntax(), f)
5389 } 5391 }
5390} 5392}
5391impl std::fmt::Display for TypeBoundList { 5393impl std::fmt::Display for TypeBoundList {
5392 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5394 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5393 std::fmt::Display::fmt(self.syntax(), f) 5395 std::fmt::Display::fmt(self.syntax(), f)
5394 } 5396 }
5395} 5397}
5396impl std::fmt::Display for WherePred { 5398impl std::fmt::Display for WherePred {
5397 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5399 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5398 std::fmt::Display::fmt(self.syntax(), f) 5400 std::fmt::Display::fmt(self.syntax(), f)
5399 } 5401 }
5400} 5402}
5401impl std::fmt::Display for WhereClause { 5403impl std::fmt::Display for WhereClause {
5402 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5404 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5403 std::fmt::Display::fmt(self.syntax(), f) 5405 std::fmt::Display::fmt(self.syntax(), f)
5404 } 5406 }
5405} 5407}
5406impl std::fmt::Display for Abi { 5408impl std::fmt::Display for Abi {
5407 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5409 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5408 std::fmt::Display::fmt(self.syntax(), f) 5410 std::fmt::Display::fmt(self.syntax(), f)
5409 } 5411 }
5410} 5412}
5411impl std::fmt::Display for ExprStmt { 5413impl std::fmt::Display for ExprStmt {
5412 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5413 std::fmt::Display::fmt(self.syntax(), f) 5415 std::fmt::Display::fmt(self.syntax(), f)
5414 } 5416 }
5415} 5417}
5416impl std::fmt::Display for LetStmt { 5418impl std::fmt::Display for LetStmt {
5417 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5419 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5418 std::fmt::Display::fmt(self.syntax(), f) 5420 std::fmt::Display::fmt(self.syntax(), f)
5419 } 5421 }
5420} 5422}
5421impl std::fmt::Display for Condition { 5423impl std::fmt::Display for Condition {
5422 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5424 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5423 std::fmt::Display::fmt(self.syntax(), f) 5425 std::fmt::Display::fmt(self.syntax(), f)
5424 } 5426 }
5425} 5427}
5426impl std::fmt::Display for ParamList { 5428impl std::fmt::Display for ParamList {
5427 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5429 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5428 std::fmt::Display::fmt(self.syntax(), f) 5430 std::fmt::Display::fmt(self.syntax(), f)
5429 } 5431 }
5430} 5432}
5431impl std::fmt::Display for SelfParam { 5433impl std::fmt::Display for SelfParam {
5432 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5434 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5433 std::fmt::Display::fmt(self.syntax(), f) 5435 std::fmt::Display::fmt(self.syntax(), f)
5434 } 5436 }
5435} 5437}
5436impl std::fmt::Display for Param { 5438impl std::fmt::Display for Param {
5437 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5439 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5438 std::fmt::Display::fmt(self.syntax(), f) 5440 std::fmt::Display::fmt(self.syntax(), f)
5439 } 5441 }
5440} 5442}
5441impl std::fmt::Display for UseItem { 5443impl std::fmt::Display for UseItem {
5442 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5444 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5443 std::fmt::Display::fmt(self.syntax(), f) 5445 std::fmt::Display::fmt(self.syntax(), f)
5444 } 5446 }
5445} 5447}
5446impl std::fmt::Display for UseTree { 5448impl std::fmt::Display for UseTree {
5447 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5449 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5448 std::fmt::Display::fmt(self.syntax(), f) 5450 std::fmt::Display::fmt(self.syntax(), f)
5449 } 5451 }
5450} 5452}
5451impl std::fmt::Display for Alias { 5453impl std::fmt::Display for Alias {
5452 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5454 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5453 std::fmt::Display::fmt(self.syntax(), f) 5455 std::fmt::Display::fmt(self.syntax(), f)
5454 } 5456 }
5455} 5457}
5456impl std::fmt::Display for UseTreeList { 5458impl std::fmt::Display for UseTreeList {
5457 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5459 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5458 std::fmt::Display::fmt(self.syntax(), f) 5460 std::fmt::Display::fmt(self.syntax(), f)
5459 } 5461 }
5460} 5462}
5461impl std::fmt::Display for ExternCrateItem { 5463impl std::fmt::Display for ExternCrateItem {
5462 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5464 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5463 std::fmt::Display::fmt(self.syntax(), f) 5465 std::fmt::Display::fmt(self.syntax(), f)
5464 } 5466 }
5465} 5467}
5466impl std::fmt::Display for ArgList { 5468impl std::fmt::Display for ArgList {
5467 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5469 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5468 std::fmt::Display::fmt(self.syntax(), f) 5470 std::fmt::Display::fmt(self.syntax(), f)
5469 } 5471 }
5470} 5472}
5471impl std::fmt::Display for Path { 5473impl std::fmt::Display for Path {
5472 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5474 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5473 std::fmt::Display::fmt(self.syntax(), f) 5475 std::fmt::Display::fmt(self.syntax(), f)
5474 } 5476 }
5475} 5477}
5476impl std::fmt::Display for PathSegment { 5478impl std::fmt::Display for PathSegment {
5477 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5479 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5478 std::fmt::Display::fmt(self.syntax(), f) 5480 std::fmt::Display::fmt(self.syntax(), f)
5479 } 5481 }
5480} 5482}
5481impl std::fmt::Display for TypeArgList { 5483impl std::fmt::Display for TypeArgList {
5482 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5484 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5483 std::fmt::Display::fmt(self.syntax(), f) 5485 std::fmt::Display::fmt(self.syntax(), f)
5484 } 5486 }
5485} 5487}
5486impl std::fmt::Display for TypeArg { 5488impl std::fmt::Display for TypeArg {
5487 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5489 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5488 std::fmt::Display::fmt(self.syntax(), f) 5490 std::fmt::Display::fmt(self.syntax(), f)
5489 } 5491 }
5490} 5492}
5491impl std::fmt::Display for AssocTypeArg { 5493impl std::fmt::Display for AssocTypeArg {
5492 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5494 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5493 std::fmt::Display::fmt(self.syntax(), f) 5495 std::fmt::Display::fmt(self.syntax(), f)
5494 } 5496 }
5495} 5497}
5496impl std::fmt::Display for LifetimeArg { 5498impl std::fmt::Display for LifetimeArg {
5497 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5499 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5498 std::fmt::Display::fmt(self.syntax(), f) 5500 std::fmt::Display::fmt(self.syntax(), f)
5499 } 5501 }
5500} 5502}
5501impl std::fmt::Display for ConstArg { 5503impl std::fmt::Display for ConstArg {
5502 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5504 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5503 std::fmt::Display::fmt(self.syntax(), f) 5505 std::fmt::Display::fmt(self.syntax(), f)
5504 } 5506 }
5505} 5507}
5506impl std::fmt::Display for MacroItems { 5508impl std::fmt::Display for MacroItems {
5507 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5508 std::fmt::Display::fmt(self.syntax(), f) 5510 std::fmt::Display::fmt(self.syntax(), f)
5509 } 5511 }
5510} 5512}
5511impl std::fmt::Display for MacroStmts { 5513impl std::fmt::Display for MacroStmts {
5512 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5514 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5513 std::fmt::Display::fmt(self.syntax(), f) 5515 std::fmt::Display::fmt(self.syntax(), f)
5514 } 5516 }
5515} 5517}
5516impl std::fmt::Display for ExternItemList { 5518impl std::fmt::Display for ExternItemList {
5517 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5519 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5518 std::fmt::Display::fmt(self.syntax(), f) 5520 std::fmt::Display::fmt(self.syntax(), f)
5519 } 5521 }
5520} 5522}
5521impl std::fmt::Display for ExternBlock { 5523impl std::fmt::Display for ExternBlock {
5522 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5524 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5523 std::fmt::Display::fmt(self.syntax(), f) 5525 std::fmt::Display::fmt(self.syntax(), f)
5524 } 5526 }
5525} 5527}
5526impl std::fmt::Display for MetaItem { 5528impl std::fmt::Display for MetaItem {
5527 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5529 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5528 std::fmt::Display::fmt(self.syntax(), f) 5530 std::fmt::Display::fmt(self.syntax(), f)
5529 } 5531 }
5530} 5532}
5531impl std::fmt::Display for MacroDef { 5533impl std::fmt::Display for MacroDef {
5532 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5534 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5533 std::fmt::Display::fmt(self.syntax(), f) 5535 std::fmt::Display::fmt(self.syntax(), f)
5534 } 5536 }
5535} 5537}
diff --git a/crates/ra_syntax/src/ast/generated/tokens.rs b/crates/ra_syntax/src/ast/generated/tokens.rs
index f91befaac..abadd0b61 100644
--- a/crates/ra_syntax/src/ast/generated/tokens.rs
+++ b/crates/ra_syntax/src/ast/generated/tokens.rs
@@ -11,7 +11,7 @@ pub struct Whitespace {
11 pub(crate) syntax: SyntaxToken, 11 pub(crate) syntax: SyntaxToken,
12} 12}
13impl std::fmt::Display for Whitespace { 13impl std::fmt::Display for Whitespace {
14 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 std::fmt::Display::fmt(&self.syntax, f) 15 std::fmt::Display::fmt(&self.syntax, f)
16 } 16 }
17} 17}
@@ -32,7 +32,7 @@ pub struct Comment {
32 pub(crate) syntax: SyntaxToken, 32 pub(crate) syntax: SyntaxToken,
33} 33}
34impl std::fmt::Display for Comment { 34impl std::fmt::Display for Comment {
35 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 std::fmt::Display::fmt(&self.syntax, f) 36 std::fmt::Display::fmt(&self.syntax, f)
37 } 37 }
38} 38}
@@ -53,7 +53,7 @@ pub struct String {
53 pub(crate) syntax: SyntaxToken, 53 pub(crate) syntax: SyntaxToken,
54} 54}
55impl std::fmt::Display for String { 55impl std::fmt::Display for String {
56 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 std::fmt::Display::fmt(&self.syntax, f) 57 std::fmt::Display::fmt(&self.syntax, f)
58 } 58 }
59} 59}
@@ -74,7 +74,7 @@ pub struct RawString {
74 pub(crate) syntax: SyntaxToken, 74 pub(crate) syntax: SyntaxToken,
75} 75}
76impl std::fmt::Display for RawString { 76impl std::fmt::Display for RawString {
77 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 std::fmt::Display::fmt(&self.syntax, f) 78 std::fmt::Display::fmt(&self.syntax, f)
79 } 79 }
80} 80}
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index 3cd6d99c3..2e72d4927 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -84,7 +84,7 @@ impl Whitespace {
84} 84}
85 85
86pub struct QuoteOffsets { 86pub struct QuoteOffsets {
87 pub quotes: [TextRange; 2], 87 pub quotes: (TextRange, TextRange),
88 pub contents: TextRange, 88 pub contents: TextRange,
89} 89}
90 90
@@ -103,7 +103,7 @@ impl QuoteOffsets {
103 let end = TextSize::of(literal); 103 let end = TextSize::of(literal);
104 104
105 let res = QuoteOffsets { 105 let res = QuoteOffsets {
106 quotes: [TextRange::new(start, left_quote), TextRange::new(right_quote, end)], 106 quotes: (TextRange::new(start, left_quote), TextRange::new(right_quote, end)),
107 contents: TextRange::new(left_quote, right_quote), 107 contents: TextRange::new(left_quote, right_quote),
108 }; 108 };
109 Some(res) 109 Some(res)
@@ -116,17 +116,17 @@ pub trait HasQuotes: AstToken {
116 let offsets = QuoteOffsets::new(text)?; 116 let offsets = QuoteOffsets::new(text)?;
117 let o = self.syntax().text_range().start(); 117 let o = self.syntax().text_range().start();
118 let offsets = QuoteOffsets { 118 let offsets = QuoteOffsets {
119 quotes: [offsets.quotes[0] + o, offsets.quotes[1] + o], 119 quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o),
120 contents: offsets.contents + o, 120 contents: offsets.contents + o,
121 }; 121 };
122 Some(offsets) 122 Some(offsets)
123 } 123 }
124 fn open_quote_text_range(&self) -> Option<TextRange> { 124 fn open_quote_text_range(&self) -> Option<TextRange> {
125 self.quote_offsets().map(|it| it.quotes[0]) 125 self.quote_offsets().map(|it| it.quotes.0)
126 } 126 }
127 127
128 fn close_quote_text_range(&self) -> Option<TextRange> { 128 fn close_quote_text_range(&self) -> Option<TextRange> {
129 self.quote_offsets().map(|it| it.quotes[1]) 129 self.quote_offsets().map(|it| it.quotes.1)
130 } 130 }
131 131
132 fn text_range_between_quotes(&self) -> Option<TextRange> { 132 fn text_range_between_quotes(&self) -> Option<TextRange> {
@@ -335,16 +335,26 @@ pub trait HasFormatSpecifier: AstToken {
335 } 335 }
336 c if c == '_' || c.is_alphabetic() => { 336 c if c == '_' || c.is_alphabetic() => {
337 read_identifier(&mut chars, &mut callback); 337 read_identifier(&mut chars, &mut callback);
338 if chars.peek().and_then(|next| next.1.as_ref().ok()).copied() 338 // can be either width (indicated by dollar sign, or type in which case
339 != Some('$') 339 // the next sign has to be `}`)
340 { 340 let next =
341 continue; 341 chars.peek().and_then(|next| next.1.as_ref().ok()).copied();
342 } 342 match next {
343 skip_char_and_emit( 343 Some('$') => skip_char_and_emit(
344 &mut chars, 344 &mut chars,
345 FormatSpecifier::DollarSign, 345 FormatSpecifier::DollarSign,
346 &mut callback, 346 &mut callback,
347 ); 347 ),
348 Some('}') => {
349 skip_char_and_emit(
350 &mut chars,
351 FormatSpecifier::Close,
352 &mut callback,
353 );
354 continue;
355 }
356 _ => continue,
357 };
348 } 358 }
349 _ => {} 359 _ => {}
350 } 360 }
@@ -416,17 +426,11 @@ pub trait HasFormatSpecifier: AstToken {
416 } 426 }
417 } 427 }
418 428
419 let mut cloned = chars.clone().take(2); 429 if let Some((_, Ok('}'))) = chars.peek() {
420 let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); 430 skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback);
421 let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); 431 } else {
422 if first != Some('}') {
423 continue;
424 }
425 if second == Some('}') {
426 // Escaped format end specifier, `}}`
427 continue; 432 continue;
428 } 433 }
429 skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback);
430 } 434 }
431 _ => { 435 _ => {
432 while let Some((_, Ok(next_char))) = chars.peek() { 436 while let Some((_, Ok(next_char))) = chars.peek() {
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs
index bfc05e08b..a8f2454fd 100644
--- a/crates/ra_syntax/src/ast/traits.rs
+++ b/crates/ra_syntax/src/ast/traits.rs
@@ -83,13 +83,22 @@ pub trait DocCommentsOwner: AstNode {
83 CommentIter { iter: self.syntax().children_with_tokens() } 83 CommentIter { iter: self.syntax().children_with_tokens() }
84 } 84 }
85 85
86 fn doc_comment_text(&self) -> Option<String> {
87 self.doc_comments().doc_comment_text()
88 }
89}
90
91impl CommentIter {
92 pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> CommentIter {
93 CommentIter { iter: syntax_node.children_with_tokens() }
94 }
95
86 /// Returns the textual content of a doc comment block as a single string. 96 /// Returns the textual content of a doc comment block as a single string.
87 /// That is, strips leading `///` (+ optional 1 character of whitespace), 97 /// That is, strips leading `///` (+ optional 1 character of whitespace),
88 /// trailing `*/`, trailing whitespace and then joins the lines. 98 /// trailing `*/`, trailing whitespace and then joins the lines.
89 fn doc_comment_text(&self) -> Option<String> { 99 pub fn doc_comment_text(self) -> Option<String> {
90 let mut has_comments = false; 100 let mut has_comments = false;
91 let docs = self 101 let docs = self
92 .doc_comments()
93 .filter(|comment| comment.kind().doc.is_some()) 102 .filter(|comment| comment.kind().doc.is_some())
94 .map(|comment| { 103 .map(|comment| {
95 has_comments = true; 104 has_comments = true;
diff --git a/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast b/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast
index 7c957fdde..48610a5eb 100644
--- a/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast
+++ b/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast
@@ -180,44 +180,45 @@ [email protected]
180 [email protected] 180 [email protected]
181 [email protected] 181 [email protected]
182 [email protected] "(" 182 [email protected] "("
183 [email protected] 183 [email protected]
184 [email protected] 184 [email protected] "for"
185 [email protected] 185 [email protected]
186 [email protected] 186 [email protected]
187 [email protected] 187 [email protected]
188 [email protected] 188 [email protected] "<"
189 [email protected] "for" 189 [email protected]
190 [email protected] 190 [email protected] "\'a"
191 [email protected] "<" 191 [email protected] ">"
192 [email protected] 192 [email protected] " "
193 [email protected] "\'a" 193 [email protected]
194 [email protected] ">" 194 [email protected]
195 [email protected] " " 195 [email protected]
196 [email protected]
196 [email protected] 197 [email protected]
197 [email protected] 198 [email protected]
198 [email protected] 199 [email protected]
199 [email protected] 200 [email protected]
200 [email protected] "Trait" 201 [email protected] "Trait"
201 [email protected] "<" 202 [email protected] "<"
202 [email protected] 203 [email protected]
203 [email protected] "\'a" 204 [email protected] "\'a"
204 [email protected] ">" 205 [email protected] ">"
205 [email protected] 206 [email protected]
206 [email protected] ")" 207 [email protected] ")"
207 [email protected] " " 208 [email protected] " "
208 [email protected] "+" 209 [email protected] "+"
209 [email protected] " " 210 [email protected] " "
210 [email protected] 211 [email protected]
211 [email protected] "(" 212 [email protected] "("
212 [email protected] 213 [email protected]
213 [email protected] 214 [email protected]
214 [email protected] 215 [email protected]
215 [email protected] 216 [email protected]
216 [email protected] "Copy" 217 [email protected] "Copy"
217 [email protected] ")" 218 [email protected] ")"
218 [email protected] ">" 219 [email protected] ">"
219 [email protected] 220 [email protected]
220 [email protected] ";" 221 [email protected] ";"
221 [email protected] "\n " 222 [email protected] "\n "
222 [email protected] 223 [email protected]
223 [email protected] "let" 224 [email protected] "let"
@@ -302,13 +303,12 @@ error 146..146: expected expression
302error 147..147: expected SEMICOLON 303error 147..147: expected SEMICOLON
303error 148..148: expected expression 304error 148..148: expected expression
304error 149..149: expected SEMICOLON 305error 149..149: expected SEMICOLON
305error 154..154: expected pattern 306error 155..155: expected type
306error 155..155: expected IN_KW 307error 158..158: expected IN_KW
307error 155..155: expected expression
308error 157..157: expected a block
309error 165..165: expected expression 308error 165..165: expected expression
310error 168..168: expected expression 309error 168..168: expected expression
311error 179..179: expected expression 310error 179..179: expected expression
311error 180..180: expected a block
312error 180..180: expected COMMA 312error 180..180: expected COMMA
313error 180..180: expected expression 313error 180..180: expected expression
314error 180..180: expected R_PAREN 314error 180..180: expected R_PAREN
diff --git a/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast b/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast
index 568a4cc02..4d6461d1e 100644
--- a/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast
+++ b/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast
@@ -12,17 +12,16 @@ [email protected]
12 [email protected] "where" 12 [email protected] "where"
13 [email protected] " " 13 [email protected] " "
14 [email protected] 14 [email protected]
15 [email protected] 15 [email protected] "for"
16 [email protected] "for" 16 [email protected]
17 [email protected] 17 [email protected] "<"
18 [email protected] "<" 18 [email protected]
19 [email protected] 19 [email protected] "\'a"
20 [email protected] "\'a" 20 [email protected] ">"
21 [email protected] ">"
22 [email protected] "\n" 21 [email protected] "\n"
23 [email protected] 22 [email protected]
24 [email protected] "{" 23 [email protected] "{"
25 [email protected] "}" 24 [email protected] "}"
26 [email protected] "\n" 25 [email protected] "\n"
27error 26..26: expected a path 26error 26..26: expected type
28error 26..26: expected colon 27error 26..26: expected colon
diff --git a/crates/ra_syntax/test_data/parser/err/0043_default_const.rast b/crates/ra_syntax/test_data/parser/err/0043_default_const.rast
new file mode 100644
index 000000000..8eb583ef8
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0043_default_const.rast
@@ -0,0 +1,40 @@
1[email protected]
2 [email protected]
3 [email protected] "trait"
4 [email protected] " "
5 [email protected]
6 [email protected] "T"
7 [email protected] " "
8 [email protected]
9 [email protected] "{"
10 [email protected] "\n "
11 [email protected]
12 [email protected]
13 [email protected]
14 [email protected]
15 [email protected] "default"
16 [email protected] " "
17 [email protected]
18 [email protected] "const"
19 [email protected] " "
20 [email protected]
21 [email protected] "f"
22 [email protected] ":"
23 [email protected] " "
24 [email protected]
25 [email protected]
26 [email protected]
27 [email protected]
28 [email protected] "u8"
29 [email protected] " "
30 [email protected] "="
31 [email protected] " "
32 [email protected]
33 [email protected] "0"
34 [email protected] ";"
35 [email protected] "\n"
36 [email protected] "}"
37 [email protected] "\n"
38error 19..19: expected BANG
39error 19..19: expected `{`, `[`, `(`
40error 19..19: expected SEMICOLON
diff --git a/crates/ra_syntax/test_data/parser/err/0043_default_const.rs b/crates/ra_syntax/test_data/parser/err/0043_default_const.rs
new file mode 100644
index 000000000..80f15474a
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0043_default_const.rs
@@ -0,0 +1,3 @@
1trait T {
2 default const f: u8 = 0;
3}
diff --git a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast
new file mode 100644
index 000000000..cb90f28bc
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast
@@ -0,0 +1,240 @@
1[email protected]
2 [email protected]
3 [email protected] "type"
4 [email protected] " "
5 [email protected]
6 [email protected] "ForRef"
7 [email protected] " "
8 [email protected] "="
9 [email protected] " "
10 [email protected]
11 [email protected] "for"
12 [email protected]
13 [email protected] "<"
14 [email protected]
15 [email protected] "\'a"
16 [email protected] ">"
17 [email protected] " "
18 [email protected]
19 [email protected] "&"
20 [email protected] "\'a"
21 [email protected] " "
22 [email protected]
23 [email protected]
24 [email protected]
25 [email protected]
26 [email protected] "u32"
27 [email protected] ";"
28 [email protected] "\n"
29 [email protected]
30 [email protected] "type"
31 [email protected] " "
32 [email protected]
33 [email protected] "ForTup"
34 [email protected] " "
35 [email protected] "="
36 [email protected] " "
37 [email protected]
38 [email protected] "for"
39 [email protected]
40 [email protected] "<"
41 [email protected]
42 [email protected] "\'a"
43 [email protected] ">"
44 [email protected] " "
45 [email protected]
46 [email protected] "("
47 [email protected]
48 [email protected] "&"
49 [email protected] "\'a"
50 [email protected] " "
51 [email protected]
52 [email protected]
53 [email protected]
54 [email protected]
55 [email protected] "u32"
56 [email protected] ","
57 [email protected] ")"
58 [email protected] ";"
59 [email protected] "\n"
60 [email protected]
61 [email protected] "type"
62 [email protected] " "
63 [email protected]
64 [email protected] "ForSlice"
65 [email protected] " "
66 [email protected] "="
67 [email protected] " "
68 [email protected]
69 [email protected] "for"
70 [email protected]
71 [email protected] "<"
72 [email protected]
73 [email protected] "\'a"
74 [email protected] ">"
75 [email protected] " "
76 [email protected]
77 [email protected] "["
78 [email protected]
79 [email protected]
80 [email protected]
81 [email protected]
82 [email protected] "u32"
83 [email protected] "]"
84 [email protected] ";"
85 [email protected] "\n"
86 [email protected]
87 [email protected] "type"
88 [email protected] " "
89 [email protected]
90 [email protected] "ForForFn"
91 [email protected] " "
92 [email protected] "="
93 [email protected] " "
94 [email protected]
95 [email protected] "for"
96 [email protected]
97 [email protected] "<"
98 [email protected]
99 [email protected] "\'a"
100 [email protected] ">"
101 [email protected] " "
102 [email protected]
103 [email protected] "for"
104 [email protected]
105 [email protected] "<"
106 [email protected]
107 [email protected] "\'b"
108 [email protected] ">"
109 [email protected] " "
110 [email protected]
111 [email protected] "fn"
112 [email protected]
113 [email protected] "("
114 [email protected]
115 [email protected]
116 [email protected] "&"
117 [email protected] "\'a"
118 [email protected] " "
119 [email protected]
120 [email protected]
121 [email protected]
122 [email protected]
123 [email protected] "i32"
124 [email protected] ","
125 [email protected] " "
126 [email protected]
127 [email protected]
128 [email protected] "&"
129 [email protected] "\'b"
130 [email protected] " "
131 [email protected]
132 [email protected]
133 [email protected]
134 [email protected]
135 [email protected] "i32"
136 [email protected] ")"
137 [email protected] ";"
138 [email protected] "\n"
139 [email protected]
140 [email protected] "fn"
141 [email protected] " "
142 [email protected]
143 [email protected] "for_for_for"
144 [email protected]
145 [email protected] "<"
146 [email protected]
147 [email protected]
148 [email protected] "T"
149 [email protected] ">"
150 [email protected]
151 [email protected] "("
152 [email protected] ")"
153 [email protected] "\n"
154 [email protected]
155 [email protected] "where"
156 [email protected] "\n "
157 [email protected]
158 [email protected] "for"
159 [email protected]
160 [email protected] "<"
161 [email protected]
162 [email protected] "\'a"
163 [email protected] ">"
164 [email protected] " "
165 [email protected]
166 [email protected] "for"
167 [email protected]
168 [email protected] "<"
169 [email protected]
170 [email protected] "\'b"
171 [email protected] ">"
172 [email protected] " "
173 [email protected]
174 [email protected] "for"
175 [email protected]
176 [email protected] "<"
177 [email protected]
178 [email protected] "\'c"
179 [email protected] ">"
180 [email protected] " "
181 [email protected]
182 [email protected] "fn"
183 [email protected]
184 [email protected] "("
185 [email protected]
186 [email protected]
187 [email protected] "&"
188 [email protected] "\'a"
189 [email protected] " "
190 [email protected]
191 [email protected]
192 [email protected]
193 [email protected]
194 [email protected] "T"
195 [email protected] ","
196 [email protected] " "
197 [email protected]
198 [email protected]
199 [email protected] "&"
200 [email protected] "\'b"
201 [email protected] " "
202 [email protected]
203 [email protected]
204 [email protected]
205 [email protected]
206 [email protected] "T"
207 [email protected] ","
208 [email protected] " "
209 [email protected]
210 [email protected]
211 [email protected] "&"
212 [email protected] "\'c"
213 [email protected] " "
214 [email protected]
215 [email protected]
216 [email protected]
217 [email protected]
218 [email protected] "T"
219 [email protected] ")"
220 [email protected] ":"
221 [email protected] " "
222 [email protected]
223 [email protected]
224 [email protected]
225 [email protected]
226 [email protected]
227 [email protected]
228 [email protected] "Copy"
229 [email protected] ","
230 [email protected] "\n"
231 [email protected]
232 [email protected] "{"
233 [email protected] "\n"
234 [email protected] "}"
235 [email protected] "\n"
236error 21..21: expected a function pointer or path
237error 52..52: expected a function pointer or path
238error 88..88: expected a function pointer or path
239error 119..119: expected a function pointer or path
240error 195..195: expected a function pointer or path
diff --git a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs
new file mode 100644
index 000000000..0e9f8ccb4
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs
@@ -0,0 +1,9 @@
1type ForRef = for<'a> &'a u32;
2type ForTup = for<'a> (&'a u32,);
3type ForSlice = for<'a> [u32];
4type ForForFn = for<'a> for<'b> fn(&'a i32, &'b i32);
5fn for_for_for<T>()
6where
7 for<'a> for<'b> for<'c> fn(&'a T, &'b T, &'c T): Copy,
8{
9}
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast b/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast
index 9be441110..53f7ebaf9 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast
+++ b/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast
@@ -17,23 +17,29 @@ [email protected]
17 [email protected] "{" 17 [email protected] "{"
18 [email protected] "}" 18 [email protected] "}"
19 [email protected] "\n" 19 [email protected] "\n"
20 ERROR@25..31 20 CONST_DEF@25..46
21 [email protected] "unsafe" 21 [email protected] "unsafe"
22 [email protected] " " 22 [email protected] " "
23 [email protected]
24 [email protected] "const" 23 [email protected] "const"
25 [email protected] " " 24 [email protected] " "
26 [email protected] "fn" 25 [email protected]
26 [email protected] "fn"
27 [email protected] " " 27 [email protected] " "
28 [email protected] 28 [email protected]
29 [email protected] "bar" 29 [email protected]
30 [email protected] 30 [email protected]
31 [email protected] "(" 31 [email protected]
32 [email protected] ")" 32 [email protected] "bar"
33 [email protected] " " 33 [email protected]
34 [email protected] 34 [email protected] "("
35 [email protected] "{" 35 [email protected] ")"
36 [email protected] "}" 36 [email protected] " "
37 [email protected]
38 [email protected] "{"
39 [email protected] "}"
37 [email protected] "\n" 40 [email protected] "\n"
38error 6..6: expected existential, fn, trait or impl 41error 6..6: expected existential, fn, trait or impl
39error 31..31: expected existential, fn, trait or impl 42error 38..38: expected a name
43error 40..40: expected COLON
44error 46..46: expected SEMICOLON
45error 47..47: expected an item
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
index 9dc473e43..cd0892451 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
@@ -1,61 +1,60 @@
1[email protected]9 1SOURCE_FILE@0..54
2 FN_DEF@0..48 2 FN_DEF@0..53
3 [email protected] "fn" 3 [email protected] "fn"
4 [email protected] " " 4 [email protected] " "
5 [email protected] 5 [email protected]
6 [email protected] "test" 6 [email protected] "for_trait"
7 [email protected] 7 [email protected]
8 [email protected] "<" 8 [email protected] "<"
9 [email protected] 9 [email protected]
10 [email protected] 10 [email protected]
11 [email protected] "F" 11 [email protected] "F"
12 [email protected] ">" 12 [email protected] ">"
13 [email protected] 13 [email protected]
14 [email protected] "(" 14 [email protected] "("
15 [email protected] ")" 15 [email protected] ")"
16 [email protected] "\n" 16 [email protected] "\n"
17 [email protected] 17 [email protected]
18 [email protected] "where" 18 [email protected] "where"
19 [email protected] "\n " 19 [email protected] "\n "
20 [email protected] 20 [email protected]
21 [email protected] 21 [email protected] "for"
22 [email protected] "for" 22 [email protected]
23 [email protected] 23 [email protected] "<"
24 [email protected] "<" 24 [email protected]
25 [email protected] 25 [email protected] "\'a"
26 [email protected] "\'a" 26 [email protected] ">"
27 [email protected] ">" 27 [email protected] " "
28 [email protected] " " 28 [email protected]
29 [email protected] 29 [email protected]
30 [email protected] 30 [email protected]
31 [email protected] 31 [email protected]
32 [email protected] 32 [email protected] "F"
33 [email protected] "F" 33 [email protected] ":"
34 [email protected] ":" 34 [email protected] " "
35 [email protected] " " 35 [email protected]
36 [email protected] 36 [email protected]
37 [email protected] 37 [email protected]
38 [email protected] 38 [email protected]
39 [email protected] 39 [email protected]
40 [email protected] 40 [email protected]
41 [email protected] 41 [email protected] "Fn"
42 [email protected] "Fn" 42 [email protected]
43 [email protected] 43 [email protected] "("
44 [email protected] "(" 44 [email protected]
45 [email protected] 45 [email protected]
46 [email protected] 46 [email protected] "&"
47 [email protected] "&" 47 [email protected] "\'a"
48 [email protected] "\'a" 48 [email protected] " "
49 [email protected] " " 49 [email protected]
50 [email protected] 50 [email protected]
51 [email protected] 51 [email protected]
52 [email protected] 52 [email protected]
53 [email protected] 53 [email protected] "str"
54 [email protected] "str" 54 [email protected] ")"
55 [email protected] ")" 55 [email protected] "\n"
56 [email protected] "\n" 56 [email protected]
57 [email protected] 57 [email protected] "{"
58 [email protected] "{" 58 [email protected] " "
59 [email protected] " " 59 [email protected] "}"
60 [email protected] "}" 60 [email protected] "\n"
61 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs
index b448c6178..423bc105b 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs
@@ -1,4 +1,4 @@
1fn test<F>() 1fn for_trait<F>()
2where 2where
3 for<'a> F: Fn(&'a str) 3 for<'a> F: Fn(&'a str)
4{ } 4{ }
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast
index dfb8d57ad..b26ac2d36 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast
@@ -1,4 +1,4 @@
1[email protected]00 1SOURCE_FILE@0..121
2 [email protected] 2 [email protected]
3 [email protected] "type" 3 [email protected] "type"
4 [email protected] " " 4 [email protected] " "
@@ -29,212 +29,84 @@ [email protected]
29 [email protected] ")" 29 [email protected] ")"
30 [email protected] ";" 30 [email protected] ";"
31 [email protected] "\n" 31 [email protected] "\n"
32 [email protected] 32 [email protected]
33 [email protected] "fn" 33 [email protected] "type"
34 [email protected] " " 34 [email protected] " "
35 [email protected] 35 [email protected]
36 [email protected] "foo" 36 [email protected] "B"
37 [email protected] 37 [email protected] " "
38 [email protected] "<" 38 [email protected] "="
39 [email protected] 39 [email protected] " "
40 [email protected] 40 [email protected]
41 [email protected] "T" 41 [email protected] "for"
42 [email protected] ">" 42 [email protected]
43 [email protected] 43 [email protected] "<"
44 [email protected] "(" 44 [email protected]
45 [email protected] 45 [email protected] "\'a"
46 [email protected] 46 [email protected] ">"
47 [email protected] 47 [email protected] " "
48 [email protected] "_t" 48 [email protected]
49 [email protected] ":" 49 [email protected] "unsafe"
50 [email protected] " " 50 [email protected] " "
51 [email protected] 51 [email protected]
52 [email protected] "&" 52 [email protected] "extern"
53 [email protected] 53 [email protected] " "
54 [email protected] 54 [email protected] "\"C\""
55 [email protected] 55 [email protected] " "
56 [email protected] 56 [email protected] "fn"
57 [email protected] "T" 57 [email protected]
58 [email protected] ")" 58 [email protected] "("
59 [email protected] " " 59 [email protected]
60 [email protected] 60 [email protected]
61 [email protected] "where" 61 [email protected] "&"
62 [email protected] " " 62 [email protected] "\'a"
63 [email protected] 63 [email protected] " "
64 [email protected] 64 [email protected]
65 [email protected] "for" 65 [email protected] "("
66 [email protected] 66 [email protected] ")"
67 [email protected] "<" 67 [email protected] ")"
68 [email protected] 68 [email protected] " "
69 [email protected] "\'a" 69 [email protected]
70 [email protected] ">" 70 [email protected] "->"
71 [email protected] " " 71 [email protected] " "
72 [email protected] 72 [email protected]
73 [email protected] "&" 73 [email protected] "("
74 [email protected] "\'a" 74 [email protected] ")"
75 [email protected] " " 75 [email protected] ";"
76 [email protected] 76 [email protected] "\n"
77 [email protected] 77 [email protected]
78 [email protected] 78 [email protected] "type"
79 [email protected] 79 [email protected] " "
80 [email protected] "T" 80 [email protected]
81 [email protected] ":" 81 [email protected] "Obj"
82 [email protected] " " 82 [email protected] " "
83 [email protected] 83 [email protected] "="
84 [email protected] 84 [email protected] " "
85 [email protected] 85 [email protected]
86 [email protected] 86 [email protected] "for"
87 [email protected] 87 [email protected]
88 [email protected] 88 [email protected] "<"
89 [email protected] "Iterator" 89 [email protected]
90 [email protected] " " 90 [email protected] "\'a"
91 [email protected] 91 [email protected] ">"
92 [email protected] "{" 92 [email protected] " "
93 [email protected] "}" 93 [email protected]
94 [email protected] "\n" 94 [email protected]
95 [email protected] 95 [email protected]
96 [email protected] "fn" 96 [email protected]
97 [email protected] " " 97 [email protected] "PartialEq"
98 [email protected] 98 [email protected]
99 [email protected] "bar" 99 [email protected] "<"
100 [email protected] 100 [email protected]
101 [email protected] "<" 101 [email protected]
102 [email protected] 102 [email protected] "&"
103 [email protected] 103 [email protected] "\'a"
104 [email protected] "T" 104 [email protected] " "
105 [email protected] ">" 105 [email protected]
106 [email protected] 106 [email protected]
107 [email protected] "(" 107 [email protected]
108 [email protected] 108 [email protected]
109 [email protected] 109 [email protected] "i32"
110 [email protected] 110 [email protected] ">"
111 [email protected] "_t" 111 [email protected] ";"
112 [email protected] ":" 112 [email protected] "\n"
113 [email protected] " "
114 [email protected]
115 [email protected] "&"
116 [email protected]
117 [email protected]
118 [email protected]
119 [email protected]
120 [email protected] "T"
121 [email protected] ")"
122 [email protected] " "
123 [email protected]
124 [email protected] "where"
125 [email protected] " "
126 [email protected]
127 [email protected]
128 [email protected] "for"
129 [email protected]
130 [email protected] "<"
131 [email protected]
132 [email protected] "\'a"
133 [email protected] ">"
134 [email protected] " "
135 [email protected]
136 [email protected] "&"
137 [email protected] "\'a"
138 [email protected] " "
139 [email protected] "mut"
140 [email protected] " "
141 [email protected]
142 [email protected]
143 [email protected]
144 [email protected]
145 [email protected] "T"
146 [email protected] ":"
147 [email protected] " "
148 [email protected]
149 [email protected]
150 [email protected]
151 [email protected]
152 [email protected]
153 [email protected]
154 [email protected] "Iterator"
155 [email protected] " "
156 [email protected]
157 [email protected] "{"
158 [email protected] "}"
159 [email protected] "\n"
160 [email protected]
161 [email protected] "fn"
162 [email protected] " "
163 [email protected]
164 [email protected] "baz"
165 [email protected]
166 [email protected] "<"
167 [email protected]
168 [email protected]
169 [email protected] "T"
170 [email protected] ">"
171 [email protected]
172 [email protected] "("
173 [email protected]
174 [email protected]
175 [email protected]
176 [email protected] "_t"
177 [email protected] ":"
178 [email protected] " "
179 [email protected]
180 [email protected] "&"
181 [email protected]
182 [email protected]
183 [email protected]
184 [email protected]
185 [email protected] "T"
186 [email protected] ")"
187 [email protected] " "
188 [email protected]
189 [email protected] "where"
190 [email protected] " "
191 [email protected]
192 [email protected]
193 [email protected] "for"
194 [email protected]
195 [email protected] "<"
196 [email protected]
197 [email protected] "\'a"
198 [email protected] ">"
199 [email protected] " "
200 [email protected]
201 [email protected]
202 [email protected]
203 [email protected]
204 [email protected] "<"
205 [email protected]
206 [email protected] "&"
207 [email protected] "\'a"
208 [email protected] " "
209 [email protected]
210 [email protected]
211 [email protected]
212 [email protected]
213 [email protected] "T"
214 [email protected] " "
215 [email protected] "as"
216 [email protected] " "
217 [email protected]
218 [email protected]
219 [email protected]
220 [email protected]
221 [email protected] "Baz"
222 [email protected] ">"
223 [email protected] "::"
224 [email protected]
225 [email protected]
226 [email protected] "Foo"
227 [email protected] ":"
228 [email protected] " "
229 [email protected]
230 [email protected]
231 [email protected]
232 [email protected]
233 [email protected]
234 [email protected]
235 [email protected] "Iterator"
236 [email protected] " "
237 [email protected]
238 [email protected] "{"
239 [email protected] "}"
240 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs
index d6774d438..8ac7b9e10 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs
@@ -1,4 +1,3 @@
1type A = for<'a> fn() -> (); 1type A = for<'a> fn() -> ();
2fn foo<T>(_t: &T) where for<'a> &'a T: Iterator {} 2type B = for<'a> unsafe extern "C" fn(&'a ()) -> ();
3fn bar<T>(_t: &T) where for<'a> &'a mut T: Iterator {} 3type Obj = for<'a> PartialEq<&'a i32>;
4fn baz<T>(_t: &T) where for<'a> <&'a T as Baz>::Foo: Iterator {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast
new file mode 100644
index 000000000..adb6159f4
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast
@@ -0,0 +1,40 @@
1[email protected]
2 [email protected]
3 [email protected] "impl"
4 [email protected] " "
5 [email protected]
6 [email protected]
7 [email protected]
8 [email protected]
9 [email protected] "T"
10 [email protected] " "
11 [email protected] "for"
12 [email protected] " "
13 [email protected]
14 [email protected]
15 [email protected]
16 [email protected]
17 [email protected] "Foo"
18 [email protected] " "
19 [email protected]
20 [email protected] "{"
21 [email protected] "\n "
22 [email protected]
23 [email protected] "default"
24 [email protected] " "
25 [email protected] "unsafe"
26 [email protected] " "
27 [email protected] "fn"
28 [email protected] " "
29 [email protected]
30 [email protected] "foo"
31 [email protected]
32 [email protected] "("
33 [email protected] ")"
34 [email protected] " "
35 [email protected]
36 [email protected] "{"
37 [email protected] "}"
38 [email protected] "\n"
39 [email protected] "}"
40 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs
new file mode 100644
index 000000000..12926cd8a
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs
@@ -0,0 +1,3 @@
1impl T for Foo {
2 default unsafe fn foo() {}
3}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast
new file mode 100644
index 000000000..a9eda5668
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast
@@ -0,0 +1,18 @@
1[email protected]
2 [email protected]
3 [email protected] "default"
4 [email protected] " "
5 [email protected] "unsafe"
6 [email protected] " "
7 [email protected] "impl"
8 [email protected] " "
9 [email protected]
10 [email protected]
11 [email protected]
12 [email protected]
13 [email protected] "Foo"
14 [email protected] " "
15 [email protected]
16 [email protected] "{"
17 [email protected] "}"
18 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs
new file mode 100644
index 000000000..ba0998ff4
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs
@@ -0,0 +1 @@
default unsafe impl Foo {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast
new file mode 100644
index 000000000..868899275
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast
@@ -0,0 +1,38 @@
1[email protected]
2 [email protected]
3 [email protected] "fn"
4 [email protected] " "
5 [email protected]
6 [email protected] "main"
7 [email protected]
8 [email protected] "("
9 [email protected] ")"
10 [email protected] " "
11 [email protected]
12 [email protected] "{"
13 [email protected] " "
14 [email protected]
15 [email protected] "let"
16 [email protected] " "
17 [email protected]
18 [email protected]
19 [email protected]
20 [email protected]
21 [email protected] "<"
22 [email protected]
23 [email protected] "_"
24 [email protected] ">"
25 [email protected] "::"
26 [email protected]
27 [email protected]
28 [email protected] "Foo"
29 [email protected] " "
30 [email protected] "="
31 [email protected] " "
32 [email protected]
33 [email protected] "("
34 [email protected] ")"
35 [email protected] ";"
36 [email protected] " "
37 [email protected] "}"
38 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs
new file mode 100644
index 000000000..ebe26834d
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs
@@ -0,0 +1 @@
fn main() { let <_>::Foo = (); }
diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast
new file mode 100644
index 000000000..dab0247ee
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast
@@ -0,0 +1,44 @@
1[email protected]
2 [email protected]
3 [email protected] "impl"
4 [email protected] " "
5 [email protected]
6 [email protected]
7 [email protected]
8 [email protected]
9 [email protected] "T"
10 [email protected] " "
11 [email protected] "for"
12 [email protected] " "
13 [email protected]
14 [email protected]
15 [email protected]
16 [email protected]
17 [email protected] "Foo"
18 [email protected] " "
19 [email protected]
20 [email protected] "{"
21 [email protected] "\n "
22 [email protected]
23 [email protected] "default"
24 [email protected] " "
25 [email protected] "const"
26 [email protected] " "
27 [email protected]
28 [email protected] "f"
29 [email protected] ":"
30 [email protected] " "
31 [email protected]
32 [email protected]
33 [email protected]
34 [email protected]
35 [email protected] "u8"
36 [email protected] " "
37 [email protected] "="
38 [email protected] " "
39 [email protected]
40 [email protected] "0"
41 [email protected] ";"
42 [email protected] "\n"
43 [email protected] "}"
44 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs
new file mode 100644
index 000000000..dfb3b92dc
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs
@@ -0,0 +1,3 @@
1impl T for Foo {
2 default const f: u8 = 0;
3}
diff --git a/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast
new file mode 100644
index 000000000..503585103
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast
@@ -0,0 +1,392 @@
1[email protected]
2 [email protected]
3 [email protected] "fn"
4 [email protected] " "
5 [email protected]
6 [email protected] "for_trait"
7 [email protected]
8 [email protected] "<"
9 [email protected]
10 [email protected]
11 [email protected] "F"
12 [email protected] ">"
13 [email protected]
14 [email protected] "("
15 [email protected] ")"
16 [email protected] "\n"
17 [email protected]
18 [email protected] "where"
19 [email protected] "\n "
20 [email protected]
21 [email protected] "for"
22 [email protected]
23 [email protected] "<"
24 [email protected]
25 [email protected] "\'a"
26 [email protected] ">"
27 [email protected] " "
28 [email protected]
29 [email protected]
30 [email protected]
31 [email protected]
32 [email protected] "F"
33 [email protected] ":"
34 [email protected] " "
35 [email protected]
36 [email protected]
37 [email protected]
38 [email protected]
39 [email protected]
40 [email protected]
41 [email protected] "Fn"
42 [email protected]
43 [email protected] "("
44 [email protected]
45 [email protected]
46 [email protected] "&"
47 [email protected] "\'a"
48 [email protected] " "
49 [email protected]
50 [email protected]
51 [email protected]
52 [email protected]
53 [email protected] "str"
54 [email protected] ")"
55 [email protected] ","
56 [email protected] "\n"
57 [email protected]
58 [email protected] "{"
59 [email protected] "\n"
60 [email protected] "}"
61 [email protected] "\n"
62 [email protected]
63 [email protected] "fn"
64 [email protected] " "
65 [email protected]
66 [email protected] "for_ref"
67 [email protected]
68 [email protected] "<"
69 [email protected]
70 [email protected]
71 [email protected] "F"
72 [email protected] ">"
73 [email protected]
74 [email protected] "("
75 [email protected] ")"
76 [email protected] "\n"
77 [email protected]
78 [email protected] "where"
79 [email protected] "\n "
80 [email protected]
81 [email protected] "for"
82 [email protected]
83 [email protected] "<"
84 [email protected]
85 [email protected] "\'a"
86 [email protected] ">"
87 [email protected] " "
88 [email protected]
89 [email protected] "&"
90 [email protected] "\'a"
91 [email protected] " "
92 [email protected]
93 [email protected]
94 [email protected]
95 [email protected]
96 [email protected] "F"
97 [email protected] ":"
98 [email protected] " "
99 [email protected]
100 [email protected]
101 [email protected]
102 [email protected]
103 [email protected]
104 [email protected]
105 [email protected] "Debug"
106 [email protected] ","
107 [email protected] "\n"
108 [email protected]
109 [email protected] "{"
110 [email protected] "\n"
111 [email protected] "}"
112 [email protected] "\n"
113 [email protected]
114 [email protected] "fn"
115 [email protected] " "
116 [email protected]
117 [email protected] "for_parens"
118 [email protected]
119 [email protected] "<"
120 [email protected]
121 [email protected]
122 [email protected] "F"
123 [email protected] ">"
124 [email protected]
125 [email protected] "("
126 [email protected] ")"
127 [email protected] "\n"
128 [email protected]
129 [email protected] "where"
130 [email protected] "\n "
131 [email protected]
132 [email protected] "for"
133 [email protected]
134 [email protected] "<"
135 [email protected]
136 [email protected] "\'a"
137 [email protected] ">"
138 [email protected] " "
139 [email protected]
140 [email protected] "("
141 [email protected]
142 [email protected] "&"
143 [email protected] "\'a"
144 [email protected] " "
145 [email protected]
146 [email protected]
147 [email protected]
148 [email protected]
149 [email protected] "F"
150 [email protected] ")"
151 [email protected] ":"
152 [email protected] " "
153 [email protected]
154 [email protected]
155 [email protected]
156 [email protected]
157 [email protected]
158 [email protected]
159 [email protected] "Fn"
160 [email protected]
161 [email protected] "("
162 [email protected]
163 [email protected]
164 [email protected] "&"
165 [email protected] "\'a"
166 [email protected] " "
167 [email protected]
168 [email protected]
169 [email protected]
170 [email protected]
171 [email protected] "str"
172 [email protected] ")"
173 [email protected] ","
174 [email protected] "\n"
175 [email protected]
176 [email protected] "{"
177 [email protected] "\n"
178 [email protected] "}"
179 [email protected] "\n"
180 [email protected]
181 [email protected] "fn"
182 [email protected] " "
183 [email protected]
184 [email protected] "for_slice"
185 [email protected]
186 [email protected] "<"
187 [email protected]
188 [email protected]
189 [email protected] "F"
190 [email protected] ">"
191 [email protected]
192 [email protected] "("
193 [email protected] ")"
194 [email protected] "\n"
195 [email protected]
196 [email protected] "where"
197 [email protected] "\n "
198 [email protected]
199 [email protected] "for"
200 [email protected]
201 [email protected] "<"
202 [email protected]
203 [email protected] "\'a"
204 [email protected] ">"
205 [email protected] " "
206 [email protected]
207 [email protected] "["
208 [email protected]
209 [email protected] "&"
210 [email protected] "\'a"
211 [email protected] " "
212 [email protected]
213 [email protected]
214 [email protected]
215 [email protected]
216 [email protected] "F"
217 [email protected] "]"
218 [email protected] ":"
219 [email protected] " "
220 [email protected]
221 [email protected]
222 [email protected]
223 [email protected]
224 [email protected]
225 [email protected]
226 [email protected] "Eq"
227 [email protected] ","
228 [email protected] "\n"
229 [email protected]
230 [email protected] "{"
231 [email protected] "\n"
232 [email protected] "}"
233 [email protected] "\n"
234 [email protected]
235 [email protected] "fn"
236 [email protected] " "
237 [email protected]
238 [email protected] "for_qpath"
239 [email protected]
240 [email protected] "<"
241 [email protected]
242 [email protected]
243 [email protected] "T"
244 [email protected] ">"
245 [email protected]
246 [email protected] "("
247 [email protected]
248 [email protected]
249 [email protected]
250 [email protected] "_t"
251 [email protected] ":"
252 [email protected] " "
253 [email protected]
254 [email protected] "&"
255 [email protected]
256 [email protected]
257 [email protected]
258 [email protected]
259 [email protected] "T"
260 [email protected] ")"
261 [email protected] "\n"
262 [email protected]
263 [email protected] "where"
264 [email protected] "\n "
265 [email protected]
266 [email protected] "for"
267 [email protected]
268 [email protected] "<"
269 [email protected]
270 [email protected] "\'a"
271 [email protected] ">"
272 [email protected] " "
273 [email protected]
274 [email protected]
275 [email protected]
276 [email protected]
277 [email protected] "<"
278 [email protected]
279 [email protected] "&"
280 [email protected] "\'a"
281 [email protected] " "
282 [email protected]
283 [email protected]
284 [email protected]
285 [email protected]
286 [email protected] "T"
287 [email protected] " "
288 [email protected] "as"
289 [email protected] " "
290 [email protected]
291 [email protected]
292 [email protected]
293 [email protected]
294 [email protected] "Baz"
295 [email protected] ">"
296 [email protected] "::"
297 [email protected]
298 [email protected]
299 [email protected] "Foo"
300 [email protected] ":"
301 [email protected] " "
302 [email protected]
303 [email protected]
304 [email protected]
305 [email protected]
306 [email protected]
307 [email protected]
308 [email protected] "Iterator"
309 [email protected] ","
310 [email protected] "\n"
311 [email protected]
312 [email protected] "{"
313 [email protected] "\n"
314 [email protected] "}"
315 [email protected] "\n"
316 [email protected]
317 [email protected] "fn"
318 [email protected] " "
319 [email protected]
320 [email protected] "for_for_fn"
321 [email protected]
322 [email protected] "<"
323 [email protected]
324 [email protected]
325 [email protected] "T"
326 [email protected] ">"
327 [email protected]
328 [email protected] "("
329 [email protected] ")"
330 [email protected] "\n"
331 [email protected]
332 [email protected] "where"
333 [email protected] "\n "
334 [email protected]
335 [email protected] "for"
336 [email protected]
337 [email protected] "<"
338 [email protected]
339 [email protected] "\'a"
340 [email protected] ">"
341 [email protected] " "
342 [email protected]
343 [email protected] "for"
344 [email protected]
345 [email protected] "<"
346 [email protected]
347 [email protected] "\'b"
348 [email protected] ">"
349 [email protected] " "
350 [email protected]
351 [email protected] "fn"
352 [email protected]
353 [email protected] "("
354 [email protected]
355 [email protected]
356 [email protected] "&"
357 [email protected] "\'a"
358 [email protected] " "
359 [email protected]
360 [email protected]
361 [email protected]
362 [email protected]
363 [email protected] "T"
364 [email protected] ","
365 [email protected] " "
366 [email protected]
367 [email protected]
368 [email protected] "&"
369 [email protected] "\'b"
370 [email protected] " "
371 [email protected]
372 [email protected]
373 [email protected]
374 [email protected]
375 [email protected] "T"
376 [email protected] ")"
377 [email protected] ":"
378 [email protected] " "
379 [email protected]
380 [email protected]
381 [email protected]
382 [email protected]
383 [email protected]
384 [email protected]
385 [email protected] "Copy"
386 [email protected] ","
387 [email protected] "\n"
388 [email protected]
389 [email protected] "{"
390 [email protected] "\n"
391 [email protected] "}"
392 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs
new file mode 100644
index 000000000..9058c4619
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs
@@ -0,0 +1,30 @@
1fn for_trait<F>()
2where
3 for<'a> F: Fn(&'a str),
4{
5}
6fn for_ref<F>()
7where
8 for<'a> &'a F: Debug,
9{
10}
11fn for_parens<F>()
12where
13 for<'a> (&'a F): Fn(&'a str),
14{
15}
16fn for_slice<F>()
17where
18 for<'a> [&'a F]: Eq,
19{
20}
21fn for_qpath<T>(_t: &T)
22where
23 for<'a> <&'a T as Baz>::Foo: Iterator,
24{
25}
26fn for_for_fn<T>()
27where
28 for<'a> for<'b> fn(&'a T, &'b T): Copy,
29{
30}
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 65b487db3..458089e53 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -10,7 +10,7 @@ doctest = false
10 10
11[[bin]] 11[[bin]]
12name = "rust-analyzer" 12name = "rust-analyzer"
13path = "./src/bin/main.rs" 13path = "src/bin/main.rs"
14 14
15[dependencies] 15[dependencies]
16anyhow = "1.0.26" 16anyhow = "1.0.26"
@@ -32,7 +32,7 @@ threadpool = "1.7.1"
32 32
33stdx = { path = "../stdx" } 33stdx = { path = "../stdx" }
34 34
35lsp-server = "0.3.1" 35lsp-server = "0.3.2"
36ra_flycheck = { path = "../ra_flycheck" } 36ra_flycheck = { path = "../ra_flycheck" }
37ra_ide = { path = "../ra_ide" } 37ra_ide = { path = "../ra_ide" }
38ra_prof = { path = "../ra_prof" } 38ra_prof = { path = "../ra_prof" }
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index e82fd57de..8d071ab1c 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -4,9 +4,14 @@
4mod args; 4mod args;
5 5
6use lsp_server::Connection; 6use lsp_server::Connection;
7use rust_analyzer::{cli, config::Config, from_json, Result}; 7use rust_analyzer::{
8 cli,
9 config::{Config, LinkedProject},
10 from_json, Result,
11};
8 12
9use crate::args::HelpPrinted; 13use crate::args::HelpPrinted;
14use ra_project_model::ProjectManifest;
10 15
11fn main() -> Result<()> { 16fn main() -> Result<()> {
12 setup_logging()?; 17 setup_logging()?;
@@ -97,17 +102,6 @@ fn run_server() -> Result<()> {
97 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); 102 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
98 } 103 }
99 104
100 let cwd = std::env::current_dir()?;
101 let root = initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
102
103 let workspace_roots = initialize_params
104 .workspace_folders
105 .map(|workspaces| {
106 workspaces.into_iter().filter_map(|it| it.uri.to_file_path().ok()).collect::<Vec<_>>()
107 })
108 .filter(|workspaces| !workspaces.is_empty())
109 .unwrap_or_else(|| vec![root]);
110
111 let config = { 105 let config = {
112 let mut config = Config::default(); 106 let mut config = Config::default();
113 if let Some(value) = &initialize_params.initialization_options { 107 if let Some(value) = &initialize_params.initialization_options {
@@ -115,10 +109,31 @@ fn run_server() -> Result<()> {
115 } 109 }
116 config.update_caps(&initialize_params.capabilities); 110 config.update_caps(&initialize_params.capabilities);
117 111
112 if config.linked_projects.is_empty() {
113 let cwd = std::env::current_dir()?;
114 let root =
115 initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
116 let workspace_roots = initialize_params
117 .workspace_folders
118 .map(|workspaces| {
119 workspaces
120 .into_iter()
121 .filter_map(|it| it.uri.to_file_path().ok())
122 .collect::<Vec<_>>()
123 })
124 .filter(|workspaces| !workspaces.is_empty())
125 .unwrap_or_else(|| vec![root]);
126
127 config.linked_projects = ProjectManifest::discover_all(&workspace_roots)
128 .into_iter()
129 .map(LinkedProject::from)
130 .collect();
131 }
132
118 config 133 config
119 }; 134 };
120 135
121 rust_analyzer::main_loop(workspace_roots, config, connection)?; 136 rust_analyzer::main_loop(config, connection)?;
122 137
123 log::info!("shutting down IO..."); 138 log::info!("shutting down IO...");
124 io_threads.join()?; 139 io_threads.join()?;
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 345693524..673795e78 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -87,6 +87,9 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
87 "ssr": true, 87 "ssr": true,
88 "onEnter": true, 88 "onEnter": true,
89 "parentModule": true, 89 "parentModule": true,
90 "runnables": {
91 "kinds": [ "cargo" ],
92 },
90 })), 93 })),
91 } 94 }
92} 95}
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 441fb61df..44f856f6b 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -1,10 +1,10 @@
1//! See `CargoTargetSpec` 1//! See `CargoTargetSpec`
2 2
3use ra_cfg::CfgExpr;
3use ra_ide::{FileId, RunnableKind, TestId}; 4use ra_ide::{FileId, RunnableKind, TestId};
4use ra_project_model::{self, ProjectWorkspace, TargetKind}; 5use ra_project_model::{self, ProjectWorkspace, TargetKind};
5 6
6use crate::{world::WorldSnapshot, Result}; 7use crate::{global_state::GlobalStateSnapshot, Result};
7use ra_syntax::SmolStr;
8 8
9/// Abstract representation of Cargo target. 9/// Abstract representation of Cargo target.
10/// 10///
@@ -21,7 +21,7 @@ impl CargoTargetSpec {
21 pub(crate) fn runnable_args( 21 pub(crate) fn runnable_args(
22 spec: Option<CargoTargetSpec>, 22 spec: Option<CargoTargetSpec>,
23 kind: &RunnableKind, 23 kind: &RunnableKind,
24 features_needed: &Vec<SmolStr>, 24 cfgs: &[CfgExpr],
25 ) -> Result<(Vec<String>, Vec<String>)> { 25 ) -> Result<(Vec<String>, Vec<String>)> {
26 let mut args = Vec::new(); 26 let mut args = Vec::new();
27 let mut extra_args = Vec::new(); 27 let mut extra_args = Vec::new();
@@ -76,16 +76,20 @@ impl CargoTargetSpec {
76 } 76 }
77 } 77 }
78 78
79 features_needed.iter().for_each(|feature| { 79 let mut features = Vec::new();
80 for cfg in cfgs {
81 required_features(cfg, &mut features);
82 }
83 for feature in features {
80 args.push("--features".to_string()); 84 args.push("--features".to_string());
81 args.push(feature.to_string()); 85 args.push(feature);
82 }); 86 }
83 87
84 Ok((args, extra_args)) 88 Ok((args, extra_args))
85 } 89 }
86 90
87 pub(crate) fn for_file( 91 pub(crate) fn for_file(
88 world: &WorldSnapshot, 92 world: &GlobalStateSnapshot,
89 file_id: FileId, 93 file_id: FileId,
90 ) -> Result<Option<CargoTargetSpec>> { 94 ) -> Result<Option<CargoTargetSpec>> {
91 let &crate_id = match world.analysis().crate_for(file_id)?.first() { 95 let &crate_id = match world.analysis().crate_for(file_id)?.first() {
@@ -140,3 +144,74 @@ impl CargoTargetSpec {
140 } 144 }
141 } 145 }
142} 146}
147
148/// Fill minimal features needed
149fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) {
150 match cfg_expr {
151 CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.to_string()),
152 CfgExpr::All(preds) => {
153 preds.iter().for_each(|cfg| required_features(cfg, features));
154 }
155 CfgExpr::Any(preds) => {
156 for cfg in preds {
157 let len_features = features.len();
158 required_features(cfg, features);
159 if len_features != features.len() {
160 break;
161 }
162 }
163 }
164 _ => {}
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 use mbe::{ast_to_token_tree, TokenMap};
173 use ra_cfg::parse_cfg;
174 use ra_syntax::{
175 ast::{self, AstNode},
176 SmolStr,
177 };
178
179 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
180 let source_file = ast::SourceFile::parse(input).ok().unwrap();
181 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
182 ast_to_token_tree(&tt).unwrap()
183 }
184
185 #[test]
186 fn test_cfg_expr_minimal_features_needed() {
187 let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
188 let cfg_expr = parse_cfg(&subtree);
189 let mut min_features = vec![];
190 required_features(&cfg_expr, &mut min_features);
191
192 assert_eq!(min_features, vec![SmolStr::new("baz")]);
193
194 let (subtree, _) =
195 get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
196 let cfg_expr = parse_cfg(&subtree);
197
198 let mut min_features = vec![];
199 required_features(&cfg_expr, &mut min_features);
200 assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]);
201
202 let (subtree, _) =
203 get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
204 let cfg_expr = parse_cfg(&subtree);
205
206 let mut min_features = vec![];
207 required_features(&cfg_expr, &mut min_features);
208 assert_eq!(min_features, vec![SmolStr::new("baz")]);
209
210 let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
211 let cfg_expr = parse_cfg(&subtree);
212
213 let mut min_features = vec![];
214 required_features(&cfg_expr, &mut min_features);
215 assert!(min_features.is_empty());
216 }
217}
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 8eaf75ff6..97367d7c6 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -8,7 +8,7 @@ use crossbeam_channel::{unbounded, Receiver};
8use ra_db::{ExternSourceId, FileId, SourceRootId}; 8use ra_db::{ExternSourceId, FileId, SourceRootId};
9use ra_ide::{AnalysisChange, AnalysisHost}; 9use ra_ide::{AnalysisChange, AnalysisHost};
10use ra_project_model::{ 10use ra_project_model::{
11 get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectRoot, ProjectWorkspace, 11 CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest, ProjectWorkspace,
12}; 12};
13use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; 13use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
14use rustc_hash::{FxHashMap, FxHashSet}; 14use rustc_hash::{FxHashMap, FxHashSet};
@@ -28,7 +28,7 @@ pub fn load_cargo(
28 with_proc_macro: bool, 28 with_proc_macro: bool,
29) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> { 29) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
30 let root = std::env::current_dir()?.join(root); 30 let root = std::env::current_dir()?.join(root);
31 let root = ProjectRoot::discover_single(&root)?; 31 let root = ProjectManifest::discover_single(&root)?;
32 let ws = ProjectWorkspace::load( 32 let ws = ProjectWorkspace::load(
33 root, 33 root,
34 &CargoConfig { load_out_dirs_from_check, ..Default::default() }, 34 &CargoConfig { load_out_dirs_from_check, ..Default::default() },
@@ -36,28 +36,28 @@ pub fn load_cargo(
36 )?; 36 )?;
37 37
38 let mut extern_dirs = FxHashSet::default(); 38 let mut extern_dirs = FxHashSet::default();
39 extern_dirs.extend(ws.out_dirs());
40
41 let mut project_roots = ws.to_roots();
42 project_roots.extend(extern_dirs.iter().cloned().map(PackageRoot::new_non_member));
43 39
44 let (sender, receiver) = unbounded(); 40 let (sender, receiver) = unbounded();
45 let sender = Box::new(move |t| sender.send(t).unwrap()); 41 let sender = Box::new(move |t| sender.send(t).unwrap());
46 let (mut vfs, roots) = Vfs::new( 42
47 project_roots 43 let mut roots = Vec::new();
48 .iter() 44 let project_roots = ws.to_roots();
49 .map(|pkg_root| { 45 for root in &project_roots {
50 RootEntry::new( 46 roots.push(RootEntry::new(
51 pkg_root.path().to_owned(), 47 root.path().to_owned(),
52 RustPackageFilterBuilder::default() 48 RustPackageFilterBuilder::default().set_member(root.is_member()).into_vfs_filter(),
53 .set_member(pkg_root.is_member()) 49 ));
54 .into_vfs_filter(), 50
55 ) 51 if let Some(out_dir) = root.out_dir() {
56 }) 52 extern_dirs.insert(out_dir.to_path_buf());
57 .collect(), 53 roots.push(RootEntry::new(
58 sender, 54 out_dir.to_owned(),
59 Watch(false), 55 RustPackageFilterBuilder::default().set_member(root.is_member()).into_vfs_filter(),
60 ); 56 ))
57 }
58 }
59
60 let (mut vfs, roots) = Vfs::new(roots, sender, Watch(false));
61 61
62 let source_roots = roots 62 let source_roots = roots
63 .into_iter() 63 .into_iter()
@@ -111,10 +111,6 @@ pub(crate) fn load(
111 vfs.root2path(root) 111 vfs.root2path(root)
112 ); 112 );
113 analysis_change.add_root(source_root_id, is_local); 113 analysis_change.add_root(source_root_id, is_local);
114 analysis_change.set_debug_root_path(
115 source_root_id,
116 source_roots[&source_root_id].path().display().to_string(),
117 );
118 114
119 let vfs_root_path = vfs.root2path(root); 115 let vfs_root_path = vfs.root2path(root);
120 if extern_dirs.contains(&vfs_root_path) { 116 if extern_dirs.contains(&vfs_root_path) {
@@ -147,26 +143,14 @@ pub(crate) fn load(
147 } 143 }
148 } 144 }
149 145
150 // FIXME: cfg options? 146 let crate_graph =
151 let default_cfg_options = { 147 ws.to_crate_graph(None, &extern_source_roots, proc_macro_client, &mut |path: &Path| {
152 let mut opts = get_rustc_cfg_options(None);
153 opts.insert_atom("test".into());
154 opts.insert_atom("debug_assertion".into());
155 opts
156 };
157
158 let crate_graph = ws.to_crate_graph(
159 &default_cfg_options,
160 &extern_source_roots,
161 proc_macro_client,
162 &mut |path: &Path| {
163 // Some path from metadata will be non canonicalized, e.g. /foo/../bar/lib.rs 148 // Some path from metadata will be non canonicalized, e.g. /foo/../bar/lib.rs
164 let path = path.canonicalize().ok()?; 149 let path = path.canonicalize().ok()?;
165 let vfs_file = vfs.load(&path); 150 let vfs_file = vfs.load(&path);
166 log::debug!("vfs file {:?} -> {:?}", path, vfs_file); 151 log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
167 vfs_file.map(vfs_file_to_id) 152 vfs_file.map(vfs_file_to_id)
168 }, 153 });
169 );
170 log::debug!("crate graph: {:?}", crate_graph); 154 log::debug!("crate graph: {:?}", crate_graph);
171 analysis_change.set_crate_graph(crate_graph); 155 analysis_change.set_crate_graph(crate_graph);
172 156
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index c0f7c2c0c..1253db836 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -11,15 +11,14 @@ use std::{ffi::OsString, path::PathBuf};
11 11
12use lsp_types::ClientCapabilities; 12use lsp_types::ClientCapabilities;
13use ra_flycheck::FlycheckConfig; 13use ra_flycheck::FlycheckConfig;
14use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; 14use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
15use ra_project_model::CargoConfig; 15use ra_project_model::{CargoConfig, JsonProject, ProjectManifest};
16use serde::Deserialize; 16use serde::Deserialize;
17 17
18#[derive(Debug, Clone)] 18#[derive(Debug, Clone)]
19pub struct Config { 19pub struct Config {
20 pub client_caps: ClientCapsConfig, 20 pub client_caps: ClientCapsConfig,
21 21
22 pub with_sysroot: bool,
23 pub publish_diagnostics: bool, 22 pub publish_diagnostics: bool,
24 pub lru_capacity: Option<usize>, 23 pub lru_capacity: Option<usize>,
25 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>, 24 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
@@ -35,6 +34,28 @@ pub struct Config {
35 pub assist: AssistConfig, 34 pub assist: AssistConfig,
36 pub call_info_full: bool, 35 pub call_info_full: bool,
37 pub lens: LensConfig, 36 pub lens: LensConfig,
37 pub hover: HoverConfig,
38
39 pub with_sysroot: bool,
40 pub linked_projects: Vec<LinkedProject>,
41}
42
43#[derive(Debug, Clone)]
44pub enum LinkedProject {
45 ProjectManifest(ProjectManifest),
46 JsonProject(JsonProject),
47}
48
49impl From<ProjectManifest> for LinkedProject {
50 fn from(v: ProjectManifest) -> Self {
51 LinkedProject::ProjectManifest(v)
52 }
53}
54
55impl From<JsonProject> for LinkedProject {
56 fn from(v: JsonProject) -> Self {
57 LinkedProject::JsonProject(v)
58 }
38} 59}
39 60
40#[derive(Clone, Debug, PartialEq, Eq)] 61#[derive(Clone, Debug, PartialEq, Eq)]
@@ -103,6 +124,8 @@ pub struct ClientCapsConfig {
103 pub code_action_literals: bool, 124 pub code_action_literals: bool,
104 pub work_done_progress: bool, 125 pub work_done_progress: bool,
105 pub code_action_group: bool, 126 pub code_action_group: bool,
127 pub resolve_code_action: bool,
128 pub hover_actions: bool,
106} 129}
107 130
108impl Default for Config { 131impl Default for Config {
@@ -122,8 +145,9 @@ impl Default for Config {
122 check: Some(FlycheckConfig::CargoCommand { 145 check: Some(FlycheckConfig::CargoCommand {
123 command: "check".to_string(), 146 command: "check".to_string(),
124 all_targets: true, 147 all_targets: true,
125 all_features: true, 148 all_features: false,
126 extra_args: Vec::new(), 149 extra_args: Vec::new(),
150 features: Vec::new(),
127 }), 151 }),
128 152
129 inlay_hints: InlayHintsConfig { 153 inlay_hints: InlayHintsConfig {
@@ -141,6 +165,8 @@ impl Default for Config {
141 assist: AssistConfig::default(), 165 assist: AssistConfig::default(),
142 call_info_full: true, 166 call_info_full: true,
143 lens: LensConfig::default(), 167 lens: LensConfig::default(),
168 hover: HoverConfig::default(),
169 linked_projects: Vec::new(),
144 } 170 }
145 } 171 }
146} 172}
@@ -209,13 +235,14 @@ impl Config {
209 } 235 }
210 // otherwise configure command customizations 236 // otherwise configure command customizations
211 _ => { 237 _ => {
212 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features }) 238 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features, features })
213 = &mut self.check 239 = &mut self.check
214 { 240 {
215 set(value, "/checkOnSave/extraArgs", extra_args); 241 set(value, "/checkOnSave/extraArgs", extra_args);
216 set(value, "/checkOnSave/command", command); 242 set(value, "/checkOnSave/command", command);
217 set(value, "/checkOnSave/allTargets", all_targets); 243 set(value, "/checkOnSave/allTargets", all_targets);
218 set(value, "/checkOnSave/allFeatures", all_features); 244 *all_features = get(value, "/checkOnSave/allFeatures").unwrap_or(self.cargo.all_features);
245 *features = get(value, "/checkOnSave/features").unwrap_or(self.cargo.features.clone());
219 } 246 }
220 } 247 }
221 }; 248 };
@@ -240,6 +267,32 @@ impl Config {
240 self.lens = LensConfig::NO_LENS; 267 self.lens = LensConfig::NO_LENS;
241 } 268 }
242 269
270 if let Some(linked_projects) = get::<Vec<ManifestOrJsonProject>>(value, "/linkedProjects") {
271 if !linked_projects.is_empty() {
272 self.linked_projects.clear();
273 for linked_project in linked_projects {
274 let linked_project = match linked_project {
275 ManifestOrJsonProject::Manifest(it) => match ProjectManifest::from_manifest_file(it) {
276 Ok(it) => it.into(),
277 Err(_) => continue,
278 }
279 ManifestOrJsonProject::JsonProject(it) => it.into(),
280 };
281 self.linked_projects.push(linked_project);
282 }
283 }
284 }
285
286 let mut use_hover_actions = false;
287 set(value, "/hoverActions/enable", &mut use_hover_actions);
288 if use_hover_actions {
289 set(value, "/hoverActions/implementations", &mut self.hover.implementations);
290 set(value, "/hoverActions/run", &mut self.hover.run);
291 set(value, "/hoverActions/debug", &mut self.hover.debug);
292 } else {
293 self.hover = HoverConfig::NO_ACTIONS;
294 }
295
243 log::info!("Config::update() = {:#?}", self); 296 log::info!("Config::update() = {:#?}", self);
244 297
245 fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { 298 fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
@@ -293,13 +346,22 @@ impl Config {
293 346
294 self.assist.allow_snippets(false); 347 self.assist.allow_snippets(false);
295 if let Some(experimental) = &caps.experimental { 348 if let Some(experimental) = &caps.experimental {
296 let snippet_text_edit = 349 let get_bool =
297 experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); 350 |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true);
351
352 let snippet_text_edit = get_bool("snippetTextEdit");
298 self.assist.allow_snippets(snippet_text_edit); 353 self.assist.allow_snippets(snippet_text_edit);
299 354
300 let code_action_group = 355 self.client_caps.code_action_group = get_bool("codeActionGroup");
301 experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true); 356 self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
302 self.client_caps.code_action_group = code_action_group 357 self.client_caps.hover_actions = get_bool("hoverActions");
303 } 358 }
304 } 359 }
305} 360}
361
362#[derive(Deserialize)]
363#[serde(untagged)]
364enum ManifestOrJsonProject {
365 Manifest(PathBuf),
366 JsonProject(JsonProject),
367}
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
index c40cfdcdc..272057b47 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
@@ -65,6 +65,7 @@ expression: diag
65 fixes: [ 65 fixes: [
66 CodeAction { 66 CodeAction {
67 title: "return the expression directly", 67 title: "return the expression directly",
68 id: None,
68 group: None, 69 group: None,
69 kind: Some( 70 kind: Some(
70 "quickfix", 71 "quickfix",
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
index 6dd3fcb2e..f0273315e 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
@@ -29,7 +29,7 @@ expression: diag
29 }, 29 },
30 }, 30 },
31 severity: Some( 31 severity: Some(
32 Warning, 32 Hint,
33 ), 33 ),
34 code: Some( 34 code: Some(
35 String( 35 String(
@@ -50,6 +50,7 @@ expression: diag
50 fixes: [ 50 fixes: [
51 CodeAction { 51 CodeAction {
52 title: "consider prefixing with an underscore", 52 title: "consider prefixing with an underscore",
53 id: None,
53 group: None, 54 group: None,
54 kind: Some( 55 kind: Some(
55 "quickfix", 56 "quickfix",
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index a500d670a..04e286780 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -145,6 +145,7 @@ fn map_rust_child_diagnostic(
145 } else { 145 } else {
146 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { 146 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
147 title: rd.message.clone(), 147 title: rd.message.clone(),
148 id: None,
148 group: None, 149 group: None,
149 kind: Some("quickfix".to_string()), 150 kind: Some("quickfix".to_string()),
150 edit: Some(lsp_ext::SnippetWorkspaceEdit { 151 edit: Some(lsp_ext::SnippetWorkspaceEdit {
@@ -183,7 +184,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
183 return Vec::new(); 184 return Vec::new();
184 } 185 }
185 186
186 let severity = map_level_to_severity(rd.level); 187 let mut severity = map_level_to_severity(rd.level);
187 188
188 let mut source = String::from("rustc"); 189 let mut source = String::from("rustc");
189 let mut code = rd.code.as_ref().map(|c| c.code.clone()); 190 let mut code = rd.code.as_ref().map(|c| c.code.clone());
@@ -225,6 +226,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
225 } 226 }
226 227
227 if is_unused_or_unnecessary(rd) { 228 if is_unused_or_unnecessary(rd) {
229 severity = Some(DiagnosticSeverity::Hint);
228 tags.push(DiagnosticTag::Unnecessary); 230 tags.push(DiagnosticTag::Unnecessary);
229 } 231 }
230 232
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs
index 4bb16a496..206673829 100644
--- a/crates/rust-analyzer/src/from_proto.rs
+++ b/crates/rust-analyzer/src/from_proto.rs
@@ -3,7 +3,7 @@ use ra_db::{FileId, FilePosition, FileRange};
3use ra_ide::{LineCol, LineIndex}; 3use ra_ide::{LineCol, LineIndex};
4use ra_syntax::{TextRange, TextSize}; 4use ra_syntax::{TextRange, TextSize};
5 5
6use crate::{world::WorldSnapshot, Result}; 6use crate::{global_state::GlobalStateSnapshot, Result};
7 7
8pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { 8pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize {
9 let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 }; 9 let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 };
@@ -16,12 +16,12 @@ pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Tex
16 TextRange::new(start, end) 16 TextRange::new(start, end)
17} 17}
18 18
19pub(crate) fn file_id(world: &WorldSnapshot, url: &lsp_types::Url) -> Result<FileId> { 19pub(crate) fn file_id(world: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result<FileId> {
20 world.uri_to_file_id(url) 20 world.uri_to_file_id(url)
21} 21}
22 22
23pub(crate) fn file_position( 23pub(crate) fn file_position(
24 world: &WorldSnapshot, 24 world: &GlobalStateSnapshot,
25 tdpp: lsp_types::TextDocumentPositionParams, 25 tdpp: lsp_types::TextDocumentPositionParams,
26) -> Result<FilePosition> { 26) -> Result<FilePosition> {
27 let file_id = file_id(world, &tdpp.text_document.uri)?; 27 let file_id = file_id(world, &tdpp.text_document.uri)?;
@@ -31,7 +31,7 @@ pub(crate) fn file_position(
31} 31}
32 32
33pub(crate) fn file_range( 33pub(crate) fn file_range(
34 world: &WorldSnapshot, 34 world: &GlobalStateSnapshot,
35 text_document_identifier: lsp_types::TextDocumentIdentifier, 35 text_document_identifier: lsp_types::TextDocumentIdentifier,
36 range: lsp_types::Range, 36 range: lsp_types::Range,
37) -> Result<FileRange> { 37) -> Result<FileRange> {
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/global_state.rs
index 367272925..21116e165 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -15,7 +15,7 @@ use ra_flycheck::{Flycheck, FlycheckConfig};
15use ra_ide::{ 15use ra_ide::{
16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId, 16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId,
17}; 17};
18use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; 18use ra_project_model::{ProcMacroClient, ProjectWorkspace};
19use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; 19use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
20use relative_path::RelativePathBuf; 20use relative_path::RelativePathBuf;
21use stdx::format_to; 21use stdx::format_to;
@@ -50,15 +50,15 @@ fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) ->
50 }) 50 })
51} 51}
52 52
53/// `WorldState` is the primary mutable state of the language server 53/// `GlobalState` is the primary mutable state of the language server
54/// 54///
55/// The most interesting components are `vfs`, which stores a consistent 55/// The most interesting components are `vfs`, which stores a consistent
56/// snapshot of the file systems, and `analysis_host`, which stores our 56/// snapshot of the file systems, and `analysis_host`, which stores our
57/// incremental salsa database. 57/// incremental salsa database.
58#[derive(Debug)] 58#[derive(Debug)]
59pub struct WorldState { 59pub struct GlobalState {
60 pub config: Config, 60 pub config: Config,
61 pub roots: Vec<PathBuf>, 61 pub local_roots: Vec<PathBuf>,
62 pub workspaces: Arc<Vec<ProjectWorkspace>>, 62 pub workspaces: Arc<Vec<ProjectWorkspace>>,
63 pub analysis_host: AnalysisHost, 63 pub analysis_host: AnalysisHost,
64 pub vfs: Arc<RwLock<Vfs>>, 64 pub vfs: Arc<RwLock<Vfs>>,
@@ -70,7 +70,7 @@ pub struct WorldState {
70} 70}
71 71
72/// An immutable snapshot of the world's state at a point in time. 72/// An immutable snapshot of the world's state at a point in time.
73pub struct WorldSnapshot { 73pub struct GlobalStateSnapshot {
74 pub config: Config, 74 pub config: Config,
75 pub workspaces: Arc<Vec<ProjectWorkspace>>, 75 pub workspaces: Arc<Vec<ProjectWorkspace>>,
76 pub analysis: Analysis, 76 pub analysis: Analysis,
@@ -79,20 +79,19 @@ pub struct WorldSnapshot {
79 vfs: Arc<RwLock<Vfs>>, 79 vfs: Arc<RwLock<Vfs>>,
80} 80}
81 81
82impl WorldState { 82impl GlobalState {
83 pub fn new( 83 pub fn new(
84 folder_roots: Vec<PathBuf>,
85 workspaces: Vec<ProjectWorkspace>, 84 workspaces: Vec<ProjectWorkspace>,
86 lru_capacity: Option<usize>, 85 lru_capacity: Option<usize>,
87 exclude_globs: &[Glob], 86 exclude_globs: &[Glob],
88 watch: Watch, 87 watch: Watch,
89 config: Config, 88 config: Config,
90 ) -> WorldState { 89 ) -> GlobalState {
91 let mut change = AnalysisChange::new(); 90 let mut change = AnalysisChange::new();
92 91
93 let extern_dirs: FxHashSet<_> = 92 let mut extern_dirs: FxHashSet<PathBuf> = FxHashSet::default();
94 workspaces.iter().flat_map(ProjectWorkspace::out_dirs).collect();
95 93
94 let mut local_roots = Vec::new();
96 let roots: Vec<_> = { 95 let roots: Vec<_> = {
97 let create_filter = |is_member| { 96 let create_filter = |is_member| {
98 RustPackageFilterBuilder::default() 97 RustPackageFilterBuilder::default()
@@ -100,18 +99,22 @@ impl WorldState {
100 .exclude(exclude_globs.iter().cloned()) 99 .exclude(exclude_globs.iter().cloned())
101 .into_vfs_filter() 100 .into_vfs_filter()
102 }; 101 };
103 folder_roots 102 let mut roots = Vec::new();
104 .iter() 103 for root in workspaces.iter().flat_map(ProjectWorkspace::to_roots) {
105 .map(|path| RootEntry::new(path.clone(), create_filter(true))) 104 let path = root.path().to_owned();
106 .chain(workspaces.iter().flat_map(ProjectWorkspace::to_roots).map(|pkg_root| { 105 if root.is_member() {
107 RootEntry::new(pkg_root.path().to_owned(), create_filter(pkg_root.is_member())) 106 local_roots.push(path.clone());
108 })) 107 }
109 .chain( 108 roots.push(RootEntry::new(path, create_filter(root.is_member())));
110 extern_dirs 109 if let Some(out_dir) = root.out_dir() {
111 .iter() 110 extern_dirs.insert(out_dir.to_path_buf());
112 .map(|path| RootEntry::new(path.to_owned(), create_filter(false))), 111 roots.push(RootEntry::new(
113 ) 112 out_dir.to_path_buf(),
114 .collect() 113 create_filter(root.is_member()),
114 ))
115 }
116 }
117 roots
115 }; 118 };
116 119
117 let (task_sender, task_receiver) = unbounded(); 120 let (task_sender, task_receiver) = unbounded();
@@ -121,9 +124,8 @@ impl WorldState {
121 let mut extern_source_roots = FxHashMap::default(); 124 let mut extern_source_roots = FxHashMap::default();
122 for r in vfs_roots { 125 for r in vfs_roots {
123 let vfs_root_path = vfs.root2path(r); 126 let vfs_root_path = vfs.root2path(r);
124 let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it)); 127 let is_local = local_roots.iter().any(|it| vfs_root_path.starts_with(it));
125 change.add_root(SourceRootId(r.0), is_local); 128 change.add_root(SourceRootId(r.0), is_local);
126 change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string());
127 129
128 // FIXME: add path2root in vfs to simpily this logic 130 // FIXME: add path2root in vfs to simpily this logic
129 if extern_dirs.contains(&vfs_root_path) { 131 if extern_dirs.contains(&vfs_root_path) {
@@ -131,14 +133,6 @@ impl WorldState {
131 } 133 }
132 } 134 }
133 135
134 // FIXME: Read default cfgs from config
135 let default_cfg_options = {
136 let mut opts = get_rustc_cfg_options(config.cargo.target.as_ref());
137 opts.insert_atom("test".into());
138 opts.insert_atom("debug_assertion".into());
139 opts
140 };
141
142 let proc_macro_client = match &config.proc_macro_srv { 136 let proc_macro_client = match &config.proc_macro_srv {
143 None => ProcMacroClient::dummy(), 137 None => ProcMacroClient::dummy(),
144 Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) { 138 Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
@@ -164,7 +158,7 @@ impl WorldState {
164 }; 158 };
165 for ws in workspaces.iter() { 159 for ws in workspaces.iter() {
166 crate_graph.extend(ws.to_crate_graph( 160 crate_graph.extend(ws.to_crate_graph(
167 &default_cfg_options, 161 config.cargo.target.as_deref(),
168 &extern_source_roots, 162 &extern_source_roots,
169 &proc_macro_client, 163 &proc_macro_client,
170 &mut load, 164 &mut load,
@@ -176,9 +170,9 @@ impl WorldState {
176 170
177 let mut analysis_host = AnalysisHost::new(lru_capacity); 171 let mut analysis_host = AnalysisHost::new(lru_capacity);
178 analysis_host.apply_change(change); 172 analysis_host.apply_change(change);
179 WorldState { 173 GlobalState {
180 config, 174 config,
181 roots: folder_roots, 175 local_roots,
182 workspaces: Arc::new(workspaces), 176 workspaces: Arc::new(workspaces),
183 analysis_host, 177 analysis_host,
184 vfs: Arc::new(RwLock::new(vfs)), 178 vfs: Arc::new(RwLock::new(vfs)),
@@ -216,7 +210,7 @@ impl WorldState {
216 match c { 210 match c {
217 VfsChange::AddRoot { root, files } => { 211 VfsChange::AddRoot { root, files } => {
218 let root_path = self.vfs.read().root2path(root); 212 let root_path = self.vfs.read().root2path(root);
219 let is_local = self.roots.iter().any(|r| root_path.starts_with(r)); 213 let is_local = self.local_roots.iter().any(|r| root_path.starts_with(r));
220 if is_local { 214 if is_local {
221 *roots_scanned += 1; 215 *roots_scanned += 1;
222 for (file, path, text) in files { 216 for (file, path, text) in files {
@@ -251,8 +245,8 @@ impl WorldState {
251 self.analysis_host.apply_change(change); 245 self.analysis_host.apply_change(change);
252 } 246 }
253 247
254 pub fn snapshot(&self) -> WorldSnapshot { 248 pub fn snapshot(&self) -> GlobalStateSnapshot {
255 WorldSnapshot { 249 GlobalStateSnapshot {
256 config: self.config.clone(), 250 config: self.config.clone(),
257 workspaces: Arc::clone(&self.workspaces), 251 workspaces: Arc::clone(&self.workspaces),
258 analysis: self.analysis_host.analysis(), 252 analysis: self.analysis_host.analysis(),
@@ -275,7 +269,7 @@ impl WorldState {
275 } 269 }
276} 270}
277 271
278impl WorldSnapshot { 272impl GlobalStateSnapshot {
279 pub fn analysis(&self) -> &Analysis { 273 pub fn analysis(&self) -> &Analysis {
280 &self.analysis 274 &self.analysis
281 } 275 }
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index 57d0e9218..609cb69d3 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -26,7 +26,7 @@ mod main_loop;
26mod markdown; 26mod markdown;
27pub mod lsp_ext; 27pub mod lsp_ext;
28pub mod config; 28pub mod config;
29mod world; 29mod global_state;
30mod diagnostics; 30mod diagnostics;
31mod semantic_tokens; 31mod semantic_tokens;
32 32
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index acb1dacb6..1371f6cb4 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -4,7 +4,6 @@ use std::{collections::HashMap, path::PathBuf};
4 4
5use lsp_types::request::Request; 5use lsp_types::request::Request;
6use lsp_types::{Position, Range, TextDocumentIdentifier}; 6use lsp_types::{Position, Range, TextDocumentIdentifier};
7use rustc_hash::FxHashMap;
8use serde::{Deserialize, Serialize}; 7use serde::{Deserialize, Serialize};
9 8
10pub enum AnalyzerStatus {} 9pub enum AnalyzerStatus {}
@@ -98,6 +97,22 @@ pub struct JoinLinesParams {
98 pub ranges: Vec<Range>, 97 pub ranges: Vec<Range>,
99} 98}
100 99
100pub enum ResolveCodeActionRequest {}
101
102impl Request for ResolveCodeActionRequest {
103 type Params = ResolveCodeActionParams;
104 type Result = Option<SnippetWorkspaceEdit>;
105 const METHOD: &'static str = "experimental/resolveCodeAction";
106}
107
108/// Params for the ResolveCodeActionRequest
109#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
110#[serde(rename_all = "camelCase")]
111pub struct ResolveCodeActionParams {
112 pub code_action_params: lsp_types::CodeActionParams,
113 pub id: String,
114}
115
101pub enum OnEnter {} 116pub enum OnEnter {}
102 117
103impl Request for OnEnter { 118impl Request for OnEnter {
@@ -111,7 +126,7 @@ pub enum Runnables {}
111impl Request for Runnables { 126impl Request for Runnables {
112 type Params = RunnablesParams; 127 type Params = RunnablesParams;
113 type Result = Vec<Runnable>; 128 type Result = Vec<Runnable>;
114 const METHOD: &'static str = "rust-analyzer/runnables"; 129 const METHOD: &'static str = "experimental/runnables";
115} 130}
116 131
117#[derive(Serialize, Deserialize, Debug)] 132#[derive(Serialize, Deserialize, Debug)]
@@ -124,13 +139,28 @@ pub struct RunnablesParams {
124#[derive(Deserialize, Serialize, Debug)] 139#[derive(Deserialize, Serialize, Debug)]
125#[serde(rename_all = "camelCase")] 140#[serde(rename_all = "camelCase")]
126pub struct Runnable { 141pub struct Runnable {
127 pub range: Range,
128 pub label: String, 142 pub label: String,
129 pub bin: String, 143 #[serde(skip_serializing_if = "Option::is_none")]
130 pub args: Vec<String>, 144 pub location: Option<lsp_types::LocationLink>,
131 pub extra_args: Vec<String>, 145 pub kind: RunnableKind,
132 pub env: FxHashMap<String, String>, 146 pub args: CargoRunnable,
133 pub cwd: Option<PathBuf>, 147}
148
149#[derive(Serialize, Deserialize, Debug)]
150#[serde(rename_all = "lowercase")]
151pub enum RunnableKind {
152 Cargo,
153}
154
155#[derive(Deserialize, Serialize, Debug)]
156#[serde(rename_all = "camelCase")]
157pub struct CargoRunnable {
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub workspace_root: Option<PathBuf>,
160 // command, --package and --lib stuff
161 pub cargo_args: Vec<String>,
162 // stuff after --
163 pub executable_args: Vec<String>,
134} 164}
135 165
136pub enum InlayHints {} 166pub enum InlayHints {}
@@ -188,6 +218,8 @@ impl Request for CodeActionRequest {
188pub struct CodeAction { 218pub struct CodeAction {
189 pub title: String, 219 pub title: String,
190 #[serde(skip_serializing_if = "Option::is_none")] 220 #[serde(skip_serializing_if = "Option::is_none")]
221 pub id: Option<String>,
222 #[serde(skip_serializing_if = "Option::is_none")]
191 pub group: Option<String>, 223 pub group: Option<String>,
192 #[serde(skip_serializing_if = "Option::is_none")] 224 #[serde(skip_serializing_if = "Option::is_none")]
193 pub kind: Option<String>, 225 pub kind: Option<String>,
@@ -228,3 +260,35 @@ pub struct SnippetTextEdit {
228 #[serde(skip_serializing_if = "Option::is_none")] 260 #[serde(skip_serializing_if = "Option::is_none")]
229 pub insert_text_format: Option<lsp_types::InsertTextFormat>, 261 pub insert_text_format: Option<lsp_types::InsertTextFormat>,
230} 262}
263
264pub enum HoverRequest {}
265
266impl Request for HoverRequest {
267 type Params = lsp_types::HoverParams;
268 type Result = Option<Hover>;
269 const METHOD: &'static str = "textDocument/hover";
270}
271
272#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
273pub struct Hover {
274 #[serde(flatten)]
275 pub hover: lsp_types::Hover,
276 #[serde(skip_serializing_if = "Vec::is_empty")]
277 pub actions: Vec<CommandLinkGroup>,
278}
279
280#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
281pub struct CommandLinkGroup {
282 #[serde(skip_serializing_if = "Option::is_none")]
283 pub title: Option<String>,
284 pub commands: Vec<CommandLink>,
285}
286
287// LSP v3.15 Command does not have a `tooltip` field, vscode supports one.
288#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
289pub struct CommandLink {
290 #[serde(flatten)]
291 pub command: lsp_types::Command,
292 #[serde(skip_serializing_if = "Option::is_none")]
293 pub tooltip: Option<String>,
294}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index f1287d52c..752dbf145 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -12,13 +12,11 @@ use std::{
12 fmt, 12 fmt,
13 ops::Range, 13 ops::Range,
14 panic, 14 panic,
15 path::PathBuf,
16 sync::Arc, 15 sync::Arc,
17 time::{Duration, Instant}, 16 time::{Duration, Instant},
18}; 17};
19 18
20use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; 19use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
21use itertools::Itertools;
22use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 20use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
23use lsp_types::{ 21use lsp_types::{
24 DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, 22 DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress,
@@ -36,14 +34,15 @@ use serde::{de::DeserializeOwned, Serialize};
36use threadpool::ThreadPool; 34use threadpool::ThreadPool;
37 35
38use crate::{ 36use crate::{
39 config::{Config, FilesWatcher}, 37 config::{Config, FilesWatcher, LinkedProject},
40 diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask}, 38 diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask},
41 from_proto, lsp_ext, 39 from_proto,
40 global_state::{GlobalState, GlobalStateSnapshot},
41 lsp_ext,
42 main_loop::{ 42 main_loop::{
43 pending_requests::{PendingRequest, PendingRequests}, 43 pending_requests::{PendingRequest, PendingRequests},
44 subscriptions::Subscriptions, 44 subscriptions::Subscriptions,
45 }, 45 },
46 world::{WorldSnapshot, WorldState},
47 Result, 46 Result,
48}; 47};
49 48
@@ -69,7 +68,7 @@ impl fmt::Display for LspError {
69 68
70impl Error for LspError {} 69impl Error for LspError {}
71 70
72pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> { 71pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
73 log::info!("initial config: {:#?}", config); 72 log::info!("initial config: {:#?}", config);
74 73
75 // Windows scheduler implements priority boosts: if thread waits for an 74 // Windows scheduler implements priority boosts: if thread waits for an
@@ -92,43 +91,37 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
92 } 91 }
93 92
94 let mut loop_state = LoopState::default(); 93 let mut loop_state = LoopState::default();
95 let mut world_state = { 94 let mut global_state = {
96 let workspaces = { 95 let workspaces = {
97 // FIXME: support dynamic workspace loading. 96 if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found {
98 let project_roots: FxHashSet<_> = ws_roots
99 .iter()
100 .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok())
101 .flatten()
102 .collect();
103
104 if project_roots.is_empty() && config.notifications.cargo_toml_not_found {
105 show_message( 97 show_message(
106 lsp_types::MessageType::Error, 98 lsp_types::MessageType::Error,
107 format!( 99 "rust-analyzer failed to discover workspace".to_string(),
108 "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}",
109 ws_roots.iter().format_with(", ", |it, f| f(&it.display()))
110 ),
111 &connection.sender, 100 &connection.sender,
112 ); 101 );
113 }; 102 };
114 103
115 project_roots 104 config
116 .into_iter() 105 .linked_projects
117 .filter_map(|root| { 106 .iter()
118 ra_project_model::ProjectWorkspace::load( 107 .filter_map(|project| match project {
119 root, 108 LinkedProject::ProjectManifest(manifest) => {
120 &config.cargo, 109 ra_project_model::ProjectWorkspace::load(
121 config.with_sysroot, 110 manifest.clone(),
122 ) 111 &config.cargo,
123 .map_err(|err| { 112 config.with_sysroot,
124 log::error!("failed to load workspace: {:#}", err); 113 )
125 show_message( 114 .map_err(|err| {
126 lsp_types::MessageType::Error, 115 log::error!("failed to load workspace: {:#}", err);
127 format!("rust-analyzer failed to load workspace: {:#}", err), 116 show_message(
128 &connection.sender, 117 lsp_types::MessageType::Error,
129 ); 118 format!("rust-analyzer failed to load workspace: {:#}", err),
130 }) 119 &connection.sender,
131 .ok() 120 );
121 })
122 .ok()
123 }
124 LinkedProject::JsonProject(it) => Some(it.clone().into()),
132 }) 125 })
133 .collect::<Vec<_>>() 126 .collect::<Vec<_>>()
134 }; 127 };
@@ -163,8 +156,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
163 connection.sender.send(request.into()).unwrap(); 156 connection.sender.send(request.into()).unwrap();
164 } 157 }
165 158
166 WorldState::new( 159 GlobalState::new(
167 ws_roots,
168 workspaces, 160 workspaces,
169 config.lru_capacity, 161 config.lru_capacity,
170 &globs, 162 &globs,
@@ -173,7 +165,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
173 ) 165 )
174 }; 166 };
175 167
176 loop_state.roots_total = world_state.vfs.read().n_roots(); 168 loop_state.roots_total = global_state.vfs.read().n_roots();
177 169
178 let pool = ThreadPool::default(); 170 let pool = ThreadPool::default();
179 let (task_sender, task_receiver) = unbounded::<Task>(); 171 let (task_sender, task_receiver) = unbounded::<Task>();
@@ -191,12 +183,12 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
191 Err(RecvError) => return Err("client exited without shutdown".into()), 183 Err(RecvError) => return Err("client exited without shutdown".into()),
192 }, 184 },
193 recv(task_receiver) -> task => Event::Task(task.unwrap()), 185 recv(task_receiver) -> task => Event::Task(task.unwrap()),
194 recv(world_state.task_receiver) -> task => match task { 186 recv(global_state.task_receiver) -> task => match task {
195 Ok(task) => Event::Vfs(task), 187 Ok(task) => Event::Vfs(task),
196 Err(RecvError) => return Err("vfs died".into()), 188 Err(RecvError) => return Err("vfs died".into()),
197 }, 189 },
198 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()), 190 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()),
199 recv(world_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { 191 recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task {
200 Ok(task) => Event::CheckWatcher(task), 192 Ok(task) => Event::CheckWatcher(task),
201 Err(RecvError) => return Err("check watcher died".into()), 193 Err(RecvError) => return Err("check watcher died".into()),
202 } 194 }
@@ -211,16 +203,16 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
211 &task_sender, 203 &task_sender,
212 &libdata_sender, 204 &libdata_sender,
213 &connection, 205 &connection,
214 &mut world_state, 206 &mut global_state,
215 &mut loop_state, 207 &mut loop_state,
216 event, 208 event,
217 )?; 209 )?;
218 } 210 }
219 } 211 }
220 world_state.analysis_host.request_cancellation(); 212 global_state.analysis_host.request_cancellation();
221 log::info!("waiting for tasks to finish..."); 213 log::info!("waiting for tasks to finish...");
222 task_receiver.into_iter().for_each(|task| { 214 task_receiver.into_iter().for_each(|task| {
223 on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut world_state) 215 on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut global_state)
224 }); 216 });
225 libdata_receiver.into_iter().for_each(drop); 217 libdata_receiver.into_iter().for_each(drop);
226 log::info!("...tasks have finished"); 218 log::info!("...tasks have finished");
@@ -229,7 +221,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
229 drop(pool); 221 drop(pool);
230 log::info!("...threadpool has finished"); 222 log::info!("...threadpool has finished");
231 223
232 let vfs = Arc::try_unwrap(world_state.vfs).expect("all snapshots should be dead"); 224 let vfs = Arc::try_unwrap(global_state.vfs).expect("all snapshots should be dead");
233 drop(vfs); 225 drop(vfs);
234 226
235 Ok(()) 227 Ok(())
@@ -320,7 +312,7 @@ fn loop_turn(
320 task_sender: &Sender<Task>, 312 task_sender: &Sender<Task>,
321 libdata_sender: &Sender<LibraryData>, 313 libdata_sender: &Sender<LibraryData>,
322 connection: &Connection, 314 connection: &Connection,
323 world_state: &mut WorldState, 315 global_state: &mut GlobalState,
324 loop_state: &mut LoopState, 316 loop_state: &mut LoopState,
325 event: Event, 317 event: Event,
326) -> Result<()> { 318) -> Result<()> {
@@ -336,22 +328,22 @@ fn loop_turn(
336 328
337 match event { 329 match event {
338 Event::Task(task) => { 330 Event::Task(task) => {
339 on_task(task, &connection.sender, &mut loop_state.pending_requests, world_state); 331 on_task(task, &connection.sender, &mut loop_state.pending_requests, global_state);
340 world_state.maybe_collect_garbage(); 332 global_state.maybe_collect_garbage();
341 } 333 }
342 Event::Vfs(task) => { 334 Event::Vfs(task) => {
343 world_state.vfs.write().handle_task(task); 335 global_state.vfs.write().handle_task(task);
344 } 336 }
345 Event::Lib(lib) => { 337 Event::Lib(lib) => {
346 world_state.add_lib(lib); 338 global_state.add_lib(lib);
347 world_state.maybe_collect_garbage(); 339 global_state.maybe_collect_garbage();
348 loop_state.in_flight_libraries -= 1; 340 loop_state.in_flight_libraries -= 1;
349 loop_state.roots_scanned += 1; 341 loop_state.roots_scanned += 1;
350 } 342 }
351 Event::CheckWatcher(task) => on_check_task(task, world_state, task_sender)?, 343 Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?,
352 Event::Msg(msg) => match msg { 344 Event::Msg(msg) => match msg {
353 Message::Request(req) => on_request( 345 Message::Request(req) => on_request(
354 world_state, 346 global_state,
355 &mut loop_state.pending_requests, 347 &mut loop_state.pending_requests,
356 pool, 348 pool,
357 task_sender, 349 task_sender,
@@ -360,7 +352,7 @@ fn loop_turn(
360 req, 352 req,
361 )?, 353 )?,
362 Message::Notification(not) => { 354 Message::Notification(not) => {
363 on_notification(&connection.sender, world_state, loop_state, not)?; 355 on_notification(&connection.sender, global_state, loop_state, not)?;
364 } 356 }
365 Message::Response(resp) => { 357 Message::Response(resp) => {
366 let removed = loop_state.pending_responses.remove(&resp.id); 358 let removed = loop_state.pending_responses.remove(&resp.id);
@@ -379,9 +371,9 @@ fn loop_turn(
379 } 371 }
380 (None, Some(configs)) => { 372 (None, Some(configs)) => {
381 if let Some(new_config) = configs.get(0) { 373 if let Some(new_config) = configs.get(0) {
382 let mut config = world_state.config.clone(); 374 let mut config = global_state.config.clone();
383 config.update(&new_config); 375 config.update(&new_config);
384 world_state.update_configuration(config); 376 global_state.update_configuration(config);
385 } 377 }
386 } 378 }
387 (None, None) => { 379 (None, None) => {
@@ -394,7 +386,7 @@ fn loop_turn(
394 }; 386 };
395 387
396 let mut state_changed = false; 388 let mut state_changed = false;
397 if let Some(changes) = world_state.process_changes(&mut loop_state.roots_scanned) { 389 if let Some(changes) = global_state.process_changes(&mut loop_state.roots_scanned) {
398 state_changed = true; 390 state_changed = true;
399 loop_state.pending_libraries.extend(changes); 391 loop_state.pending_libraries.extend(changes);
400 } 392 }
@@ -416,7 +408,7 @@ fn loop_turn(
416 } 408 }
417 409
418 let show_progress = 410 let show_progress =
419 !loop_state.workspace_loaded && world_state.config.client_caps.work_done_progress; 411 !loop_state.workspace_loaded && global_state.config.client_caps.work_done_progress;
420 412
421 if !loop_state.workspace_loaded 413 if !loop_state.workspace_loaded
422 && loop_state.roots_scanned == loop_state.roots_total 414 && loop_state.roots_scanned == loop_state.roots_total
@@ -425,7 +417,7 @@ fn loop_turn(
425 { 417 {
426 state_changed = true; 418 state_changed = true;
427 loop_state.workspace_loaded = true; 419 loop_state.workspace_loaded = true;
428 if let Some(flycheck) = &world_state.flycheck { 420 if let Some(flycheck) = &global_state.flycheck {
429 flycheck.update(); 421 flycheck.update();
430 } 422 }
431 } 423 }
@@ -437,13 +429,13 @@ fn loop_turn(
437 if state_changed && loop_state.workspace_loaded { 429 if state_changed && loop_state.workspace_loaded {
438 update_file_notifications_on_threadpool( 430 update_file_notifications_on_threadpool(
439 pool, 431 pool,
440 world_state.snapshot(), 432 global_state.snapshot(),
441 task_sender.clone(), 433 task_sender.clone(),
442 loop_state.subscriptions.subscriptions(), 434 loop_state.subscriptions.subscriptions(),
443 ); 435 );
444 pool.execute({ 436 pool.execute({
445 let subs = loop_state.subscriptions.subscriptions(); 437 let subs = loop_state.subscriptions.subscriptions();
446 let snap = world_state.snapshot(); 438 let snap = global_state.snapshot();
447 move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) 439 move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ())
448 }); 440 });
449 } 441 }
@@ -467,7 +459,7 @@ fn on_task(
467 task: Task, 459 task: Task,
468 msg_sender: &Sender<Message>, 460 msg_sender: &Sender<Message>,
469 pending_requests: &mut PendingRequests, 461 pending_requests: &mut PendingRequests,
470 state: &mut WorldState, 462 state: &mut GlobalState,
471) { 463) {
472 match task { 464 match task {
473 Task::Respond(response) => { 465 Task::Respond(response) => {
@@ -485,7 +477,7 @@ fn on_task(
485} 477}
486 478
487fn on_request( 479fn on_request(
488 world: &mut WorldState, 480 global_state: &mut GlobalState,
489 pending_requests: &mut PendingRequests, 481 pending_requests: &mut PendingRequests,
490 pool: &ThreadPool, 482 pool: &ThreadPool,
491 task_sender: &Sender<Task>, 483 task_sender: &Sender<Task>,
@@ -496,7 +488,7 @@ fn on_request(
496 let mut pool_dispatcher = PoolDispatcher { 488 let mut pool_dispatcher = PoolDispatcher {
497 req: Some(req), 489 req: Some(req),
498 pool, 490 pool,
499 world, 491 global_state,
500 task_sender, 492 task_sender,
501 msg_sender, 493 msg_sender,
502 pending_requests, 494 pending_requests,
@@ -517,6 +509,8 @@ fn on_request(
517 .on::<lsp_ext::Runnables>(handlers::handle_runnables)? 509 .on::<lsp_ext::Runnables>(handlers::handle_runnables)?
518 .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)? 510 .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)?
519 .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)? 511 .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)?
512 .on::<lsp_ext::ResolveCodeActionRequest>(handlers::handle_resolve_code_action)?
513 .on::<lsp_ext::HoverRequest>(handlers::handle_hover)?
520 .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)? 514 .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)?
521 .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)? 515 .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)?
522 .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)? 516 .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
@@ -528,7 +522,6 @@ fn on_request(
528 .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)? 522 .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)?
529 .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)? 523 .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)?
530 .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)? 524 .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)?
531 .on::<lsp_types::request::HoverRequest>(handlers::handle_hover)?
532 .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)? 525 .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)?
533 .on::<lsp_types::request::Rename>(handlers::handle_rename)? 526 .on::<lsp_types::request::Rename>(handlers::handle_rename)?
534 .on::<lsp_types::request::References>(handlers::handle_references)? 527 .on::<lsp_types::request::References>(handlers::handle_references)?
@@ -552,7 +545,7 @@ fn on_request(
552 545
553fn on_notification( 546fn on_notification(
554 msg_sender: &Sender<Message>, 547 msg_sender: &Sender<Message>,
555 state: &mut WorldState, 548 state: &mut GlobalState,
556 loop_state: &mut LoopState, 549 loop_state: &mut LoopState,
557 not: Notification, 550 not: Notification,
558) -> Result<()> { 551) -> Result<()> {
@@ -726,7 +719,7 @@ fn apply_document_changes(
726 719
727fn on_check_task( 720fn on_check_task(
728 task: CheckTask, 721 task: CheckTask,
729 world_state: &mut WorldState, 722 global_state: &mut GlobalState,
730 task_sender: &Sender<Task>, 723 task_sender: &Sender<Task>,
731) -> Result<()> { 724) -> Result<()> {
732 match task { 725 match task {
@@ -745,7 +738,7 @@ fn on_check_task(
745 .uri 738 .uri
746 .to_file_path() 739 .to_file_path()
747 .map_err(|()| format!("invalid uri: {}", diag.location.uri))?; 740 .map_err(|()| format!("invalid uri: {}", diag.location.uri))?;
748 let file_id = match world_state.vfs.read().path2file(&path) { 741 let file_id = match global_state.vfs.read().path2file(&path) {
749 Some(file) => FileId(file.0), 742 Some(file) => FileId(file.0),
750 None => { 743 None => {
751 log::error!( 744 log::error!(
@@ -765,7 +758,7 @@ fn on_check_task(
765 } 758 }
766 759
767 CheckTask::Status(status) => { 760 CheckTask::Status(status) => {
768 if world_state.config.client_caps.work_done_progress { 761 if global_state.config.client_caps.work_done_progress {
769 let progress = match status { 762 let progress = match status {
770 Status::Being => { 763 Status::Being => {
771 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { 764 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
@@ -804,7 +797,7 @@ fn on_check_task(
804 Ok(()) 797 Ok(())
805} 798}
806 799
807fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: &mut WorldState) { 800fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: &mut GlobalState) {
808 let subscriptions = state.diagnostics.handle_task(task); 801 let subscriptions = state.diagnostics.handle_task(task);
809 802
810 for file_id in subscriptions { 803 for file_id in subscriptions {
@@ -879,7 +872,7 @@ fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) {
879struct PoolDispatcher<'a> { 872struct PoolDispatcher<'a> {
880 req: Option<Request>, 873 req: Option<Request>,
881 pool: &'a ThreadPool, 874 pool: &'a ThreadPool,
882 world: &'a mut WorldState, 875 global_state: &'a mut GlobalState,
883 pending_requests: &'a mut PendingRequests, 876 pending_requests: &'a mut PendingRequests,
884 msg_sender: &'a Sender<Message>, 877 msg_sender: &'a Sender<Message>,
885 task_sender: &'a Sender<Task>, 878 task_sender: &'a Sender<Task>,
@@ -890,7 +883,7 @@ impl<'a> PoolDispatcher<'a> {
890 /// Dispatches the request onto the current thread 883 /// Dispatches the request onto the current thread
891 fn on_sync<R>( 884 fn on_sync<R>(
892 &mut self, 885 &mut self,
893 f: fn(&mut WorldState, R::Params) -> Result<R::Result>, 886 f: fn(&mut GlobalState, R::Params) -> Result<R::Result>,
894 ) -> Result<&mut Self> 887 ) -> Result<&mut Self>
895 where 888 where
896 R: lsp_types::request::Request + 'static, 889 R: lsp_types::request::Request + 'static,
@@ -903,18 +896,21 @@ impl<'a> PoolDispatcher<'a> {
903 return Ok(self); 896 return Ok(self);
904 } 897 }
905 }; 898 };
906 let world = panic::AssertUnwindSafe(&mut *self.world); 899 let world = panic::AssertUnwindSafe(&mut *self.global_state);
907 let task = panic::catch_unwind(move || { 900 let task = panic::catch_unwind(move || {
908 let result = f(world.0, params); 901 let result = f(world.0, params);
909 result_to_task::<R>(id, result) 902 result_to_task::<R>(id, result)
910 }) 903 })
911 .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?; 904 .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?;
912 on_task(task, self.msg_sender, self.pending_requests, self.world); 905 on_task(task, self.msg_sender, self.pending_requests, self.global_state);
913 Ok(self) 906 Ok(self)
914 } 907 }
915 908
916 /// Dispatches the request onto thread pool 909 /// Dispatches the request onto thread pool
917 fn on<R>(&mut self, f: fn(WorldSnapshot, R::Params) -> Result<R::Result>) -> Result<&mut Self> 910 fn on<R>(
911 &mut self,
912 f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
913 ) -> Result<&mut Self>
918 where 914 where
919 R: lsp_types::request::Request + 'static, 915 R: lsp_types::request::Request + 'static,
920 R::Params: DeserializeOwned + Send + 'static, 916 R::Params: DeserializeOwned + Send + 'static,
@@ -928,7 +924,7 @@ impl<'a> PoolDispatcher<'a> {
928 }; 924 };
929 925
930 self.pool.execute({ 926 self.pool.execute({
931 let world = self.world.snapshot(); 927 let world = self.global_state.snapshot();
932 let sender = self.task_sender.clone(); 928 let sender = self.task_sender.clone();
933 move || { 929 move || {
934 let result = f(world, params); 930 let result = f(world, params);
@@ -1012,7 +1008,7 @@ where
1012 1008
1013fn update_file_notifications_on_threadpool( 1009fn update_file_notifications_on_threadpool(
1014 pool: &ThreadPool, 1010 pool: &ThreadPool,
1015 world: WorldSnapshot, 1011 world: GlobalStateSnapshot,
1016 task_sender: Sender<Task>, 1012 task_sender: Sender<Task>,
1017 subscriptions: Vec<FileId>, 1013 subscriptions: Vec<FileId>,
1018) { 1014) {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 1f910ff82..a41adf8b0 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -12,40 +12,37 @@ use lsp_types::{
12 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, 12 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
13 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, 13 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
14 CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, 14 CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight,
15 DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, 15 DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location, MarkupContent,
16 MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, 16 MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams,
17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, 17 SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, 18 TextDocumentIdentifier, Url, WorkspaceEdit,
19}; 19};
20use ra_cfg::CfgExpr;
21use ra_ide::{ 20use ra_ide::{
22 FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, 21 FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, Runnable, RunnableKind,
23 TextEdit, 22 SearchScope, TextEdit,
24}; 23};
25use ra_prof::profile; 24use ra_prof::profile;
26use ra_project_model::TargetKind; 25use ra_project_model::TargetKind;
27use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize}; 26use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize};
28use rustc_hash::FxHashMap;
29use serde::{Deserialize, Serialize}; 27use serde::{Deserialize, Serialize};
30use serde_json::to_value; 28use serde_json::to_value;
31use stdx::format_to; 29use stdx::{format_to, split1};
32 30
33use crate::{ 31use crate::{
34 cargo_target_spec::CargoTargetSpec, 32 cargo_target_spec::CargoTargetSpec,
35 config::RustfmtConfig, 33 config::RustfmtConfig,
36 diagnostics::DiagnosticTask, 34 diagnostics::DiagnosticTask,
37 from_json, from_proto, 35 from_json, from_proto,
36 global_state::GlobalStateSnapshot,
38 lsp_ext::{self, InlayHint, InlayHintsParams}, 37 lsp_ext::{self, InlayHint, InlayHintsParams},
39 to_proto, 38 to_proto, LspError, Result,
40 world::WorldSnapshot,
41 LspError, Result,
42}; 39};
43 40
44pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { 41pub fn handle_analyzer_status(snap: GlobalStateSnapshot, _: ()) -> Result<String> {
45 let _p = profile("handle_analyzer_status"); 42 let _p = profile("handle_analyzer_status");
46 let mut buf = world.status(); 43 let mut buf = snap.status();
47 format_to!(buf, "\n\nrequests:\n"); 44 format_to!(buf, "\n\nrequests:\n");
48 let requests = world.latest_requests.read(); 45 let requests = snap.latest_requests.read();
49 for (is_last, r) in requests.iter() { 46 for (is_last, r) in requests.iter() {
50 let mark = if is_last { "*" } else { " " }; 47 let mark = if is_last { "*" } else { " " };
51 format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis()); 48 format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis());
@@ -54,37 +51,37 @@ pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> {
54} 51}
55 52
56pub fn handle_syntax_tree( 53pub fn handle_syntax_tree(
57 world: WorldSnapshot, 54 snap: GlobalStateSnapshot,
58 params: lsp_ext::SyntaxTreeParams, 55 params: lsp_ext::SyntaxTreeParams,
59) -> Result<String> { 56) -> Result<String> {
60 let _p = profile("handle_syntax_tree"); 57 let _p = profile("handle_syntax_tree");
61 let id = from_proto::file_id(&world, &params.text_document.uri)?; 58 let id = from_proto::file_id(&snap, &params.text_document.uri)?;
62 let line_index = world.analysis().file_line_index(id)?; 59 let line_index = snap.analysis().file_line_index(id)?;
63 let text_range = params.range.map(|r| from_proto::text_range(&line_index, r)); 60 let text_range = params.range.map(|r| from_proto::text_range(&line_index, r));
64 let res = world.analysis().syntax_tree(id, text_range)?; 61 let res = snap.analysis().syntax_tree(id, text_range)?;
65 Ok(res) 62 Ok(res)
66} 63}
67 64
68pub fn handle_expand_macro( 65pub fn handle_expand_macro(
69 world: WorldSnapshot, 66 snap: GlobalStateSnapshot,
70 params: lsp_ext::ExpandMacroParams, 67 params: lsp_ext::ExpandMacroParams,
71) -> Result<Option<lsp_ext::ExpandedMacro>> { 68) -> Result<Option<lsp_ext::ExpandedMacro>> {
72 let _p = profile("handle_expand_macro"); 69 let _p = profile("handle_expand_macro");
73 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 70 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
74 let line_index = world.analysis().file_line_index(file_id)?; 71 let line_index = snap.analysis().file_line_index(file_id)?;
75 let offset = from_proto::offset(&line_index, params.position); 72 let offset = from_proto::offset(&line_index, params.position);
76 73
77 let res = world.analysis().expand_macro(FilePosition { file_id, offset })?; 74 let res = snap.analysis().expand_macro(FilePosition { file_id, offset })?;
78 Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion })) 75 Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
79} 76}
80 77
81pub fn handle_selection_range( 78pub fn handle_selection_range(
82 world: WorldSnapshot, 79 snap: GlobalStateSnapshot,
83 params: lsp_types::SelectionRangeParams, 80 params: lsp_types::SelectionRangeParams,
84) -> Result<Option<Vec<lsp_types::SelectionRange>>> { 81) -> Result<Option<Vec<lsp_types::SelectionRange>>> {
85 let _p = profile("handle_selection_range"); 82 let _p = profile("handle_selection_range");
86 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 83 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
87 let line_index = world.analysis().file_line_index(file_id)?; 84 let line_index = snap.analysis().file_line_index(file_id)?;
88 let res: Result<Vec<lsp_types::SelectionRange>> = params 85 let res: Result<Vec<lsp_types::SelectionRange>> = params
89 .positions 86 .positions
90 .into_iter() 87 .into_iter()
@@ -96,7 +93,7 @@ pub fn handle_selection_range(
96 loop { 93 loop {
97 ranges.push(range); 94 ranges.push(range);
98 let frange = FileRange { file_id, range }; 95 let frange = FileRange { file_id, range };
99 let next = world.analysis().extend_selection(frange)?; 96 let next = snap.analysis().extend_selection(frange)?;
100 if next == range { 97 if next == range {
101 break; 98 break;
102 } else { 99 } else {
@@ -122,18 +119,18 @@ pub fn handle_selection_range(
122} 119}
123 120
124pub fn handle_matching_brace( 121pub fn handle_matching_brace(
125 world: WorldSnapshot, 122 snap: GlobalStateSnapshot,
126 params: lsp_ext::MatchingBraceParams, 123 params: lsp_ext::MatchingBraceParams,
127) -> Result<Vec<Position>> { 124) -> Result<Vec<Position>> {
128 let _p = profile("handle_matching_brace"); 125 let _p = profile("handle_matching_brace");
129 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 126 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
130 let line_index = world.analysis().file_line_index(file_id)?; 127 let line_index = snap.analysis().file_line_index(file_id)?;
131 let res = params 128 let res = params
132 .positions 129 .positions
133 .into_iter() 130 .into_iter()
134 .map(|position| { 131 .map(|position| {
135 let offset = from_proto::offset(&line_index, position); 132 let offset = from_proto::offset(&line_index, position);
136 let offset = match world.analysis().matching_brace(FilePosition { file_id, offset }) { 133 let offset = match snap.analysis().matching_brace(FilePosition { file_id, offset }) {
137 Ok(Some(matching_brace_offset)) => matching_brace_offset, 134 Ok(Some(matching_brace_offset)) => matching_brace_offset,
138 Err(_) | Ok(None) => offset, 135 Err(_) | Ok(None) => offset,
139 }; 136 };
@@ -144,17 +141,17 @@ pub fn handle_matching_brace(
144} 141}
145 142
146pub fn handle_join_lines( 143pub fn handle_join_lines(
147 world: WorldSnapshot, 144 snap: GlobalStateSnapshot,
148 params: lsp_ext::JoinLinesParams, 145 params: lsp_ext::JoinLinesParams,
149) -> Result<Vec<lsp_types::TextEdit>> { 146) -> Result<Vec<lsp_types::TextEdit>> {
150 let _p = profile("handle_join_lines"); 147 let _p = profile("handle_join_lines");
151 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 148 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
152 let line_index = world.analysis().file_line_index(file_id)?; 149 let line_index = snap.analysis().file_line_index(file_id)?;
153 let line_endings = world.file_line_endings(file_id); 150 let line_endings = snap.file_line_endings(file_id);
154 let mut res = TextEdit::default(); 151 let mut res = TextEdit::default();
155 for range in params.ranges { 152 for range in params.ranges {
156 let range = from_proto::text_range(&line_index, range); 153 let range = from_proto::text_range(&line_index, range);
157 let edit = world.analysis().join_lines(FileRange { file_id, range })?; 154 let edit = snap.analysis().join_lines(FileRange { file_id, range })?;
158 match res.union(edit) { 155 match res.union(edit) {
159 Ok(()) => (), 156 Ok(()) => (),
160 Err(_edit) => { 157 Err(_edit) => {
@@ -167,37 +164,37 @@ pub fn handle_join_lines(
167} 164}
168 165
169pub fn handle_on_enter( 166pub fn handle_on_enter(
170 world: WorldSnapshot, 167 snap: GlobalStateSnapshot,
171 params: lsp_types::TextDocumentPositionParams, 168 params: lsp_types::TextDocumentPositionParams,
172) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> { 169) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
173 let _p = profile("handle_on_enter"); 170 let _p = profile("handle_on_enter");
174 let position = from_proto::file_position(&world, params)?; 171 let position = from_proto::file_position(&snap, params)?;
175 let edit = match world.analysis().on_enter(position)? { 172 let edit = match snap.analysis().on_enter(position)? {
176 None => return Ok(None), 173 None => return Ok(None),
177 Some(it) => it, 174 Some(it) => it,
178 }; 175 };
179 let line_index = world.analysis().file_line_index(position.file_id)?; 176 let line_index = snap.analysis().file_line_index(position.file_id)?;
180 let line_endings = world.file_line_endings(position.file_id); 177 let line_endings = snap.file_line_endings(position.file_id);
181 let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit); 178 let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit);
182 Ok(Some(edit)) 179 Ok(Some(edit))
183} 180}
184 181
185// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. 182// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
186pub fn handle_on_type_formatting( 183pub fn handle_on_type_formatting(
187 world: WorldSnapshot, 184 snap: GlobalStateSnapshot,
188 params: lsp_types::DocumentOnTypeFormattingParams, 185 params: lsp_types::DocumentOnTypeFormattingParams,
189) -> Result<Option<Vec<lsp_types::TextEdit>>> { 186) -> Result<Option<Vec<lsp_types::TextEdit>>> {
190 let _p = profile("handle_on_type_formatting"); 187 let _p = profile("handle_on_type_formatting");
191 let mut position = from_proto::file_position(&world, params.text_document_position)?; 188 let mut position = from_proto::file_position(&snap, params.text_document_position)?;
192 let line_index = world.analysis().file_line_index(position.file_id)?; 189 let line_index = snap.analysis().file_line_index(position.file_id)?;
193 let line_endings = world.file_line_endings(position.file_id); 190 let line_endings = snap.file_line_endings(position.file_id);
194 191
195 // in `ra_ide`, the `on_type` invariant is that 192 // in `ra_ide`, the `on_type` invariant is that
196 // `text.char_at(position) == typed_char`. 193 // `text.char_at(position) == typed_char`.
197 position.offset -= TextSize::of('.'); 194 position.offset -= TextSize::of('.');
198 let char_typed = params.ch.chars().next().unwrap_or('\0'); 195 let char_typed = params.ch.chars().next().unwrap_or('\0');
199 assert!({ 196 assert!({
200 let text = world.analysis().file_text(position.file_id)?; 197 let text = snap.analysis().file_text(position.file_id)?;
201 text[usize::from(position.offset)..].starts_with(char_typed) 198 text[usize::from(position.offset)..].starts_with(char_typed)
202 }); 199 });
203 200
@@ -209,7 +206,7 @@ pub fn handle_on_type_formatting(
209 return Ok(None); 206 return Ok(None);
210 } 207 }
211 208
212 let edit = world.analysis().on_char_typed(position, char_typed)?; 209 let edit = snap.analysis().on_char_typed(position, char_typed)?;
213 let mut edit = match edit { 210 let mut edit = match edit {
214 Some(it) => it, 211 Some(it) => it,
215 None => return Ok(None), 212 None => return Ok(None),
@@ -223,16 +220,16 @@ pub fn handle_on_type_formatting(
223} 220}
224 221
225pub fn handle_document_symbol( 222pub fn handle_document_symbol(
226 world: WorldSnapshot, 223 snap: GlobalStateSnapshot,
227 params: lsp_types::DocumentSymbolParams, 224 params: lsp_types::DocumentSymbolParams,
228) -> Result<Option<lsp_types::DocumentSymbolResponse>> { 225) -> Result<Option<lsp_types::DocumentSymbolResponse>> {
229 let _p = profile("handle_document_symbol"); 226 let _p = profile("handle_document_symbol");
230 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 227 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
231 let line_index = world.analysis().file_line_index(file_id)?; 228 let line_index = snap.analysis().file_line_index(file_id)?;
232 229
233 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); 230 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new();
234 231
235 for symbol in world.analysis().file_structure(file_id)? { 232 for symbol in snap.analysis().file_structure(file_id)? {
236 let doc_symbol = DocumentSymbol { 233 let doc_symbol = DocumentSymbol {
237 name: symbol.label, 234 name: symbol.label,
238 detail: symbol.detail, 235 detail: symbol.detail,
@@ -258,10 +255,10 @@ pub fn handle_document_symbol(
258 } 255 }
259 } 256 }
260 257
261 let res = if world.config.client_caps.hierarchical_symbols { 258 let res = if snap.config.client_caps.hierarchical_symbols {
262 document_symbols.into() 259 document_symbols.into()
263 } else { 260 } else {
264 let url = to_proto::url(&world, file_id)?; 261 let url = to_proto::url(&snap, file_id)?;
265 let mut symbol_information = Vec::<SymbolInformation>::new(); 262 let mut symbol_information = Vec::<SymbolInformation>::new();
266 for symbol in document_symbols { 263 for symbol in document_symbols {
267 flatten_document_symbol(&symbol, None, &url, &mut symbol_information); 264 flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
@@ -291,7 +288,7 @@ pub fn handle_document_symbol(
291} 288}
292 289
293pub fn handle_workspace_symbol( 290pub fn handle_workspace_symbol(
294 world: WorldSnapshot, 291 snap: GlobalStateSnapshot,
295 params: lsp_types::WorkspaceSymbolParams, 292 params: lsp_types::WorkspaceSymbolParams,
296) -> Result<Option<Vec<SymbolInformation>>> { 293) -> Result<Option<Vec<SymbolInformation>>> {
297 let _p = profile("handle_workspace_symbol"); 294 let _p = profile("handle_workspace_symbol");
@@ -309,22 +306,22 @@ pub fn handle_workspace_symbol(
309 q.limit(128); 306 q.limit(128);
310 q 307 q
311 }; 308 };
312 let mut res = exec_query(&world, query)?; 309 let mut res = exec_query(&snap, query)?;
313 if res.is_empty() && !all_symbols { 310 if res.is_empty() && !all_symbols {
314 let mut query = Query::new(params.query); 311 let mut query = Query::new(params.query);
315 query.limit(128); 312 query.limit(128);
316 res = exec_query(&world, query)?; 313 res = exec_query(&snap, query)?;
317 } 314 }
318 315
319 return Ok(Some(res)); 316 return Ok(Some(res));
320 317
321 fn exec_query(world: &WorldSnapshot, query: Query) -> Result<Vec<SymbolInformation>> { 318 fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> {
322 let mut res = Vec::new(); 319 let mut res = Vec::new();
323 for nav in world.analysis().symbol_search(query)? { 320 for nav in snap.analysis().symbol_search(query)? {
324 let info = SymbolInformation { 321 let info = SymbolInformation {
325 name: nav.name().to_string(), 322 name: nav.name().to_string(),
326 kind: to_proto::symbol_kind(nav.kind()), 323 kind: to_proto::symbol_kind(nav.kind()),
327 location: to_proto::location(world, nav.file_range())?, 324 location: to_proto::location(snap, nav.file_range())?,
328 container_name: nav.container_name().map(|v| v.to_string()), 325 container_name: nav.container_name().map(|v| v.to_string()),
329 deprecated: None, 326 deprecated: None,
330 }; 327 };
@@ -335,88 +332,83 @@ pub fn handle_workspace_symbol(
335} 332}
336 333
337pub fn handle_goto_definition( 334pub fn handle_goto_definition(
338 world: WorldSnapshot, 335 snap: GlobalStateSnapshot,
339 params: lsp_types::GotoDefinitionParams, 336 params: lsp_types::GotoDefinitionParams,
340) -> Result<Option<lsp_types::GotoDefinitionResponse>> { 337) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
341 let _p = profile("handle_goto_definition"); 338 let _p = profile("handle_goto_definition");
342 let position = from_proto::file_position(&world, params.text_document_position_params)?; 339 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
343 let nav_info = match world.analysis().goto_definition(position)? { 340 let nav_info = match snap.analysis().goto_definition(position)? {
344 None => return Ok(None), 341 None => return Ok(None),
345 Some(it) => it, 342 Some(it) => it,
346 }; 343 };
347 let src = FileRange { file_id: position.file_id, range: nav_info.range }; 344 let src = FileRange { file_id: position.file_id, range: nav_info.range };
348 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; 345 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
349 Ok(Some(res)) 346 Ok(Some(res))
350} 347}
351 348
352pub fn handle_goto_implementation( 349pub fn handle_goto_implementation(
353 world: WorldSnapshot, 350 snap: GlobalStateSnapshot,
354 params: lsp_types::request::GotoImplementationParams, 351 params: lsp_types::request::GotoImplementationParams,
355) -> Result<Option<lsp_types::request::GotoImplementationResponse>> { 352) -> Result<Option<lsp_types::request::GotoImplementationResponse>> {
356 let _p = profile("handle_goto_implementation"); 353 let _p = profile("handle_goto_implementation");
357 let position = from_proto::file_position(&world, params.text_document_position_params)?; 354 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
358 let nav_info = match world.analysis().goto_implementation(position)? { 355 let nav_info = match snap.analysis().goto_implementation(position)? {
359 None => return Ok(None), 356 None => return Ok(None),
360 Some(it) => it, 357 Some(it) => it,
361 }; 358 };
362 let src = FileRange { file_id: position.file_id, range: nav_info.range }; 359 let src = FileRange { file_id: position.file_id, range: nav_info.range };
363 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; 360 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
364 Ok(Some(res)) 361 Ok(Some(res))
365} 362}
366 363
367pub fn handle_goto_type_definition( 364pub fn handle_goto_type_definition(
368 world: WorldSnapshot, 365 snap: GlobalStateSnapshot,
369 params: lsp_types::request::GotoTypeDefinitionParams, 366 params: lsp_types::request::GotoTypeDefinitionParams,
370) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> { 367) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> {
371 let _p = profile("handle_goto_type_definition"); 368 let _p = profile("handle_goto_type_definition");
372 let position = from_proto::file_position(&world, params.text_document_position_params)?; 369 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
373 let nav_info = match world.analysis().goto_type_definition(position)? { 370 let nav_info = match snap.analysis().goto_type_definition(position)? {
374 None => return Ok(None), 371 None => return Ok(None),
375 Some(it) => it, 372 Some(it) => it,
376 }; 373 };
377 let src = FileRange { file_id: position.file_id, range: nav_info.range }; 374 let src = FileRange { file_id: position.file_id, range: nav_info.range };
378 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; 375 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
379 Ok(Some(res)) 376 Ok(Some(res))
380} 377}
381 378
382pub fn handle_parent_module( 379pub fn handle_parent_module(
383 world: WorldSnapshot, 380 snap: GlobalStateSnapshot,
384 params: lsp_types::TextDocumentPositionParams, 381 params: lsp_types::TextDocumentPositionParams,
385) -> Result<Option<lsp_types::GotoDefinitionResponse>> { 382) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
386 let _p = profile("handle_parent_module"); 383 let _p = profile("handle_parent_module");
387 let position = from_proto::file_position(&world, params)?; 384 let position = from_proto::file_position(&snap, params)?;
388 let navs = world.analysis().parent_module(position)?; 385 let navs = snap.analysis().parent_module(position)?;
389 let res = to_proto::goto_definition_response(&world, None, navs)?; 386 let res = to_proto::goto_definition_response(&snap, None, navs)?;
390 Ok(Some(res)) 387 Ok(Some(res))
391} 388}
392 389
393pub fn handle_runnables( 390pub fn handle_runnables(
394 world: WorldSnapshot, 391 snap: GlobalStateSnapshot,
395 params: lsp_ext::RunnablesParams, 392 params: lsp_ext::RunnablesParams,
396) -> Result<Vec<lsp_ext::Runnable>> { 393) -> Result<Vec<lsp_ext::Runnable>> {
397 let _p = profile("handle_runnables"); 394 let _p = profile("handle_runnables");
398 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 395 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
399 let line_index = world.analysis().file_line_index(file_id)?; 396 let line_index = snap.analysis().file_line_index(file_id)?;
400 let offset = params.position.map(|it| from_proto::offset(&line_index, it)); 397 let offset = params.position.map(|it| from_proto::offset(&line_index, it));
401 let mut res = Vec::new(); 398 let mut res = Vec::new();
402 let workspace_root = world.workspace_root_for(file_id); 399 let workspace_root = snap.workspace_root_for(file_id);
403 let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; 400 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
404 for runnable in world.analysis().runnables(file_id)? { 401 for runnable in snap.analysis().runnables(file_id)? {
405 if let Some(offset) = offset { 402 if let Some(offset) = offset {
406 if !runnable.range.contains_inclusive(offset) { 403 if !runnable.nav.full_range().contains_inclusive(offset) {
407 continue; 404 continue;
408 } 405 }
409 } 406 }
410 // Do not suggest binary run on other target than binary 407 if should_skip_target(&runnable, cargo_spec.as_ref()) {
411 if let RunnableKind::Bin = runnable.kind { 408 continue;
412 if let Some(spec) = &cargo_spec {
413 match spec.target_kind {
414 TargetKind::Bin => {}
415 _ => continue,
416 }
417 }
418 } 409 }
419 res.push(to_lsp_runnable(&world, file_id, runnable)?); 410
411 res.push(to_proto::runnable(&snap, file_id, runnable)?);
420 } 412 }
421 413
422 // Add `cargo check` and `cargo test` for the whole package 414 // Add `cargo check` and `cargo test` for the whole package
@@ -424,25 +416,31 @@ pub fn handle_runnables(
424 Some(spec) => { 416 Some(spec) => {
425 for &cmd in ["check", "test"].iter() { 417 for &cmd in ["check", "test"].iter() {
426 res.push(lsp_ext::Runnable { 418 res.push(lsp_ext::Runnable {
427 range: Default::default(),
428 label: format!("cargo {} -p {}", cmd, spec.package), 419 label: format!("cargo {} -p {}", cmd, spec.package),
429 bin: "cargo".to_string(), 420 location: None,
430 args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], 421 kind: lsp_ext::RunnableKind::Cargo,
431 extra_args: Vec::new(), 422 args: lsp_ext::CargoRunnable {
432 env: FxHashMap::default(), 423 workspace_root: workspace_root.map(|root| root.to_owned()),
433 cwd: workspace_root.map(|root| root.to_owned()), 424 cargo_args: vec![
425 cmd.to_string(),
426 "--package".to_string(),
427 spec.package.clone(),
428 ],
429 executable_args: Vec::new(),
430 },
434 }) 431 })
435 } 432 }
436 } 433 }
437 None => { 434 None => {
438 res.push(lsp_ext::Runnable { 435 res.push(lsp_ext::Runnable {
439 range: Default::default(),
440 label: "cargo check --workspace".to_string(), 436 label: "cargo check --workspace".to_string(),
441 bin: "cargo".to_string(), 437 location: None,
442 args: vec!["check".to_string(), "--workspace".to_string()], 438 kind: lsp_ext::RunnableKind::Cargo,
443 extra_args: Vec::new(), 439 args: lsp_ext::CargoRunnable {
444 env: FxHashMap::default(), 440 workspace_root: workspace_root.map(|root| root.to_owned()),
445 cwd: workspace_root.map(|root| root.to_owned()), 441 cargo_args: vec!["check".to_string(), "--workspace".to_string()],
442 executable_args: Vec::new(),
443 },
446 }); 444 });
447 } 445 }
448 } 446 }
@@ -450,16 +448,16 @@ pub fn handle_runnables(
450} 448}
451 449
452pub fn handle_completion( 450pub fn handle_completion(
453 world: WorldSnapshot, 451 snap: GlobalStateSnapshot,
454 params: lsp_types::CompletionParams, 452 params: lsp_types::CompletionParams,
455) -> Result<Option<lsp_types::CompletionResponse>> { 453) -> Result<Option<lsp_types::CompletionResponse>> {
456 let _p = profile("handle_completion"); 454 let _p = profile("handle_completion");
457 let position = from_proto::file_position(&world, params.text_document_position)?; 455 let position = from_proto::file_position(&snap, params.text_document_position)?;
458 let completion_triggered_after_single_colon = { 456 let completion_triggered_after_single_colon = {
459 let mut res = false; 457 let mut res = false;
460 if let Some(ctx) = params.context { 458 if let Some(ctx) = params.context {
461 if ctx.trigger_character.unwrap_or_default() == ":" { 459 if ctx.trigger_character.unwrap_or_default() == ":" {
462 let source_file = world.analysis().parse(position.file_id)?; 460 let source_file = snap.analysis().parse(position.file_id)?;
463 let syntax = source_file.syntax(); 461 let syntax = source_file.syntax();
464 let text = syntax.text(); 462 let text = syntax.text();
465 if let Some(next_char) = text.char_at(position.offset) { 463 if let Some(next_char) = text.char_at(position.offset) {
@@ -477,12 +475,12 @@ pub fn handle_completion(
477 return Ok(None); 475 return Ok(None);
478 } 476 }
479 477
480 let items = match world.analysis().completions(&world.config.completion, position)? { 478 let items = match snap.analysis().completions(&snap.config.completion, position)? {
481 None => return Ok(None), 479 None => return Ok(None),
482 Some(items) => items, 480 Some(items) => items,
483 }; 481 };
484 let line_index = world.analysis().file_line_index(position.file_id)?; 482 let line_index = snap.analysis().file_line_index(position.file_id)?;
485 let line_endings = world.file_line_endings(position.file_id); 483 let line_endings = snap.file_line_endings(position.file_id);
486 let items: Vec<CompletionItem> = items 484 let items: Vec<CompletionItem> = items
487 .into_iter() 485 .into_iter()
488 .map(|item| to_proto::completion_item(&line_index, line_endings, item)) 486 .map(|item| to_proto::completion_item(&line_index, line_endings, item))
@@ -492,15 +490,15 @@ pub fn handle_completion(
492} 490}
493 491
494pub fn handle_folding_range( 492pub fn handle_folding_range(
495 world: WorldSnapshot, 493 snap: GlobalStateSnapshot,
496 params: FoldingRangeParams, 494 params: FoldingRangeParams,
497) -> Result<Option<Vec<FoldingRange>>> { 495) -> Result<Option<Vec<FoldingRange>>> {
498 let _p = profile("handle_folding_range"); 496 let _p = profile("handle_folding_range");
499 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 497 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
500 let folds = world.analysis().folding_ranges(file_id)?; 498 let folds = snap.analysis().folding_ranges(file_id)?;
501 let text = world.analysis().file_text(file_id)?; 499 let text = snap.analysis().file_text(file_id)?;
502 let line_index = world.analysis().file_line_index(file_id)?; 500 let line_index = snap.analysis().file_line_index(file_id)?;
503 let line_folding_only = world.config.client_caps.line_folding_only; 501 let line_folding_only = snap.config.client_caps.line_folding_only;
504 let res = folds 502 let res = folds
505 .into_iter() 503 .into_iter()
506 .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) 504 .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it))
@@ -509,16 +507,16 @@ pub fn handle_folding_range(
509} 507}
510 508
511pub fn handle_signature_help( 509pub fn handle_signature_help(
512 world: WorldSnapshot, 510 snap: GlobalStateSnapshot,
513 params: lsp_types::SignatureHelpParams, 511 params: lsp_types::SignatureHelpParams,
514) -> Result<Option<lsp_types::SignatureHelp>> { 512) -> Result<Option<lsp_types::SignatureHelp>> {
515 let _p = profile("handle_signature_help"); 513 let _p = profile("handle_signature_help");
516 let position = from_proto::file_position(&world, params.text_document_position_params)?; 514 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
517 let call_info = match world.analysis().call_info(position)? { 515 let call_info = match snap.analysis().call_info(position)? {
518 None => return Ok(None), 516 None => return Ok(None),
519 Some(it) => it, 517 Some(it) => it,
520 }; 518 };
521 let concise = !world.config.call_info_full; 519 let concise = !snap.config.call_info_full;
522 let mut active_parameter = call_info.active_parameter.map(|it| it as i64); 520 let mut active_parameter = call_info.active_parameter.map(|it| it as i64);
523 if concise && call_info.signature.has_self_param { 521 if concise && call_info.signature.has_self_param {
524 active_parameter = active_parameter.map(|it| it.saturating_sub(1)); 522 active_parameter = active_parameter.map(|it| it.saturating_sub(1));
@@ -532,46 +530,56 @@ pub fn handle_signature_help(
532 })) 530 }))
533} 531}
534 532
535pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Result<Option<Hover>> { 533pub fn handle_hover(
534 snap: GlobalStateSnapshot,
535 params: lsp_types::HoverParams,
536) -> Result<Option<lsp_ext::Hover>> {
536 let _p = profile("handle_hover"); 537 let _p = profile("handle_hover");
537 let position = from_proto::file_position(&world, params.text_document_position_params)?; 538 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
538 let info = match world.analysis().hover(position)? { 539 let info = match snap.analysis().hover(position)? {
539 None => return Ok(None), 540 None => return Ok(None),
540 Some(info) => info, 541 Some(info) => info,
541 }; 542 };
542 let line_index = world.analysis.file_line_index(position.file_id)?; 543 let line_index = snap.analysis.file_line_index(position.file_id)?;
543 let range = to_proto::range(&line_index, info.range); 544 let range = to_proto::range(&line_index, info.range);
544 let res = Hover { 545 let hover = lsp_ext::Hover {
545 contents: HoverContents::Markup(MarkupContent { 546 hover: lsp_types::Hover {
546 kind: MarkupKind::Markdown, 547 contents: HoverContents::Markup(MarkupContent {
547 value: crate::markdown::format_docs(&info.info.to_markup()), 548 kind: MarkupKind::Markdown,
548 }), 549 value: crate::markdown::format_docs(&info.info.to_markup()),
549 range: Some(range), 550 }),
551 range: Some(range),
552 },
553 actions: prepare_hover_actions(&snap, position.file_id, info.info.actions()),
550 }; 554 };
551 Ok(Some(res)) 555
556 Ok(Some(hover))
552} 557}
553 558
554pub fn handle_prepare_rename( 559pub fn handle_prepare_rename(
555 world: WorldSnapshot, 560 snap: GlobalStateSnapshot,
556 params: lsp_types::TextDocumentPositionParams, 561 params: lsp_types::TextDocumentPositionParams,
557) -> Result<Option<PrepareRenameResponse>> { 562) -> Result<Option<PrepareRenameResponse>> {
558 let _p = profile("handle_prepare_rename"); 563 let _p = profile("handle_prepare_rename");
559 let position = from_proto::file_position(&world, params)?; 564 let position = from_proto::file_position(&snap, params)?;
560 565
561 let optional_change = world.analysis().rename(position, "dummy")?; 566 let optional_change = snap.analysis().rename(position, "dummy")?;
562 let range = match optional_change { 567 let range = match optional_change {
563 None => return Ok(None), 568 None => return Ok(None),
564 Some(it) => it.range, 569 Some(it) => it.range,
565 }; 570 };
566 571
567 let line_index = world.analysis().file_line_index(position.file_id)?; 572 let line_index = snap.analysis().file_line_index(position.file_id)?;
568 let range = to_proto::range(&line_index, range); 573 let range = to_proto::range(&line_index, range);
569 Ok(Some(PrepareRenameResponse::Range(range))) 574 Ok(Some(PrepareRenameResponse::Range(range)))
570} 575}
571 576
572pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { 577pub fn handle_rename(
578 snap: GlobalStateSnapshot,
579 params: RenameParams,
580) -> Result<Option<WorkspaceEdit>> {
573 let _p = profile("handle_rename"); 581 let _p = profile("handle_rename");
574 let position = from_proto::file_position(&world, params.text_document_position)?; 582 let position = from_proto::file_position(&snap, params.text_document_position)?;
575 583
576 if params.new_name.is_empty() { 584 if params.new_name.is_empty() {
577 return Err(LspError::new( 585 return Err(LspError::new(
@@ -581,36 +589,36 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio
581 .into()); 589 .into());
582 } 590 }
583 591
584 let optional_change = world.analysis().rename(position, &*params.new_name)?; 592 let optional_change = snap.analysis().rename(position, &*params.new_name)?;
585 let source_change = match optional_change { 593 let source_change = match optional_change {
586 None => return Ok(None), 594 None => return Ok(None),
587 Some(it) => it.info, 595 Some(it) => it.info,
588 }; 596 };
589 let workspace_edit = to_proto::workspace_edit(&world, source_change)?; 597 let workspace_edit = to_proto::workspace_edit(&snap, source_change)?;
590 Ok(Some(workspace_edit)) 598 Ok(Some(workspace_edit))
591} 599}
592 600
593pub fn handle_references( 601pub fn handle_references(
594 world: WorldSnapshot, 602 snap: GlobalStateSnapshot,
595 params: lsp_types::ReferenceParams, 603 params: lsp_types::ReferenceParams,
596) -> Result<Option<Vec<Location>>> { 604) -> Result<Option<Vec<Location>>> {
597 let _p = profile("handle_references"); 605 let _p = profile("handle_references");
598 let position = from_proto::file_position(&world, params.text_document_position)?; 606 let position = from_proto::file_position(&snap, params.text_document_position)?;
599 607
600 let refs = match world.analysis().find_all_refs(position, None)? { 608 let refs = match snap.analysis().find_all_refs(position, None)? {
601 None => return Ok(None), 609 None => return Ok(None),
602 Some(refs) => refs, 610 Some(refs) => refs,
603 }; 611 };
604 612
605 let locations = if params.context.include_declaration { 613 let locations = if params.context.include_declaration {
606 refs.into_iter() 614 refs.into_iter()
607 .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) 615 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok())
608 .collect() 616 .collect()
609 } else { 617 } else {
610 // Only iterate over the references if include_declaration was false 618 // Only iterate over the references if include_declaration was false
611 refs.references() 619 refs.references()
612 .iter() 620 .iter()
613 .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) 621 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok())
614 .collect() 622 .collect()
615 }; 623 };
616 624
@@ -618,24 +626,24 @@ pub fn handle_references(
618} 626}
619 627
620pub fn handle_formatting( 628pub fn handle_formatting(
621 world: WorldSnapshot, 629 snap: GlobalStateSnapshot,
622 params: DocumentFormattingParams, 630 params: DocumentFormattingParams,
623) -> Result<Option<Vec<lsp_types::TextEdit>>> { 631) -> Result<Option<Vec<lsp_types::TextEdit>>> {
624 let _p = profile("handle_formatting"); 632 let _p = profile("handle_formatting");
625 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 633 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
626 let file = world.analysis().file_text(file_id)?; 634 let file = snap.analysis().file_text(file_id)?;
627 let crate_ids = world.analysis().crate_for(file_id)?; 635 let crate_ids = snap.analysis().crate_for(file_id)?;
628 636
629 let file_line_index = world.analysis().file_line_index(file_id)?; 637 let file_line_index = snap.analysis().file_line_index(file_id)?;
630 let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); 638 let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str()));
631 639
632 let mut rustfmt = match &world.config.rustfmt { 640 let mut rustfmt = match &snap.config.rustfmt {
633 RustfmtConfig::Rustfmt { extra_args } => { 641 RustfmtConfig::Rustfmt { extra_args } => {
634 let mut cmd = process::Command::new("rustfmt"); 642 let mut cmd = process::Command::new("rustfmt");
635 cmd.args(extra_args); 643 cmd.args(extra_args);
636 if let Some(&crate_id) = crate_ids.first() { 644 if let Some(&crate_id) = crate_ids.first() {
637 // Assume all crates are in the same edition 645 // Assume all crates are in the same edition
638 let edition = world.analysis().crate_edition(crate_id)?; 646 let edition = snap.analysis().crate_edition(crate_id)?;
639 cmd.arg("--edition"); 647 cmd.arg("--edition");
640 cmd.arg(edition.to_string()); 648 cmd.arg(edition.to_string());
641 } 649 }
@@ -693,136 +701,145 @@ pub fn handle_formatting(
693 }])) 701 }]))
694} 702}
695 703
696pub fn handle_code_action( 704fn handle_fixes(
697 world: WorldSnapshot, 705 snap: &GlobalStateSnapshot,
698 params: lsp_types::CodeActionParams, 706 params: &lsp_types::CodeActionParams,
699) -> Result<Option<Vec<lsp_ext::CodeAction>>> { 707 res: &mut Vec<lsp_ext::CodeAction>,
700 let _p = profile("handle_code_action"); 708) -> Result<()> {
701 // We intentionally don't support command-based actions, as those either 709 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
702 // requires custom client-code anyway, or requires server-initiated edits. 710 let line_index = snap.analysis().file_line_index(file_id)?;
703 // Server initiated edits break causality, so we avoid those as well.
704 if !world.config.client_caps.code_action_literals {
705 return Ok(None);
706 }
707
708 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
709 let line_index = world.analysis().file_line_index(file_id)?;
710 let range = from_proto::text_range(&line_index, params.range); 711 let range = from_proto::text_range(&line_index, params.range);
711 let frange = FileRange { file_id, range }; 712 let diagnostics = snap.analysis().diagnostics(file_id)?;
712
713 let diagnostics = world.analysis().diagnostics(file_id)?;
714 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
715 713
716 let fixes_from_diagnostics = diagnostics 714 let fixes_from_diagnostics = diagnostics
717 .into_iter() 715 .into_iter()
718 .filter_map(|d| Some((d.range, d.fix?))) 716 .filter_map(|d| Some((d.range, d.fix?)))
719 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) 717 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some())
720 .map(|(_range, fix)| fix); 718 .map(|(_range, fix)| fix);
721
722 for fix in fixes_from_diagnostics { 719 for fix in fixes_from_diagnostics {
723 let title = fix.label; 720 let title = fix.label;
724 let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?; 721 let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
725 let action = 722 let action = lsp_ext::CodeAction {
726 lsp_ext::CodeAction { title, group: None, kind: None, edit: Some(edit), command: None }; 723 title,
724 id: None,
725 group: None,
726 kind: Some(lsp_types::code_action_kind::QUICKFIX.into()),
727 edit: Some(edit),
728 command: None,
729 };
727 res.push(action); 730 res.push(action);
728 } 731 }
729 732
730 for fix in world.check_fixes.get(&file_id).into_iter().flatten() { 733 for fix in snap.check_fixes.get(&file_id).into_iter().flatten() {
731 let fix_range = from_proto::text_range(&line_index, fix.range); 734 let fix_range = from_proto::text_range(&line_index, fix.range);
732 if fix_range.intersect(range).is_none() { 735 if fix_range.intersect(range).is_none() {
733 continue; 736 continue;
734 } 737 }
735 res.push(fix.action.clone()); 738 res.push(fix.action.clone());
736 } 739 }
740 Ok(())
741}
737 742
738 for assist in world.analysis().assists(&world.config.assist, frange)?.into_iter() { 743pub fn handle_code_action(
739 res.push(to_proto::code_action(&world, assist)?.into()); 744 snap: GlobalStateSnapshot,
745 params: lsp_types::CodeActionParams,
746) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
747 let _p = profile("handle_code_action");
748 // We intentionally don't support command-based actions, as those either
749 // requires custom client-code anyway, or requires server-initiated edits.
750 // Server initiated edits break causality, so we avoid those as well.
751 if !snap.config.client_caps.code_action_literals {
752 return Ok(None);
753 }
754
755 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
756 let line_index = snap.analysis().file_line_index(file_id)?;
757 let range = from_proto::text_range(&line_index, params.range);
758 let frange = FileRange { file_id, range };
759 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
760
761 handle_fixes(&snap, &params, &mut res)?;
762
763 if snap.config.client_caps.resolve_code_action {
764 for (index, assist) in
765 snap.analysis().unresolved_assists(&snap.config.assist, frange)?.into_iter().enumerate()
766 {
767 res.push(to_proto::unresolved_code_action(&snap, assist, index)?);
768 }
769 } else {
770 for assist in snap.analysis().resolved_assists(&snap.config.assist, frange)?.into_iter() {
771 res.push(to_proto::resolved_code_action(&snap, assist)?);
772 }
740 } 773 }
774
741 Ok(Some(res)) 775 Ok(Some(res))
742} 776}
743 777
778pub fn handle_resolve_code_action(
779 snap: GlobalStateSnapshot,
780 params: lsp_ext::ResolveCodeActionParams,
781) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> {
782 let _p = profile("handle_resolve_code_action");
783 let file_id = from_proto::file_id(&snap, &params.code_action_params.text_document.uri)?;
784 let line_index = snap.analysis().file_line_index(file_id)?;
785 let range = from_proto::text_range(&line_index, params.code_action_params.range);
786 let frange = FileRange { file_id, range };
787
788 let assists = snap.analysis().resolved_assists(&snap.config.assist, frange)?;
789 let (id_string, index) = split1(&params.id, ':').unwrap();
790 let index = index.parse::<usize>().unwrap();
791 let assist = &assists[index];
792 assert!(assist.assist.id.0 == id_string);
793 Ok(to_proto::resolved_code_action(&snap, assist.clone())?.edit)
794}
795
744pub fn handle_code_lens( 796pub fn handle_code_lens(
745 world: WorldSnapshot, 797 snap: GlobalStateSnapshot,
746 params: lsp_types::CodeLensParams, 798 params: lsp_types::CodeLensParams,
747) -> Result<Option<Vec<CodeLens>>> { 799) -> Result<Option<Vec<CodeLens>>> {
748 let _p = profile("handle_code_lens"); 800 let _p = profile("handle_code_lens");
749 let mut lenses: Vec<CodeLens> = Default::default(); 801 let mut lenses: Vec<CodeLens> = Default::default();
750 802
751 if world.config.lens.none() { 803 if snap.config.lens.none() {
752 // early return before any db query! 804 // early return before any db query!
753 return Ok(Some(lenses)); 805 return Ok(Some(lenses));
754 } 806 }
755 807
756 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 808 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
757 let line_index = world.analysis().file_line_index(file_id)?; 809 let line_index = snap.analysis().file_line_index(file_id)?;
758 let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; 810 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
759 811
760 if world.config.lens.runnable() { 812 if snap.config.lens.runnable() {
761 // Gather runnables 813 // Gather runnables
762 for runnable in world.analysis().runnables(file_id)? { 814 for runnable in snap.analysis().runnables(file_id)? {
763 let (run_title, debugee) = match &runnable.kind { 815 if should_skip_target(&runnable, cargo_spec.as_ref()) {
764 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { 816 continue;
765 ("▶\u{fe0e} Run Test", true) 817 }
766 }
767 RunnableKind::DocTest { .. } => {
768 // cargo does not support -no-run for doctests
769 ("▶\u{fe0e} Run Doctest", false)
770 }
771 RunnableKind::Bench { .. } => {
772 // Nothing wrong with bench debugging
773 ("Run Bench", true)
774 }
775 RunnableKind::Bin => {
776 // Do not suggest binary run on other target than binary
777 match &cargo_spec {
778 Some(spec) => match spec.target_kind {
779 TargetKind::Bin => ("Run", true),
780 _ => continue,
781 },
782 None => continue,
783 }
784 }
785 };
786 818
787 let mut r = to_lsp_runnable(&world, file_id, runnable)?; 819 let action = runnable.action();
788 if world.config.lens.run { 820 let range = to_proto::range(&line_index, runnable.nav.range());
821 let r = to_proto::runnable(&snap, file_id, runnable)?;
822 if snap.config.lens.run {
789 let lens = CodeLens { 823 let lens = CodeLens {
790 range: r.range, 824 range,
791 command: Some(Command { 825 command: Some(run_single_command(&r, action.run_title)),
792 title: run_title.to_string(),
793 command: "rust-analyzer.runSingle".into(),
794 arguments: Some(vec![to_value(&r).unwrap()]),
795 }),
796 data: None, 826 data: None,
797 }; 827 };
798 lenses.push(lens); 828 lenses.push(lens);
799 } 829 }
800 830
801 if debugee && world.config.lens.debug { 831 if action.debugee && snap.config.lens.debug {
802 if r.args[0] == "run" { 832 let debug_lens =
803 r.args[0] = "build".into(); 833 CodeLens { range, command: Some(debug_single_command(&r)), data: None };
804 } else {
805 r.args.push("--no-run".into());
806 }
807 let debug_lens = CodeLens {
808 range: r.range,
809 command: Some(Command {
810 title: "Debug".into(),
811 command: "rust-analyzer.debugSingle".into(),
812 arguments: Some(vec![to_value(r).unwrap()]),
813 }),
814 data: None,
815 };
816 lenses.push(debug_lens); 834 lenses.push(debug_lens);
817 } 835 }
818 } 836 }
819 } 837 }
820 838
821 if world.config.lens.impementations { 839 if snap.config.lens.impementations {
822 // Handle impls 840 // Handle impls
823 lenses.extend( 841 lenses.extend(
824 world 842 snap.analysis()
825 .analysis()
826 .file_structure(file_id)? 843 .file_structure(file_id)?
827 .into_iter() 844 .into_iter()
828 .filter(|it| match it.kind { 845 .filter(|it| match it.kind {
@@ -857,14 +874,17 @@ enum CodeLensResolveData {
857 Impls(lsp_types::request::GotoImplementationParams), 874 Impls(lsp_types::request::GotoImplementationParams),
858} 875}
859 876
860pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { 877pub fn handle_code_lens_resolve(
878 snap: GlobalStateSnapshot,
879 code_lens: CodeLens,
880) -> Result<CodeLens> {
861 let _p = profile("handle_code_lens_resolve"); 881 let _p = profile("handle_code_lens_resolve");
862 let data = code_lens.data.unwrap(); 882 let data = code_lens.data.unwrap();
863 let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?; 883 let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?;
864 match resolve { 884 match resolve {
865 Some(CodeLensResolveData::Impls(lens_params)) => { 885 Some(CodeLensResolveData::Impls(lens_params)) => {
866 let locations: Vec<Location> = 886 let locations: Vec<Location> =
867 match handle_goto_implementation(world, lens_params.clone())? { 887 match handle_goto_implementation(snap, lens_params.clone())? {
868 Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc], 888 Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc],
869 Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs, 889 Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs,
870 Some(lsp_types::GotoDefinitionResponse::Link(links)) => links 890 Some(lsp_types::GotoDefinitionResponse::Link(links)) => links
@@ -874,24 +894,13 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re
874 _ => vec![], 894 _ => vec![],
875 }; 895 };
876 896
877 let title = if locations.len() == 1 { 897 let title = implementation_title(locations.len());
878 "1 implementation".into() 898 let cmd = show_references_command(
879 } else {
880 format!("{} implementations", locations.len())
881 };
882
883 // We cannot use the 'editor.action.showReferences' command directly
884 // because that command requires vscode types which we convert in the handler
885 // on the client side.
886 let cmd = Command {
887 title, 899 title,
888 command: "rust-analyzer.showReferences".into(), 900 &lens_params.text_document_position_params.text_document.uri,
889 arguments: Some(vec![ 901 code_lens.range.start,
890 to_value(&lens_params.text_document_position_params.text_document.uri).unwrap(), 902 locations,
891 to_value(code_lens.range.start).unwrap(), 903 );
892 to_value(locations).unwrap(),
893 ]),
894 };
895 Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) 904 Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None })
896 } 905 }
897 None => Ok(CodeLens { 906 None => Ok(CodeLens {
@@ -903,14 +912,14 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re
903} 912}
904 913
905pub fn handle_document_highlight( 914pub fn handle_document_highlight(
906 world: WorldSnapshot, 915 snap: GlobalStateSnapshot,
907 params: lsp_types::DocumentHighlightParams, 916 params: lsp_types::DocumentHighlightParams,
908) -> Result<Option<Vec<DocumentHighlight>>> { 917) -> Result<Option<Vec<DocumentHighlight>>> {
909 let _p = profile("handle_document_highlight"); 918 let _p = profile("handle_document_highlight");
910 let position = from_proto::file_position(&world, params.text_document_position_params)?; 919 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
911 let line_index = world.analysis().file_line_index(position.file_id)?; 920 let line_index = snap.analysis().file_line_index(position.file_id)?;
912 921
913 let refs = match world 922 let refs = match snap
914 .analysis() 923 .analysis()
915 .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))? 924 .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))?
916 { 925 {
@@ -930,19 +939,19 @@ pub fn handle_document_highlight(
930} 939}
931 940
932pub fn handle_ssr( 941pub fn handle_ssr(
933 world: WorldSnapshot, 942 snap: GlobalStateSnapshot,
934 params: lsp_ext::SsrParams, 943 params: lsp_ext::SsrParams,
935) -> Result<lsp_types::WorkspaceEdit> { 944) -> Result<lsp_types::WorkspaceEdit> {
936 let _p = profile("handle_ssr"); 945 let _p = profile("handle_ssr");
937 let source_change = 946 let source_change =
938 world.analysis().structural_search_replace(&params.query, params.parse_only)??; 947 snap.analysis().structural_search_replace(&params.query, params.parse_only)??;
939 to_proto::workspace_edit(&world, source_change) 948 to_proto::workspace_edit(&snap, source_change)
940} 949}
941 950
942pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { 951pub fn publish_diagnostics(snap: &GlobalStateSnapshot, file_id: FileId) -> Result<DiagnosticTask> {
943 let _p = profile("publish_diagnostics"); 952 let _p = profile("publish_diagnostics");
944 let line_index = world.analysis().file_line_index(file_id)?; 953 let line_index = snap.analysis().file_line_index(file_id)?;
945 let diagnostics: Vec<Diagnostic> = world 954 let diagnostics: Vec<Diagnostic> = snap
946 .analysis() 955 .analysis()
947 .diagnostics(file_id)? 956 .diagnostics(file_id)?
948 .into_iter() 957 .into_iter()
@@ -959,87 +968,29 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia
959 Ok(DiagnosticTask::SetNative(file_id, diagnostics)) 968 Ok(DiagnosticTask::SetNative(file_id, diagnostics))
960} 969}
961 970
962fn to_lsp_runnable(
963 world: &WorldSnapshot,
964 file_id: FileId,
965 runnable: Runnable,
966) -> Result<lsp_ext::Runnable> {
967 let spec = CargoTargetSpec::for_file(world, file_id)?;
968 let target = spec.as_ref().map(|s| s.target.clone());
969 let mut features_needed = vec![];
970 for cfg_expr in &runnable.cfg_exprs {
971 collect_minimal_features_needed(cfg_expr, &mut features_needed);
972 }
973 let (args, extra_args) =
974 CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?;
975 let line_index = world.analysis().file_line_index(file_id)?;
976 let label = match &runnable.kind {
977 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
978 RunnableKind::TestMod { path } => format!("test-mod {}", path),
979 RunnableKind::Bench { test_id } => format!("bench {}", test_id),
980 RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
981 RunnableKind::Bin => {
982 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
983 }
984 };
985 Ok(lsp_ext::Runnable {
986 range: to_proto::range(&line_index, runnable.range),
987 label,
988 bin: "cargo".to_string(),
989 args,
990 extra_args,
991 env: {
992 let mut m = FxHashMap::default();
993 m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
994 m
995 },
996 cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()),
997 })
998}
999
1000/// Fill minimal features needed
1001fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec<SmolStr>) {
1002 match cfg_expr {
1003 CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
1004 CfgExpr::All(preds) => {
1005 preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features));
1006 }
1007 CfgExpr::Any(preds) => {
1008 for cfg in preds {
1009 let len_features = features.len();
1010 collect_minimal_features_needed(cfg, features);
1011 if len_features != features.len() {
1012 break;
1013 }
1014 }
1015 }
1016 _ => {}
1017 }
1018}
1019
1020pub fn handle_inlay_hints( 971pub fn handle_inlay_hints(
1021 world: WorldSnapshot, 972 snap: GlobalStateSnapshot,
1022 params: InlayHintsParams, 973 params: InlayHintsParams,
1023) -> Result<Vec<InlayHint>> { 974) -> Result<Vec<InlayHint>> {
1024 let _p = profile("handle_inlay_hints"); 975 let _p = profile("handle_inlay_hints");
1025 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 976 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1026 let analysis = world.analysis(); 977 let analysis = snap.analysis();
1027 let line_index = analysis.file_line_index(file_id)?; 978 let line_index = analysis.file_line_index(file_id)?;
1028 Ok(analysis 979 Ok(analysis
1029 .inlay_hints(file_id, &world.config.inlay_hints)? 980 .inlay_hints(file_id, &snap.config.inlay_hints)?
1030 .into_iter() 981 .into_iter()
1031 .map(|it| to_proto::inlay_int(&line_index, it)) 982 .map(|it| to_proto::inlay_int(&line_index, it))
1032 .collect()) 983 .collect())
1033} 984}
1034 985
1035pub fn handle_call_hierarchy_prepare( 986pub fn handle_call_hierarchy_prepare(
1036 world: WorldSnapshot, 987 snap: GlobalStateSnapshot,
1037 params: CallHierarchyPrepareParams, 988 params: CallHierarchyPrepareParams,
1038) -> Result<Option<Vec<CallHierarchyItem>>> { 989) -> Result<Option<Vec<CallHierarchyItem>>> {
1039 let _p = profile("handle_call_hierarchy_prepare"); 990 let _p = profile("handle_call_hierarchy_prepare");
1040 let position = from_proto::file_position(&world, params.text_document_position_params)?; 991 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
1041 992
1042 let nav_info = match world.analysis().call_hierarchy(position)? { 993 let nav_info = match snap.analysis().call_hierarchy(position)? {
1043 None => return Ok(None), 994 None => return Ok(None),
1044 Some(it) => it, 995 Some(it) => it,
1045 }; 996 };
@@ -1048,24 +999,24 @@ pub fn handle_call_hierarchy_prepare(
1048 let res = navs 999 let res = navs
1049 .into_iter() 1000 .into_iter()
1050 .filter(|it| it.kind() == SyntaxKind::FN_DEF) 1001 .filter(|it| it.kind() == SyntaxKind::FN_DEF)
1051 .map(|it| to_proto::call_hierarchy_item(&world, it)) 1002 .map(|it| to_proto::call_hierarchy_item(&snap, it))
1052 .collect::<Result<Vec<_>>>()?; 1003 .collect::<Result<Vec<_>>>()?;
1053 1004
1054 Ok(Some(res)) 1005 Ok(Some(res))
1055} 1006}
1056 1007
1057pub fn handle_call_hierarchy_incoming( 1008pub fn handle_call_hierarchy_incoming(
1058 world: WorldSnapshot, 1009 snap: GlobalStateSnapshot,
1059 params: CallHierarchyIncomingCallsParams, 1010 params: CallHierarchyIncomingCallsParams,
1060) -> Result<Option<Vec<CallHierarchyIncomingCall>>> { 1011) -> Result<Option<Vec<CallHierarchyIncomingCall>>> {
1061 let _p = profile("handle_call_hierarchy_incoming"); 1012 let _p = profile("handle_call_hierarchy_incoming");
1062 let item = params.item; 1013 let item = params.item;
1063 1014
1064 let doc = TextDocumentIdentifier::new(item.uri); 1015 let doc = TextDocumentIdentifier::new(item.uri);
1065 let frange = from_proto::file_range(&world, doc, item.range)?; 1016 let frange = from_proto::file_range(&snap, doc, item.range)?;
1066 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; 1017 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1067 1018
1068 let call_items = match world.analysis().incoming_calls(fpos)? { 1019 let call_items = match snap.analysis().incoming_calls(fpos)? {
1069 None => return Ok(None), 1020 None => return Ok(None),
1070 Some(it) => it, 1021 Some(it) => it,
1071 }; 1022 };
@@ -1074,8 +1025,8 @@ pub fn handle_call_hierarchy_incoming(
1074 1025
1075 for call_item in call_items.into_iter() { 1026 for call_item in call_items.into_iter() {
1076 let file_id = call_item.target.file_id(); 1027 let file_id = call_item.target.file_id();
1077 let line_index = world.analysis().file_line_index(file_id)?; 1028 let line_index = snap.analysis().file_line_index(file_id)?;
1078 let item = to_proto::call_hierarchy_item(&world, call_item.target)?; 1029 let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1079 res.push(CallHierarchyIncomingCall { 1030 res.push(CallHierarchyIncomingCall {
1080 from: item, 1031 from: item,
1081 from_ranges: call_item 1032 from_ranges: call_item
@@ -1090,17 +1041,17 @@ pub fn handle_call_hierarchy_incoming(
1090} 1041}
1091 1042
1092pub fn handle_call_hierarchy_outgoing( 1043pub fn handle_call_hierarchy_outgoing(
1093 world: WorldSnapshot, 1044 snap: GlobalStateSnapshot,
1094 params: CallHierarchyOutgoingCallsParams, 1045 params: CallHierarchyOutgoingCallsParams,
1095) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> { 1046) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> {
1096 let _p = profile("handle_call_hierarchy_outgoing"); 1047 let _p = profile("handle_call_hierarchy_outgoing");
1097 let item = params.item; 1048 let item = params.item;
1098 1049
1099 let doc = TextDocumentIdentifier::new(item.uri); 1050 let doc = TextDocumentIdentifier::new(item.uri);
1100 let frange = from_proto::file_range(&world, doc, item.range)?; 1051 let frange = from_proto::file_range(&snap, doc, item.range)?;
1101 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; 1052 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1102 1053
1103 let call_items = match world.analysis().outgoing_calls(fpos)? { 1054 let call_items = match snap.analysis().outgoing_calls(fpos)? {
1104 None => return Ok(None), 1055 None => return Ok(None),
1105 Some(it) => it, 1056 Some(it) => it,
1106 }; 1057 };
@@ -1109,8 +1060,8 @@ pub fn handle_call_hierarchy_outgoing(
1109 1060
1110 for call_item in call_items.into_iter() { 1061 for call_item in call_items.into_iter() {
1111 let file_id = call_item.target.file_id(); 1062 let file_id = call_item.target.file_id();
1112 let line_index = world.analysis().file_line_index(file_id)?; 1063 let line_index = snap.analysis().file_line_index(file_id)?;
1113 let item = to_proto::call_hierarchy_item(&world, call_item.target)?; 1064 let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1114 res.push(CallHierarchyOutgoingCall { 1065 res.push(CallHierarchyOutgoingCall {
1115 to: item, 1066 to: item,
1116 from_ranges: call_item 1067 from_ranges: call_item
@@ -1125,82 +1076,165 @@ pub fn handle_call_hierarchy_outgoing(
1125} 1076}
1126 1077
1127pub fn handle_semantic_tokens( 1078pub fn handle_semantic_tokens(
1128 world: WorldSnapshot, 1079 snap: GlobalStateSnapshot,
1129 params: SemanticTokensParams, 1080 params: SemanticTokensParams,
1130) -> Result<Option<SemanticTokensResult>> { 1081) -> Result<Option<SemanticTokensResult>> {
1131 let _p = profile("handle_semantic_tokens"); 1082 let _p = profile("handle_semantic_tokens");
1132 1083
1133 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 1084 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1134 let text = world.analysis().file_text(file_id)?; 1085 let text = snap.analysis().file_text(file_id)?;
1135 let line_index = world.analysis().file_line_index(file_id)?; 1086 let line_index = snap.analysis().file_line_index(file_id)?;
1136 1087
1137 let highlights = world.analysis().highlight(file_id)?; 1088 let highlights = snap.analysis().highlight(file_id)?;
1138 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 1089 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1139 Ok(Some(semantic_tokens.into())) 1090 Ok(Some(semantic_tokens.into()))
1140} 1091}
1141 1092
1142pub fn handle_semantic_tokens_range( 1093pub fn handle_semantic_tokens_range(
1143 world: WorldSnapshot, 1094 snap: GlobalStateSnapshot,
1144 params: SemanticTokensRangeParams, 1095 params: SemanticTokensRangeParams,
1145) -> Result<Option<SemanticTokensRangeResult>> { 1096) -> Result<Option<SemanticTokensRangeResult>> {
1146 let _p = profile("handle_semantic_tokens_range"); 1097 let _p = profile("handle_semantic_tokens_range");
1147 1098
1148 let frange = from_proto::file_range(&world, params.text_document, params.range)?; 1099 let frange = from_proto::file_range(&snap, params.text_document, params.range)?;
1149 let text = world.analysis().file_text(frange.file_id)?; 1100 let text = snap.analysis().file_text(frange.file_id)?;
1150 let line_index = world.analysis().file_line_index(frange.file_id)?; 1101 let line_index = snap.analysis().file_line_index(frange.file_id)?;
1151 1102
1152 let highlights = world.analysis().highlight_range(frange)?; 1103 let highlights = snap.analysis().highlight_range(frange)?;
1153 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 1104 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1154 Ok(Some(semantic_tokens.into())) 1105 Ok(Some(semantic_tokens.into()))
1155} 1106}
1156 1107
1157#[cfg(test)] 1108fn implementation_title(count: usize) -> String {
1158mod tests { 1109 if count == 1 {
1159 use super::*; 1110 "1 implementation".into()
1111 } else {
1112 format!("{} implementations", count)
1113 }
1114}
1160 1115
1161 use mbe::{ast_to_token_tree, TokenMap}; 1116fn show_references_command(
1162 use ra_cfg::parse_cfg; 1117 title: String,
1163 use ra_syntax::{ 1118 uri: &lsp_types::Url,
1164 ast::{self, AstNode}, 1119 position: lsp_types::Position,
1165 SmolStr, 1120 locations: Vec<lsp_types::Location>,
1166 }; 1121) -> Command {
1122 // We cannot use the 'editor.action.showReferences' command directly
1123 // because that command requires vscode types which we convert in the handler
1124 // on the client side.
1125
1126 Command {
1127 title,
1128 command: "rust-analyzer.showReferences".into(),
1129 arguments: Some(vec![
1130 to_value(uri).unwrap(),
1131 to_value(position).unwrap(),
1132 to_value(locations).unwrap(),
1133 ]),
1134 }
1135}
1167 1136
1168 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) { 1137fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command {
1169 let source_file = ast::SourceFile::parse(input).ok().unwrap(); 1138 Command {
1170 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); 1139 title: title.to_string(),
1171 ast_to_token_tree(&tt).unwrap() 1140 command: "rust-analyzer.runSingle".into(),
1141 arguments: Some(vec![to_value(runnable).unwrap()]),
1172 } 1142 }
1143}
1173 1144
1174 #[test] 1145fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command {
1175 fn test_cfg_expr_minimal_features_needed() { 1146 Command {
1176 let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#); 1147 title: "Debug".into(),
1177 let cfg_expr = parse_cfg(&subtree); 1148 command: "rust-analyzer.debugSingle".into(),
1178 let mut min_features = vec![]; 1149 arguments: Some(vec![to_value(runnable).unwrap()]),
1179 collect_minimal_features_needed(&cfg_expr, &mut min_features); 1150 }
1151}
1180 1152
1181 assert_eq!(min_features, vec![SmolStr::new("baz")]); 1153fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink {
1154 lsp_ext::CommandLink { tooltip: Some(tooltip), command }
1155}
1182 1156
1183 let (subtree, _) = 1157fn show_impl_command_link(
1184 get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#); 1158 snap: &GlobalStateSnapshot,
1185 let cfg_expr = parse_cfg(&subtree); 1159 position: &FilePosition,
1160) -> Option<lsp_ext::CommandLinkGroup> {
1161 if snap.config.hover.implementations {
1162 if let Some(nav_data) = snap.analysis().goto_implementation(*position).unwrap_or(None) {
1163 let uri = to_proto::url(snap, position.file_id).ok()?;
1164 let line_index = snap.analysis().file_line_index(position.file_id).ok()?;
1165 let position = to_proto::position(&line_index, position.offset);
1166 let locations: Vec<_> = nav_data
1167 .info
1168 .iter()
1169 .filter_map(|it| to_proto::location(snap, it.file_range()).ok())
1170 .collect();
1171 let title = implementation_title(locations.len());
1172 let command = show_references_command(title, &uri, position, locations);
1173
1174 return Some(lsp_ext::CommandLinkGroup {
1175 commands: vec![to_command_link(command, "Go to implementations".into())],
1176 ..Default::default()
1177 });
1178 }
1179 }
1180 None
1181}
1186 1182
1187 let mut min_features = vec![]; 1183fn to_runnable_action(
1188 collect_minimal_features_needed(&cfg_expr, &mut min_features); 1184 snap: &GlobalStateSnapshot,
1189 assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]); 1185 file_id: FileId,
1186 runnable: Runnable,
1187) -> Option<lsp_ext::CommandLinkGroup> {
1188 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?;
1189 if should_skip_target(&runnable, cargo_spec.as_ref()) {
1190 return None;
1191 }
1190 1192
1191 let (subtree, _) = 1193 let action: &'static _ = runnable.action();
1192 get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#); 1194 to_proto::runnable(snap, file_id, runnable).ok().map(|r| {
1193 let cfg_expr = parse_cfg(&subtree); 1195 let mut group = lsp_ext::CommandLinkGroup::default();
1194 1196
1195 let mut min_features = vec![]; 1197 if snap.config.hover.run {
1196 collect_minimal_features_needed(&cfg_expr, &mut min_features); 1198 let run_command = run_single_command(&r, action.run_title);
1197 assert_eq!(min_features, vec![SmolStr::new("baz")]); 1199 group.commands.push(to_command_link(run_command, r.label.clone()));
1200 }
1198 1201
1199 let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#); 1202 if snap.config.hover.debug {
1200 let cfg_expr = parse_cfg(&subtree); 1203 let dbg_command = debug_single_command(&r);
1204 group.commands.push(to_command_link(dbg_command, r.label));
1205 }
1206
1207 group
1208 })
1209}
1210
1211fn prepare_hover_actions(
1212 snap: &GlobalStateSnapshot,
1213 file_id: FileId,
1214 actions: &[HoverAction],
1215) -> Vec<lsp_ext::CommandLinkGroup> {
1216 if snap.config.hover.none() || !snap.config.client_caps.hover_actions {
1217 return Vec::new();
1218 }
1201 1219
1202 let mut min_features = vec![]; 1220 actions
1203 collect_minimal_features_needed(&cfg_expr, &mut min_features); 1221 .iter()
1204 assert!(min_features.is_empty()); 1222 .filter_map(|it| match it {
1223 HoverAction::Implementaion(position) => show_impl_command_link(snap, position),
1224 HoverAction::Runnable(r) => to_runnable_action(snap, file_id, r.clone()),
1225 })
1226 .collect()
1227}
1228
1229fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) -> bool {
1230 match runnable.kind {
1231 RunnableKind::Bin => {
1232 // Do not suggest binary run on other target than binary
1233 match &cargo_spec {
1234 Some(spec) => spec.target_kind != TargetKind::Bin,
1235 None => true,
1236 }
1237 }
1238 _ => false,
1205 } 1239 }
1206} 1240}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 2fbbb4e63..710df1fbd 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -3,13 +3,16 @@ use ra_db::{FileId, FileRange};
3use ra_ide::{ 3use ra_ide::{
4 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, 4 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind,
5 FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, 5 FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel,
6 InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, 6 InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess,
7 SourceChange, SourceFileEdit, TextEdit, 7 ResolvedAssist, Runnable, Severity, SourceChange, SourceFileEdit, TextEdit,
8}; 8};
9use ra_syntax::{SyntaxKind, TextRange, TextSize}; 9use ra_syntax::{SyntaxKind, TextRange, TextSize};
10use ra_vfs::LineEndings; 10use ra_vfs::LineEndings;
11 11
12use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; 12use crate::{
13 cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot, lsp_ext,
14 semantic_tokens, Result,
15};
13 16
14pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { 17pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
15 let line_col = line_index.line_col(offset); 18 let line_col = line_index.line_col(offset);
@@ -382,41 +385,44 @@ pub(crate) fn folding_range(
382 } 385 }
383} 386}
384 387
385pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result<lsp_types::Url> { 388pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> Result<lsp_types::Url> {
386 world.file_id_to_uri(file_id) 389 snap.file_id_to_uri(file_id)
387} 390}
388 391
389pub(crate) fn versioned_text_document_identifier( 392pub(crate) fn versioned_text_document_identifier(
390 world: &WorldSnapshot, 393 snap: &GlobalStateSnapshot,
391 file_id: FileId, 394 file_id: FileId,
392 version: Option<i64>, 395 version: Option<i64>,
393) -> Result<lsp_types::VersionedTextDocumentIdentifier> { 396) -> Result<lsp_types::VersionedTextDocumentIdentifier> {
394 let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(world, file_id)?, version }; 397 let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(snap, file_id)?, version };
395 Ok(res) 398 Ok(res)
396} 399}
397 400
398pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result<lsp_types::Location> { 401pub(crate) fn location(
399 let url = url(world, frange.file_id)?; 402 snap: &GlobalStateSnapshot,
400 let line_index = world.analysis().file_line_index(frange.file_id)?; 403 frange: FileRange,
404) -> Result<lsp_types::Location> {
405 let url = url(snap, frange.file_id)?;
406 let line_index = snap.analysis().file_line_index(frange.file_id)?;
401 let range = range(&line_index, frange.range); 407 let range = range(&line_index, frange.range);
402 let loc = lsp_types::Location::new(url, range); 408 let loc = lsp_types::Location::new(url, range);
403 Ok(loc) 409 Ok(loc)
404} 410}
405 411
406pub(crate) fn location_link( 412pub(crate) fn location_link(
407 world: &WorldSnapshot, 413 snap: &GlobalStateSnapshot,
408 src: Option<FileRange>, 414 src: Option<FileRange>,
409 target: NavigationTarget, 415 target: NavigationTarget,
410) -> Result<lsp_types::LocationLink> { 416) -> Result<lsp_types::LocationLink> {
411 let origin_selection_range = match src { 417 let origin_selection_range = match src {
412 Some(src) => { 418 Some(src) => {
413 let line_index = world.analysis().file_line_index(src.file_id)?; 419 let line_index = snap.analysis().file_line_index(src.file_id)?;
414 let range = range(&line_index, src.range); 420 let range = range(&line_index, src.range);
415 Some(range) 421 Some(range)
416 } 422 }
417 None => None, 423 None => None,
418 }; 424 };
419 let (target_uri, target_range, target_selection_range) = location_info(world, target)?; 425 let (target_uri, target_range, target_selection_range) = location_info(snap, target)?;
420 let res = lsp_types::LocationLink { 426 let res = lsp_types::LocationLink {
421 origin_selection_range, 427 origin_selection_range,
422 target_uri, 428 target_uri,
@@ -427,12 +433,12 @@ pub(crate) fn location_link(
427} 433}
428 434
429fn location_info( 435fn location_info(
430 world: &WorldSnapshot, 436 snap: &GlobalStateSnapshot,
431 target: NavigationTarget, 437 target: NavigationTarget,
432) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { 438) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> {
433 let line_index = world.analysis().file_line_index(target.file_id())?; 439 let line_index = snap.analysis().file_line_index(target.file_id())?;
434 440
435 let target_uri = url(world, target.file_id())?; 441 let target_uri = url(snap, target.file_id())?;
436 let target_range = range(&line_index, target.full_range()); 442 let target_range = range(&line_index, target.full_range());
437 let target_selection_range = 443 let target_selection_range =
438 target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range); 444 target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range);
@@ -440,14 +446,14 @@ fn location_info(
440} 446}
441 447
442pub(crate) fn goto_definition_response( 448pub(crate) fn goto_definition_response(
443 world: &WorldSnapshot, 449 snap: &GlobalStateSnapshot,
444 src: Option<FileRange>, 450 src: Option<FileRange>,
445 targets: Vec<NavigationTarget>, 451 targets: Vec<NavigationTarget>,
446) -> Result<lsp_types::GotoDefinitionResponse> { 452) -> Result<lsp_types::GotoDefinitionResponse> {
447 if world.config.client_caps.location_link { 453 if snap.config.client_caps.location_link {
448 let links = targets 454 let links = targets
449 .into_iter() 455 .into_iter()
450 .map(|nav| location_link(world, src, nav)) 456 .map(|nav| location_link(snap, src, nav))
451 .collect::<Result<Vec<_>>>()?; 457 .collect::<Result<Vec<_>>>()?;
452 Ok(links.into()) 458 Ok(links.into())
453 } else { 459 } else {
@@ -455,7 +461,7 @@ pub(crate) fn goto_definition_response(
455 .into_iter() 461 .into_iter()
456 .map(|nav| { 462 .map(|nav| {
457 location( 463 location(
458 world, 464 snap,
459 FileRange { 465 FileRange {
460 file_id: nav.file_id(), 466 file_id: nav.file_id(),
461 range: nav.focus_range().unwrap_or(nav.range()), 467 range: nav.focus_range().unwrap_or(nav.range()),
@@ -468,13 +474,13 @@ pub(crate) fn goto_definition_response(
468} 474}
469 475
470pub(crate) fn snippet_text_document_edit( 476pub(crate) fn snippet_text_document_edit(
471 world: &WorldSnapshot, 477 snap: &GlobalStateSnapshot,
472 is_snippet: bool, 478 is_snippet: bool,
473 source_file_edit: SourceFileEdit, 479 source_file_edit: SourceFileEdit,
474) -> Result<lsp_ext::SnippetTextDocumentEdit> { 480) -> Result<lsp_ext::SnippetTextDocumentEdit> {
475 let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; 481 let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id, None)?;
476 let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; 482 let line_index = snap.analysis().file_line_index(source_file_edit.file_id)?;
477 let line_endings = world.file_line_endings(source_file_edit.file_id); 483 let line_endings = snap.file_line_endings(source_file_edit.file_id);
478 let edits = source_file_edit 484 let edits = source_file_edit
479 .edit 485 .edit
480 .into_iter() 486 .into_iter()
@@ -484,17 +490,17 @@ pub(crate) fn snippet_text_document_edit(
484} 490}
485 491
486pub(crate) fn resource_op( 492pub(crate) fn resource_op(
487 world: &WorldSnapshot, 493 snap: &GlobalStateSnapshot,
488 file_system_edit: FileSystemEdit, 494 file_system_edit: FileSystemEdit,
489) -> Result<lsp_types::ResourceOp> { 495) -> Result<lsp_types::ResourceOp> {
490 let res = match file_system_edit { 496 let res = match file_system_edit {
491 FileSystemEdit::CreateFile { source_root, path } => { 497 FileSystemEdit::CreateFile { source_root, path } => {
492 let uri = world.path_to_uri(source_root, &path)?; 498 let uri = snap.path_to_uri(source_root, &path)?;
493 lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) 499 lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None })
494 } 500 }
495 FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { 501 FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => {
496 let old_uri = world.file_id_to_uri(src)?; 502 let old_uri = snap.file_id_to_uri(src)?;
497 let new_uri = world.path_to_uri(dst_source_root, &dst_path)?; 503 let new_uri = snap.path_to_uri(dst_source_root, &dst_path)?;
498 lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) 504 lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None })
499 } 505 }
500 }; 506 };
@@ -502,16 +508,16 @@ pub(crate) fn resource_op(
502} 508}
503 509
504pub(crate) fn snippet_workspace_edit( 510pub(crate) fn snippet_workspace_edit(
505 world: &WorldSnapshot, 511 snap: &GlobalStateSnapshot,
506 source_change: SourceChange, 512 source_change: SourceChange,
507) -> Result<lsp_ext::SnippetWorkspaceEdit> { 513) -> Result<lsp_ext::SnippetWorkspaceEdit> {
508 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); 514 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
509 for op in source_change.file_system_edits { 515 for op in source_change.file_system_edits {
510 let op = resource_op(&world, op)?; 516 let op = resource_op(&snap, op)?;
511 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op)); 517 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op));
512 } 518 }
513 for edit in source_change.source_file_edits { 519 for edit in source_change.source_file_edits {
514 let edit = snippet_text_document_edit(&world, source_change.is_snippet, edit)?; 520 let edit = snippet_text_document_edit(&snap, source_change.is_snippet, edit)?;
515 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); 521 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
516 } 522 }
517 let workspace_edit = 523 let workspace_edit =
@@ -520,11 +526,11 @@ pub(crate) fn snippet_workspace_edit(
520} 526}
521 527
522pub(crate) fn workspace_edit( 528pub(crate) fn workspace_edit(
523 world: &WorldSnapshot, 529 snap: &GlobalStateSnapshot,
524 source_change: SourceChange, 530 source_change: SourceChange,
525) -> Result<lsp_types::WorkspaceEdit> { 531) -> Result<lsp_types::WorkspaceEdit> {
526 assert!(!source_change.is_snippet); 532 assert!(!source_change.is_snippet);
527 snippet_workspace_edit(world, source_change).map(|it| it.into()) 533 snippet_workspace_edit(snap, source_change).map(|it| it.into())
528} 534}
529 535
530impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { 536impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
@@ -563,13 +569,13 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
563} 569}
564 570
565pub fn call_hierarchy_item( 571pub fn call_hierarchy_item(
566 world: &WorldSnapshot, 572 snap: &GlobalStateSnapshot,
567 target: NavigationTarget, 573 target: NavigationTarget,
568) -> Result<lsp_types::CallHierarchyItem> { 574) -> Result<lsp_types::CallHierarchyItem> {
569 let name = target.name().to_string(); 575 let name = target.name().to_string();
570 let detail = target.description().map(|it| it.to_string()); 576 let detail = target.description().map(|it| it.to_string());
571 let kind = symbol_kind(target.kind()); 577 let kind = symbol_kind(target.kind());
572 let (uri, range, selection_range) = location_info(world, target)?; 578 let (uri, range, selection_range) = location_info(snap, target)?;
573 Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range }) 579 Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range })
574} 580}
575 581
@@ -617,13 +623,56 @@ fn main() <fold>{
617 } 623 }
618} 624}
619 625
620pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { 626pub(crate) fn unresolved_code_action(
627 snap: &GlobalStateSnapshot,
628 assist: Assist,
629 index: usize,
630) -> Result<lsp_ext::CodeAction> {
621 let res = lsp_ext::CodeAction { 631 let res = lsp_ext::CodeAction {
622 title: assist.label, 632 title: assist.label,
623 group: if world.config.client_caps.code_action_group { assist.group_label } else { None }, 633 id: Some(format!("{}:{}", assist.id.0.to_owned(), index.to_string())),
634 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0),
624 kind: Some(String::new()), 635 kind: Some(String::new()),
625 edit: Some(snippet_workspace_edit(world, assist.source_change)?), 636 edit: None,
626 command: None, 637 command: None,
627 }; 638 };
628 Ok(res) 639 Ok(res)
629} 640}
641
642pub(crate) fn resolved_code_action(
643 snap: &GlobalStateSnapshot,
644 assist: ResolvedAssist,
645) -> Result<lsp_ext::CodeAction> {
646 let change = assist.source_change;
647 unresolved_code_action(snap, assist.assist, 0).and_then(|it| {
648 Ok(lsp_ext::CodeAction {
649 id: None,
650 edit: Some(snippet_workspace_edit(snap, change)?),
651 ..it
652 })
653 })
654}
655
656pub(crate) fn runnable(
657 snap: &GlobalStateSnapshot,
658 file_id: FileId,
659 runnable: Runnable,
660) -> Result<lsp_ext::Runnable> {
661 let spec = CargoTargetSpec::for_file(snap, file_id)?;
662 let target = spec.as_ref().map(|s| s.target.clone());
663 let (cargo_args, executable_args) =
664 CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?;
665 let label = runnable.label(target);
666 let location = location_link(snap, None, runnable.nav)?;
667
668 Ok(lsp_ext::Runnable {
669 label,
670 location: Some(location),
671 kind: lsp_ext::RunnableKind::Cargo,
672 args: lsp_ext::CargoRunnable {
673 workspace_root: snap.workspace_root_for(file_id).map(|root| root.to_owned()),
674 cargo_args,
675 executable_args,
676 },
677 })
678}
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 405ddb362..0e2a83c6a 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -4,9 +4,7 @@ use std::{collections::HashMap, path::PathBuf, time::Instant};
4 4
5use lsp_types::{ 5use lsp_types::{
6 notification::DidOpenTextDocument, 6 notification::DidOpenTextDocument,
7 request::{ 7 request::{CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest},
8 CodeActionRequest, Completion, Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest,
9 },
10 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, 8 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
11 DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams, 9 DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams,
12 PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, 10 PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams,
@@ -59,52 +57,6 @@ use std::collections::Spam;
59} 57}
60 58
61#[test] 59#[test]
62fn test_runnables_no_project() {
63 if skip_slow_tests() {
64 return;
65 }
66
67 let server = project(
68 r"
69//- lib.rs
70#[test]
71fn foo() {
72}
73",
74 );
75 server.wait_until_workspace_is_loaded();
76 server.request::<Runnables>(
77 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
78 json!([
79 {
80 "args": [ "test" ],
81 "extraArgs": [ "foo", "--nocapture" ],
82 "bin": "cargo",
83 "env": { "RUST_BACKTRACE": "short" },
84 "cwd": null,
85 "label": "test foo",
86 "range": {
87 "end": { "character": 1, "line": 2 },
88 "start": { "character": 0, "line": 0 }
89 }
90 },
91 {
92 "args": ["check", "--workspace"],
93 "extraArgs": [],
94 "bin": "cargo",
95 "env": {},
96 "cwd": null,
97 "label": "cargo check --workspace",
98 "range": {
99 "end": { "character": 0, "line": 0 },
100 "start": { "character": 0, "line": 0 }
101 }
102 }
103 ]),
104 );
105}
106
107#[test]
108fn test_runnables_project() { 60fn test_runnables_project() {
109 if skip_slow_tests() { 61 if skip_slow_tests() {
110 return; 62 return;
@@ -138,42 +90,44 @@ fn main() {}
138 server.request::<Runnables>( 90 server.request::<Runnables>(
139 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, 91 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
140 json!([ 92 json!([
141 { 93 {
142 "args": [ "test", "--package", "foo", "--test", "spam" ], 94 "args": {
143 "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], 95 "cargoArgs": ["test", "--package", "foo", "--test", "spam"],
144 "bin": "cargo", 96 "executableArgs": ["test_eggs", "--exact", "--nocapture"],
145 "env": { "RUST_BACKTRACE": "short" }, 97 "workspaceRoot": server.path().join("foo")
146 "label": "test test_eggs", 98 },
147 "range": { 99 "kind": "cargo",
100 "label": "test test_eggs",
101 "location": {
102 "targetRange": {
148 "end": { "character": 17, "line": 1 }, 103 "end": { "character": 17, "line": 1 },
149 "start": { "character": 0, "line": 0 } 104 "start": { "character": 0, "line": 0 }
150 }, 105 },
151 "cwd": server.path().join("foo") 106 "targetSelectionRange": {
152 }, 107 "end": { "character": 12, "line": 1 },
153 { 108 "start": { "character": 3, "line": 1 }
154 "args": [ "check", "--package", "foo" ],
155 "extraArgs": [],
156 "bin": "cargo",
157 "env": {},
158 "label": "cargo check -p foo",
159 "range": {
160 "end": { "character": 0, "line": 0 },
161 "start": { "character": 0, "line": 0 }
162 }, 109 },
163 "cwd": server.path().join("foo") 110 "targetUri": "file:///[..]/tests/spam.rs"
164 },
165 {
166 "args": [ "test", "--package", "foo" ],
167 "extraArgs": [],
168 "bin": "cargo",
169 "env": {},
170 "label": "cargo test -p foo",
171 "range": {
172 "end": { "character": 0, "line": 0 },
173 "start": { "character": 0, "line": 0 }
174 },
175 "cwd": server.path().join("foo")
176 } 111 }
112 },
113 {
114 "args": {
115 "cargoArgs": ["check", "--package", "foo"],
116 "executableArgs": [],
117 "workspaceRoot": server.path().join("foo")
118 },
119 "kind": "cargo",
120 "label": "cargo check -p foo"
121 },
122 {
123 "args": {
124 "cargoArgs": ["test", "--package", "foo"],
125 "executableArgs": [],
126 "workspaceRoot": server.path().join("foo")
127 },
128 "kind": "cargo",
129 "label": "cargo test -p foo"
130 }
177 ]), 131 ]),
178 ); 132 );
179} 133}
@@ -342,6 +296,7 @@ fn main() {}
342 } 296 }
343 ] 297 ]
344 }, 298 },
299 "kind": "quickfix",
345 "title": "Create module" 300 "title": "Create module"
346 }]), 301 }]),
347 ); 302 );
@@ -374,8 +329,7 @@ fn test_missing_module_code_action_in_json_project() {
374 "root_module": path.join("src/lib.rs"), 329 "root_module": path.join("src/lib.rs"),
375 "deps": [], 330 "deps": [],
376 "edition": "2015", 331 "edition": "2015",
377 "atom_cfgs": [], 332 "cfg": [ "cfg_atom_1", "feature=cfg_1"],
378 "key_value_cfgs": {}
379 } ] 333 } ]
380 }); 334 });
381 335
@@ -413,6 +367,7 @@ fn main() {{}}
413 } 367 }
414 ] 368 ]
415 }, 369 },
370 "kind": "quickfix",
416 "title": "Create module" 371 "title": "Create module"
417 }]), 372 }]),
418 ); 373 );
@@ -550,6 +505,10 @@ fn main() {
550 println!("cargo:rerun-if-changed=build.rs"); 505 println!("cargo:rerun-if-changed=build.rs");
551} 506}
552//- src/main.rs 507//- src/main.rs
508#[rustc_builtin_macro] macro_rules! include {}
509#[rustc_builtin_macro] macro_rules! concat {}
510#[rustc_builtin_macro] macro_rules! env {}
511
553include!(concat!(env!("OUT_DIR"), "/hello.rs")); 512include!(concat!(env!("OUT_DIR"), "/hello.rs"));
554 513
555#[cfg(atom_cfg)] 514#[cfg(atom_cfg)]
@@ -564,10 +523,8 @@ struct B;
564fn main() { 523fn main() {
565 let va = A; 524 let va = A;
566 let vb = B; 525 let vb = B;
567 message(); 526 let should_be_str = message();
568} 527}
569
570fn main() { message(); }
571"###, 528"###,
572 ) 529 )
573 .with_config(|config| { 530 .with_config(|config| {
@@ -575,54 +532,35 @@ fn main() { message(); }
575 }) 532 })
576 .server(); 533 .server();
577 server.wait_until_workspace_is_loaded(); 534 server.wait_until_workspace_is_loaded();
578 let res = server.send_request::<GotoDefinition>(GotoDefinitionParams { 535 let res = server.send_request::<HoverRequest>(HoverParams {
579 text_document_position_params: TextDocumentPositionParams::new( 536 text_document_position_params: TextDocumentPositionParams::new(
580 server.doc_id("src/main.rs"), 537 server.doc_id("src/main.rs"),
581 Position::new(14, 8), 538 Position::new(18, 10),
582 ), 539 ),
583 work_done_progress_params: Default::default(), 540 work_done_progress_params: Default::default(),
584 partial_result_params: Default::default(),
585 }); 541 });
586 assert!(format!("{}", res).contains("hello.rs")); 542 assert!(res.to_string().contains("&str"));
587 server.request::<GotoTypeDefinition>( 543 server.request::<GotoTypeDefinition>(
588 GotoDefinitionParams { 544 GotoDefinitionParams {
589 text_document_position_params: TextDocumentPositionParams::new( 545 text_document_position_params: TextDocumentPositionParams::new(
590 server.doc_id("src/main.rs"), 546 server.doc_id("src/main.rs"),
591 Position::new(12, 9), 547 Position::new(16, 9),
592 ), 548 ),
593 work_done_progress_params: Default::default(), 549 work_done_progress_params: Default::default(),
594 partial_result_params: Default::default(), 550 partial_result_params: Default::default(),
595 }, 551 },
596 json!([{ 552 json!([{
597 "originSelectionRange": { 553 "originSelectionRange": {
598 "end": { 554 "end": { "character": 10, "line": 16 },
599 "character": 10, 555 "start": { "character": 8, "line": 16 }
600 "line": 12
601 },
602 "start": {
603 "character": 8,
604 "line": 12
605 }
606 }, 556 },
607 "targetRange": { 557 "targetRange": {
608 "end": { 558 "end": { "character": 9, "line": 7 },
609 "character": 9, 559 "start": { "character": 0, "line": 6 }
610 "line": 3
611 },
612 "start": {
613 "character": 0,
614 "line": 2
615 }
616 }, 560 },
617 "targetSelectionRange": { 561 "targetSelectionRange": {
618 "end": { 562 "end": { "character": 8, "line": 7 },
619 "character": 8, 563 "start": { "character": 7, "line": 7 }
620 "line": 3
621 },
622 "start": {
623 "character": 7,
624 "line": 3
625 }
626 }, 564 },
627 "targetUri": "file:///[..]src/main.rs" 565 "targetUri": "file:///[..]src/main.rs"
628 }]), 566 }]),
@@ -631,41 +569,23 @@ fn main() { message(); }
631 GotoDefinitionParams { 569 GotoDefinitionParams {
632 text_document_position_params: TextDocumentPositionParams::new( 570 text_document_position_params: TextDocumentPositionParams::new(
633 server.doc_id("src/main.rs"), 571 server.doc_id("src/main.rs"),
634 Position::new(13, 9), 572 Position::new(17, 9),
635 ), 573 ),
636 work_done_progress_params: Default::default(), 574 work_done_progress_params: Default::default(),
637 partial_result_params: Default::default(), 575 partial_result_params: Default::default(),
638 }, 576 },
639 json!([{ 577 json!([{
640 "originSelectionRange": { 578 "originSelectionRange": {
641 "end": { 579 "end": { "character": 10, "line": 17 },
642 "character": 10, 580 "start": { "character": 8, "line": 17 }
643 "line": 13
644 },
645 "start": {
646 "character": 8,
647 "line":13
648 }
649 }, 581 },
650 "targetRange": { 582 "targetRange": {
651 "end": { 583 "end": { "character": 9, "line": 11 },
652 "character": 9, 584 "start": { "character": 0, "line":10 }
653 "line": 7
654 },
655 "start": {
656 "character": 0,
657 "line":6
658 }
659 }, 585 },
660 "targetSelectionRange": { 586 "targetSelectionRange": {
661 "end": { 587 "end": { "character": 8, "line": 11 },
662 "character": 8, 588 "start": { "character": 7, "line": 11 }
663 "line": 7
664 },
665 "start": {
666 "character": 7,
667 "line": 7
668 }
669 }, 589 },
670 "targetUri": "file:///[..]src/main.rs" 590 "targetUri": "file:///[..]src/main.rs"
671 }]), 591 }]),
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index 66a6f4d54..30d03b622 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -19,8 +19,9 @@ use serde_json::{to_string_pretty, Value};
19use tempfile::TempDir; 19use tempfile::TempDir;
20use test_utils::{find_mismatch, parse_fixture}; 20use test_utils::{find_mismatch, parse_fixture};
21 21
22use ra_project_model::ProjectManifest;
22use rust_analyzer::{ 23use rust_analyzer::{
23 config::{ClientCapsConfig, Config}, 24 config::{ClientCapsConfig, Config, LinkedProject},
24 main_loop, 25 main_loop,
25}; 26};
26 27
@@ -42,7 +43,7 @@ impl<'a> Project<'a> {
42 self 43 self
43 } 44 }
44 45
45 pub fn root(mut self, path: &str) -> Project<'a> { 46 pub(crate) fn root(mut self, path: &str) -> Project<'a> {
46 self.roots.push(path.into()); 47 self.roots.push(path.into());
47 self 48 self
48 } 49 }
@@ -74,7 +75,16 @@ impl<'a> Project<'a> {
74 paths.push((path, entry.text)); 75 paths.push((path, entry.text));
75 } 76 }
76 77
77 let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); 78 let mut roots =
79 self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect::<Vec<_>>();
80 if roots.is_empty() {
81 roots.push(tmp_dir.path().to_path_buf());
82 }
83 let linked_projects = roots
84 .into_iter()
85 .map(|it| ProjectManifest::discover_single(&it).unwrap())
86 .map(LinkedProject::from)
87 .collect::<Vec<_>>();
78 88
79 let mut config = Config { 89 let mut config = Config {
80 client_caps: ClientCapsConfig { 90 client_caps: ClientCapsConfig {
@@ -84,6 +94,7 @@ impl<'a> Project<'a> {
84 ..Default::default() 94 ..Default::default()
85 }, 95 },
86 with_sysroot: self.with_sysroot, 96 with_sysroot: self.with_sysroot,
97 linked_projects,
87 ..Config::default() 98 ..Config::default()
88 }; 99 };
89 100
@@ -91,7 +102,7 @@ impl<'a> Project<'a> {
91 f(&mut config) 102 f(&mut config)
92 } 103 }
93 104
94 Server::new(tmp_dir, config, roots, paths) 105 Server::new(tmp_dir, config, paths)
95 } 106 }
96} 107}
97 108
@@ -109,20 +120,12 @@ pub struct Server {
109} 120}
110 121
111impl Server { 122impl Server {
112 fn new( 123 fn new(dir: TempDir, config: Config, files: Vec<(PathBuf, String)>) -> Server {
113 dir: TempDir,
114 config: Config,
115 roots: Vec<PathBuf>,
116 files: Vec<(PathBuf, String)>,
117 ) -> Server {
118 let path = dir.path().to_path_buf();
119
120 let roots = if roots.is_empty() { vec![path] } else { roots };
121 let (connection, client) = Connection::memory(); 124 let (connection, client) = Connection::memory();
122 125
123 let _thread = jod_thread::Builder::new() 126 let _thread = jod_thread::Builder::new()
124 .name("test server".to_string()) 127 .name("test server".to_string())
125 .spawn(move || main_loop(roots, config, connection).unwrap()) 128 .spawn(move || main_loop(config, connection).unwrap())
126 .expect("failed to spawn a thread"); 129 .expect("failed to spawn a thread");
127 130
128 let res = 131 let res =
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 71a57fba2..c0356344c 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -124,3 +124,8 @@ pub fn replace(buf: &mut String, from: char, to: &str) {
124 // FIXME: do this in place. 124 // FIXME: do this in place.
125 *buf = buf.replace(from, to) 125 *buf = buf.replace(from, to)
126} 126}
127
128pub fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
129 let idx = haystack.find(delim)?;
130 Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
131}
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml
index 4d185b01c..8840bf36a 100644
--- a/crates/test_utils/Cargo.toml
+++ b/crates/test_utils/Cargo.toml
@@ -14,4 +14,5 @@ serde_json = "1.0.48"
14relative-path = "1.0.0" 14relative-path = "1.0.0"
15rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
16 16
17ra_cfg = { path = "../ra_cfg" } \ No newline at end of file 17ra_cfg = { path = "../ra_cfg" }
18stdx = { path = "../stdx" } \ No newline at end of file
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 1bd97215c..2141bfc20 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -15,6 +15,7 @@ use std::{
15}; 15};
16 16
17pub use ra_cfg::CfgOptions; 17pub use ra_cfg::CfgOptions;
18use stdx::split1;
18 19
19pub use relative_path::{RelativePath, RelativePathBuf}; 20pub use relative_path::{RelativePath, RelativePathBuf};
20pub use rustc_hash::FxHashMap; 21pub use rustc_hash::FxHashMap;
@@ -332,11 +333,6 @@ fn parse_meta(meta: &str) -> FixtureMeta {
332 FixtureMeta::File(FileMeta { path, crate_name: krate, deps, edition, cfg, env }) 333 FixtureMeta::File(FileMeta { path, crate_name: krate, deps, edition, cfg, env })
333} 334}
334 335
335fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
336 let idx = haystack.find(delim)?;
337 Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
338}
339
340/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines. 336/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines.
341/// This allows fixtures to start off in a different indentation, e.g. to align the first line with 337/// This allows fixtures to start off in a different indentation, e.g. to align the first line with
342/// the other lines visually: 338/// the other lines visually:
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 65cc9fc12..ef5ffbf59 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -30,7 +30,7 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0
30 30
31* [good-first-issue](https://github.com/rust-analyzer/rust-analyzer/labels/good%20first%20issue) 31* [good-first-issue](https://github.com/rust-analyzer/rust-analyzer/labels/good%20first%20issue)
32 are good issues to get into the project. 32 are good issues to get into the project.
33* [E-mentor](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-mentor) 33* [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions)
34 issues have links to the code in question and tests. 34 issues have links to the code in question and tests.
35* [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy), 35* [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy),
36 [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium), 36 [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium),
@@ -55,7 +55,7 @@ You can run `cargo xtask install-pre-commit-hook` to install git-hook to run rus
55All Rust code lives in the `crates` top-level directory, and is organized as a 55All Rust code lives in the `crates` top-level directory, and is organized as a
56single Cargo workspace. The `editors` top-level directory contains code for 56single Cargo workspace. The `editors` top-level directory contains code for
57integrating with editors. Currently, it contains the plugin for VS Code (in 57integrating with editors. Currently, it contains the plugin for VS Code (in
58typescript). The `docs` top-level directory contains both developer and user 58TypeScript). The `docs` top-level directory contains both developer and user
59documentation. 59documentation.
60 60
61We have some automation infra in Rust in the `xtask` package. It contains 61We have some automation infra in Rust in the `xtask` package. It contains
@@ -79,8 +79,8 @@ possible. There's **"Run Extension (Debug Build)"** launch configuration for thi
79In general, I use one of the following workflows for fixing bugs and 79In general, I use one of the following workflows for fixing bugs and
80implementing features. 80implementing features.
81 81
82If the problem concerns only internal parts of rust-analyzer (ie, I don't need 82If the problem concerns only internal parts of rust-analyzer (i.e. I don't need
83to touch `rust-analyzer` crate or typescript code), there is a unit-test for it. 83to touch `rust-analyzer` crate or TypeScript code), there is a unit-test for it.
84So, I use **Rust Analyzer: Run** action in VS Code to run this single test, and 84So, I use **Rust Analyzer: Run** action in VS Code to run this single test, and
85then just do printf-driven development/debugging. As a sanity check after I'm 85then just do printf-driven development/debugging. As a sanity check after I'm
86done, I use `cargo xtask install --server` and **Reload Window** action in VS 86done, I use `cargo xtask install --server` and **Reload Window** action in VS
@@ -117,6 +117,203 @@ Additionally, I use `cargo run --release -p rust-analyzer -- analysis-stats
117path/to/some/rust/crate` to run a batch analysis. This is primarily useful for 117path/to/some/rust/crate` to run a batch analysis. This is primarily useful for
118performance optimizations, or for bug minimization. 118performance optimizations, or for bug minimization.
119 119
120# Code Style & Review Process
121
122Our approach to "clean code" is two fold:
123
124* We generally don't block PRs on style changes.
125* At the same time, all code in rust-analyzer is constantly refactored.
126
127It is explicitly OK for reviewer to flag only some nits in the PR, and than send a follow up cleanup PR for things which are easier to explain by example, cc-ing the original author.
128Sending small cleanup PRs (like rename a single local variable) is encouraged.
129
130## Scale of Changes
131
132Everyone knows that it's better to send small & focused pull requests.
133The problem is, sometimes you *have* to, eg, rewrite the whole compiler, and that just doesn't fit into a set of isolated PRs.
134
135The main thing too keep an eye on is the boundaries between various components.
136There are three kinds of changes:
137
1381. Internals of a single component are changed.
139 Specifically, you don't change any `pub` items.
140 A good example here would be an addition of a new assist.
141
1422. API of a component is expanded.
143 Specifically, you add a new `pub` function which wasn't there before.
144 A good example here would be expansion of assist API, for example, to implement lazy assists or assists groups.
145
1463. A new dependency between components is introduced.
147 Specifically, you add a `pub use` reexport from another crate or you add a new line to `[dependencies]` section of `Cargo.toml`.
148 A good example here would be adding reference search capability to the assists crates.
149
150For the first group, the change is generally merged as long as:
151
152* it works for the happy case,
153* it has tests,
154* it doesn't panic for unhappy case.
155
156For the second group, the change would be subjected to quite a bit of scrutiny and iteration.
157The new API needs to be right (or at least easy to change later).
158The actual implementation doesn't matter that much.
159It's very important to minimize the amount of changed lines of code for changes of the second kind.
160Often, you start doing change of the first kind, only to realise that you need to elevate to a change of the second kind.
161In this case, we'll probably ask you to split API changes into a separate PR.
162
163Changes of the third group should be pretty rare, so we don't specify any specific process for them.
164That said, adding an innocent-looking `pub use` is a very simple way to break encapsulation, keep an eye on it!
165
166Note: if you enjoyed this abstract hand-waving about boundaries, you might appreciate
167https://www.tedinski.com/2018/02/06/system-boundaries.html
168
169## Order of Imports
170
171We separate import groups with blank lines
172
173```rust
174mod x;
175mod y;
176
177use std::{ ... }
178
179use crate_foo::{ ... }
180use crate_bar::{ ... }
181
182use crate::{}
183
184use super::{} // but prefer `use crate::`
185```
186
187## Import Style
188
189Items from `hir` and `ast` should be used qualified:
190
191```rust
192// Good
193use ra_syntax::ast;
194
195fn frobnicate(func: hir::Function, strukt: ast::StructDef) {}
196
197// Not as good
198use hir::Function;
199use ra_syntax::ast::StructDef;
200
201fn frobnicate(func: Function, strukt: StructDef) {}
202```
203
204Avoid local `use MyEnum::*` imports.
205
206Prefer `use crate::foo::bar` to `use super::bar`.
207
208## Order of Items
209
210Optimize for the reader who sees the file for the first time, and wants to get the general idea about what's going on.
211People read things from top to bottom, so place most important things first.
212
213Specifically, if all items except one are private, always put the non-private item on top.
214
215Put `struct`s and `enum`s first, functions and impls last.
216
217Do
218
219```rust
220// Good
221struct Foo {
222 bars: Vec<Bar>
223}
224
225struct Bar;
226```
227
228rather than
229
230```rust
231// Not as good
232struct Bar;
233
234struct Foo {
235 bars: Vec<Bar>
236}
237```
238
239## Documentation
240
241For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines.
242If the line is too long, you want to split the sentence in two :-)
243
244## Preconditions
245
246Function preconditions should generally be expressed in types and provided by the caller (rather than checked by callee):
247
248```rust
249// Good
250fn frbonicate(walrus: Walrus) {
251 ...
252}
253
254// Not as good
255fn frobnicate(walrus: Option<Walrus>) {
256 let walrus = match walrus {
257 Some(it) => it,
258 None => return,
259 };
260 ...
261}
262```
263
264## Commit Style
265
266We don't have specific rules around git history hygiene.
267Maintaining clean git history is encouraged, but not enforced.
268We use rebase workflow, it's OK to rewrite history during PR review process.
269
270Avoid @mentioning people in commit messages, as such messages create a lot of duplicate notification traffic during rebases.
271
272# Architecture Invariants
273
274This section tries to document high-level design constraints, which are not
275always obvious from the low-level code.
276
277## Incomplete syntax trees
278
279Syntax trees are by design incomplete and do not enforce well-formedness.
280If ast method returns an `Option`, it *can* be `None` at runtime, even if this is forbidden by the grammar.
281
282## LSP independence
283
284rust-analyzer is independent from LSP.
285It provides features for a hypothetical perfect Rust-specific IDE client.
286Internal representations are lowered to LSP in the `rust-analyzer` crate (the only crate which is allowed to use LSP types).
287
288## IDE/Compiler split
289
290There's a semi-hard split between "compiler" and "IDE", at the `ra_hir` crate.
291Compiler derives new facts about source code.
292It explicitly acknowledges that not all info is available (i.e. you can't look at types during name resolution).
293
294IDE assumes that all information is available at all times.
295
296IDE should use only types from `ra_hir`, and should not depend on the underling compiler types.
297`ra_hir` is a facade.
298
299## IDE API
300
301The main IDE crate (`ra_ide`) uses "Plain Old Data" for the API.
302Rather than talking in definitions and references, it talks in Strings and textual offsets.
303In general, API is centered around UI concerns -- the result of the call is what the user sees in the editor, and not what the compiler sees underneath.
304The results are 100% Rust specific though.
305
306## Parser Tests
307
308Test for parser (`ra_parser`) live in `ra_syntax` crate (see `test_data` direcotory).
309There are two kinds of tests:
310
311* Manually written test cases in `parser/ok` and `parser/err`
312* "Inline" tests in `parser/inline` (these are generated) from comments in `ra_parser` crate.
313
314The purpose of inline tests is not to achieve full coverage by test cases, but to explain to the reader of the code what each particular `if` and `match` is responsible for.
315If you are tempted to add a large inline test, it might be a good idea to leave only the simplest example in place, and move the test to a manual `parser/ok` test.
316
120# Logging 317# Logging
121 318
122Logging is done by both rust-analyzer and VS Code, so it might be tricky to 319Logging is done by both rust-analyzer and VS Code, so it might be tricky to
@@ -159,8 +356,8 @@ There's also two VS Code commands which might be of interest:
159 rust code that it refers to and the rust editor will also highlight the proper 356 rust code that it refers to and the rust editor will also highlight the proper
160 text range. 357 text range.
161 358
162 If you press <kbd>Ctrl</kbd> (i.e. trigger goto definition) in the inspected 359 If you trigger Go to Definition in the inspected Rust source file,
163 Rust source file the syntax tree read-only editor should scroll to and select the 360 the syntax tree read-only editor should scroll to and select the
164 appropriate syntax node token. 361 appropriate syntax node token.
165 362
166 ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png) 363 ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png)
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index c57a93f12..a0847dad3 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -97,6 +97,30 @@ Invoking code action at this position will yield two code actions for importing
97* Is a fixed two-level structure enough? 97* Is a fixed two-level structure enough?
98* Should we devise a general way to encode custom interaction protocols for GUI refactorings? 98* Should we devise a general way to encode custom interaction protocols for GUI refactorings?
99 99
100## Lazy assists with `ResolveCodeAction`
101
102**Issue:** https://github.com/microsoft/language-server-protocol/issues/787
103
104**Client Capability** `{ "resolveCodeAction": boolean }`
105
106If this capability is set, the assists will be computed lazily. Thus `CodeAction` returned from the server will only contain `id` but not `edit` or `command` fields. The only exclusion from the rule is the diagnostic edits.
107
108After the client got the id, it should then call `experimental/resolveCodeAction` command on the server and provide the following payload:
109
110```typescript
111interface ResolveCodeActionParams {
112 id: string;
113 codeActionParams: lc.CodeActionParams;
114}
115```
116
117As a result of the command call the client will get the respective workspace edit (`lc.WorkspaceEdit`).
118
119### Unresolved Questions
120
121* Apply smarter filtering for ids?
122* Upon `resolveCodeAction` command only call the assits which should be resolved and not all of them?
123
100## Parent Module 124## Parent Module
101 125
102**Issue:** https://github.com/microsoft/language-server-protocol/issues/1002 126**Issue:** https://github.com/microsoft/language-server-protocol/issues/1002
@@ -311,6 +335,50 @@ Moreover, it would be cool if editors didn't need to implement even basic langua
311 This is how `SelectionRange` request works. 335 This is how `SelectionRange` request works.
312* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs? 336* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
313 337
338## Runnables
339
340**Issue:** https://github.com/microsoft/language-server-protocol/issues/944
341
342**Server Capability:** `{ "runnables": { "kinds": string[] } }`
343
344This request is send from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`).
345
346**Method:** `experimental/runnables`
347
348**Request:**
349
350```typescript
351interface RunnablesParams {
352 textDocument: TextDocumentIdentifier;
353 /// If null, compute runnables for the whole file.
354 position?: Position;
355}
356```
357
358**Response:** `Runnable[]`
359
360```typescript
361interface Runnable {
362 label: string;
363 /// If this Runnable is associated with a specific function/module, etc, the location of this item
364 location?: LocationLink;
365 /// Running things is necessary technology specific, `kind` needs to be advertised via server capabilities,
366 // the type of `args` is specific to `kind`. The actual running is handled by the client.
367 kind: string;
368 args: any;
369}
370```
371
372rust-analyzer supports only one `kind`, `"cargo"`. The `args` for `"cargo"` look like this:
373
374```typescript
375{
376 workspaceRoot?: string;
377 cargoArgs: string[];
378 executableArgs: string[];
379}
380```
381
314## Analyzer Status 382## Analyzer Status
315 383
316**Method:** `rust-analyzer/analyzerStatus` 384**Method:** `rust-analyzer/analyzerStatus`
@@ -400,38 +468,40 @@ interface InlayHint {
400} 468}
401``` 469```
402 470
403## Runnables 471## Hover Actions
404
405**Method:** `rust-analyzer/runnables`
406 472
407This request is send from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`). 473**Client Capability:** `{ "hoverActions": boolean }`
408Note that we plan to move this request to `experimental/runnables`, as it is not really Rust-specific, but the current API is not necessary the right one.
409Upstream issue: https://github.com/microsoft/language-server-protocol/issues/944
410 474
411**Request:** 475If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`:
412 476
413```typescript 477```typescript
414interface RunnablesParams { 478interface Hover {
415 textDocument: TextDocumentIdentifier; 479 ...
416 /// If null, compute runnables for the whole file. 480 actions?: CommandLinkGroup[];
417 position?: Position;
418} 481}
419```
420 482
421**Response:** `Runnable[]` 483interface CommandLink extends Command {
484 /**
485 * A tooltip for the command, when represented in the UI.
486 */
487 tooltip?: string;
488}
422 489
423```typescript 490interface CommandLinkGroup {
424interface Runnable { 491 title?: string;
425 /// The range this runnable is applicable for. 492 commands: CommandLink[];
426 range: lc.Range;
427 /// The label to show in the UI.
428 label: string;
429 /// The following fields describe a process to spawn.
430 bin: string;
431 args: string[];
432 /// Args for cargo after `--`.
433 extraArgs: string[];
434 env: { [key: string]: string };
435 cwd: string | null;
436} 493}
437``` 494```
495
496Such actions on the client side are appended to a hover bottom as command links:
497```
498 +-----------------------------+
499 | Hover content |
500 | |
501 +-----------------------------+
502 | _Action1_ | _Action2_ | <- first group, no TITLE
503 +-----------------------------+
504 | TITLE _Action1_ | _Action2_ | <- second group
505 +-----------------------------+
506 ...
507``` \ No newline at end of file
diff --git a/docs/user/generated_assists.adoc b/docs/user/generated_assists.adoc
deleted file mode 100644
index 4d2fb31d4..000000000
--- a/docs/user/generated_assists.adoc
+++ /dev/null
@@ -1,1015 +0,0 @@
1[discrete]
2=== `add_custom_impl`
3**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_custom_impl.rs#L14[add_custom_impl.rs]
4
5Adds impl block for derived trait.
6
7.Before
8```rust
9#[derive(Deb┃ug, Display)]
10struct S;
11```
12
13.After
14```rust
15#[derive(Display)]
16struct S;
17
18impl Debug for S {
19 $0
20}
21```
22
23
24[discrete]
25=== `add_derive`
26**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_derive.rs#L9[add_derive.rs]
27
28Adds a new `#[derive()]` clause to a struct or enum.
29
30.Before
31```rust
32struct Point {
33 x: u32,
34 y: u32,┃
35}
36```
37
38.After
39```rust
40#[derive($0)]
41struct Point {
42 x: u32,
43 y: u32,
44}
45```
46
47
48[discrete]
49=== `add_explicit_type`
50**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_explicit_type.rs#L9[add_explicit_type.rs]
51
52Specify type for a let binding.
53
54.Before
55```rust
56fn main() {
57 let x┃ = 92;
58}
59```
60
61.After
62```rust
63fn main() {
64 let x: i32 = 92;
65}
66```
67
68
69[discrete]
70=== `add_from_impl_for_enum`
71**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs#L7[add_from_impl_for_enum.rs]
72
73Adds a From impl for an enum variant with one tuple field.
74
75.Before
76```rust
77enum A { ┃One(u32) }
78```
79
80.After
81```rust
82enum A { One(u32) }
83
84impl From<u32> for A {
85 fn from(v: u32) -> Self {
86 A::One(v)
87 }
88}
89```
90
91
92[discrete]
93=== `add_function`
94**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_function.rs#L19[add_function.rs]
95
96Adds a stub function with a signature matching the function under the cursor.
97
98.Before
99```rust
100struct Baz;
101fn baz() -> Baz { Baz }
102fn foo() {
103 bar┃("", baz());
104}
105
106```
107
108.After
109```rust
110struct Baz;
111fn baz() -> Baz { Baz }
112fn foo() {
113 bar("", baz());
114}
115
116fn bar(arg: &str, baz: Baz) {
117 ${0:todo!()}
118}
119
120```
121
122
123[discrete]
124=== `add_hash`
125**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L65[raw_string.rs]
126
127Adds a hash to a raw string literal.
128
129.Before
130```rust
131fn main() {
132 r#"Hello,┃ World!"#;
133}
134```
135
136.After
137```rust
138fn main() {
139 r##"Hello, World!"##;
140}
141```
142
143
144[discrete]
145=== `add_impl`
146**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_impl.rs#L6[add_impl.rs]
147
148Adds a new inherent impl for a type.
149
150.Before
151```rust
152struct Ctx<T: Clone> {
153 data: T,┃
154}
155```
156
157.After
158```rust
159struct Ctx<T: Clone> {
160 data: T,
161}
162
163impl<T: Clone> Ctx<T> {
164 $0
165}
166```
167
168
169[discrete]
170=== `add_impl_default_members`
171**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_missing_impl_members.rs#L64[add_missing_impl_members.rs]
172
173Adds scaffold for overriding default impl members.
174
175.Before
176```rust
177trait Trait {
178 Type X;
179 fn foo(&self);
180 fn bar(&self) {}
181}
182
183impl Trait for () {
184 Type X = ();
185 fn foo(&self) {}┃
186
187}
188```
189
190.After
191```rust
192trait Trait {
193 Type X;
194 fn foo(&self);
195 fn bar(&self) {}
196}
197
198impl Trait for () {
199 Type X = ();
200 fn foo(&self) {}
201 $0fn bar(&self) {}
202
203}
204```
205
206
207[discrete]
208=== `add_impl_missing_members`
209**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_missing_impl_members.rs#L24[add_missing_impl_members.rs]
210
211Adds scaffold for required impl members.
212
213.Before
214```rust
215trait Trait<T> {
216 Type X;
217 fn foo(&self) -> T;
218 fn bar(&self) {}
219}
220
221impl Trait<u32> for () {┃
222
223}
224```
225
226.After
227```rust
228trait Trait<T> {
229 Type X;
230 fn foo(&self) -> T;
231 fn bar(&self) {}
232}
233
234impl Trait<u32> for () {
235 fn foo(&self) -> u32 {
236 ${0:todo!()}
237 }
238
239}
240```
241
242
243[discrete]
244=== `add_new`
245**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_new.rs#L12[add_new.rs]
246
247Adds a new inherent impl for a type.
248
249.Before
250```rust
251struct Ctx<T: Clone> {
252 data: T,┃
253}
254```
255
256.After
257```rust
258struct Ctx<T: Clone> {
259 data: T,
260}
261
262impl<T: Clone> Ctx<T> {
263 fn $0new(data: T) -> Self { Self { data } }
264}
265
266```
267
268
269[discrete]
270=== `add_turbo_fish`
271**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_turbo_fish.rs#L10[add_turbo_fish.rs]
272
273Adds `::<_>` to a call of a generic method or function.
274
275.Before
276```rust
277fn make<T>() -> T { todo!() }
278fn main() {
279 let x = make┃();
280}
281```
282
283.After
284```rust
285fn make<T>() -> T { todo!() }
286fn main() {
287 let x = make::<${0:_}>();
288}
289```
290
291
292[discrete]
293=== `apply_demorgan`
294**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/apply_demorgan.rs#L5[apply_demorgan.rs]
295
296Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
297This transforms expressions of the form `!l || !r` into `!(l && r)`.
298This also works with `&&`. This assist can only be applied with the cursor
299on either `||` or `&&`, with both operands being a negation of some kind.
300This means something of the form `!x` or `x != y`.
301
302.Before
303```rust
304fn main() {
305 if x != 4 ||┃ !y {}
306}
307```
308
309.After
310```rust
311fn main() {
312 if !(x == 4 && y) {}
313}
314```
315
316
317[discrete]
318=== `auto_import`
319**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/auto_import.rs#L18[auto_import.rs]
320
321If the name is unresolved, provides all possible imports for it.
322
323.Before
324```rust
325fn main() {
326 let map = HashMap┃::new();
327}
328```
329
330.After
331```rust
332use std::collections::HashMap;
333
334fn main() {
335 let map = HashMap::new();
336}
337```
338
339
340[discrete]
341=== `change_return_type_to_result`
342**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/change_return_type_to_result.rs#L8[change_return_type_to_result.rs]
343
344Change the function's return type to Result.
345
346.Before
347```rust
348fn foo() -> i32┃ { 42i32 }
349```
350
351.After
352```rust
353fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
354```
355
356
357[discrete]
358=== `change_visibility`
359**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/change_visibility.rs#L14[change_visibility.rs]
360
361Adds or changes existing visibility specifier.
362
363.Before
364```rust
365┃fn frobnicate() {}
366```
367
368.After
369```rust
370pub(crate) fn frobnicate() {}
371```
372
373
374[discrete]
375=== `convert_to_guarded_return`
376**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/early_return.rs#L21[early_return.rs]
377
378Replace a large conditional with a guarded return.
379
380.Before
381```rust
382fn main() {
383 ┃if cond {
384 foo();
385 bar();
386 }
387}
388```
389
390.After
391```rust
392fn main() {
393 if !cond {
394 return;
395 }
396 foo();
397 bar();
398}
399```
400
401
402[discrete]
403=== `fill_match_arms`
404**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/fill_match_arms.rs#L14[fill_match_arms.rs]
405
406Adds missing clauses to a `match` expression.
407
408.Before
409```rust
410enum Action { Move { distance: u32 }, Stop }
411
412fn handle(action: Action) {
413 match action {
414
415 }
416}
417```
418
419.After
420```rust
421enum Action { Move { distance: u32 }, Stop }
422
423fn handle(action: Action) {
424 match action {
425 $0Action::Move { distance } => {}
426 Action::Stop => {}
427 }
428}
429```
430
431
432[discrete]
433=== `fix_visibility`
434**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/fix_visibility.rs#L13[fix_visibility.rs]
435
436Makes inaccessible item public.
437
438.Before
439```rust
440mod m {
441 fn frobnicate() {}
442}
443fn main() {
444 m::frobnicate┃() {}
445}
446```
447
448.After
449```rust
450mod m {
451 $0pub(crate) fn frobnicate() {}
452}
453fn main() {
454 m::frobnicate() {}
455}
456```
457
458
459[discrete]
460=== `flip_binexpr`
461**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_binexpr.rs#L5[flip_binexpr.rs]
462
463Flips operands of a binary expression.
464
465.Before
466```rust
467fn main() {
468 let _ = 90 +┃ 2;
469}
470```
471
472.After
473```rust
474fn main() {
475 let _ = 2 + 90;
476}
477```
478
479
480[discrete]
481=== `flip_comma`
482**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_comma.rs#L5[flip_comma.rs]
483
484Flips two comma-separated items.
485
486.Before
487```rust
488fn main() {
489 ((1, 2),┃ (3, 4));
490}
491```
492
493.After
494```rust
495fn main() {
496 ((3, 4), (1, 2));
497}
498```
499
500
501[discrete]
502=== `flip_trait_bound`
503**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_trait_bound.rs#L9[flip_trait_bound.rs]
504
505Flips two trait bounds.
506
507.Before
508```rust
509fn foo<T: Clone +┃ Copy>() { }
510```
511
512.After
513```rust
514fn foo<T: Copy + Clone>() { }
515```
516
517
518[discrete]
519=== `inline_local_variable`
520**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/inline_local_variable.rs#L13[inline_local_variable.rs]
521
522Inlines local variable.
523
524.Before
525```rust
526fn main() {
527 let x┃ = 1 + 2;
528 x * 4;
529}
530```
531
532.After
533```rust
534fn main() {
535 (1 + 2) * 4;
536}
537```
538
539
540[discrete]
541=== `introduce_named_lifetime`
542**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/introduce_named_lifetime.rs#L12[introduce_named_lifetime.rs]
543
544Change an anonymous lifetime to a named lifetime.
545
546.Before
547```rust
548impl Cursor<'_┃> {
549 fn node(self) -> &SyntaxNode {
550 match self {
551 Cursor::Replace(node) | Cursor::Before(node) => node,
552 }
553 }
554}
555```
556
557.After
558```rust
559impl<'a> Cursor<'a> {
560 fn node(self) -> &SyntaxNode {
561 match self {
562 Cursor::Replace(node) | Cursor::Before(node) => node,
563 }
564 }
565}
566```
567
568
569[discrete]
570=== `introduce_variable`
571**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/introduce_variable.rs#L14[introduce_variable.rs]
572
573Extracts subexpression into a variable.
574
575.Before
576```rust
577fn main() {
578 ┃(1 + 2)┃ * 4;
579}
580```
581
582.After
583```rust
584fn main() {
585 let $0var_name = (1 + 2);
586 var_name * 4;
587}
588```
589
590
591[discrete]
592=== `invert_if`
593**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/invert_if.rs#L12[invert_if.rs]
594
595Apply invert_if
596This transforms if expressions of the form `if !x {A} else {B}` into `if x {B} else {A}`
597This also works with `!=`. This assist can only be applied with the cursor
598on `if`.
599
600.Before
601```rust
602fn main() {
603 if┃ !y { A } else { B }
604}
605```
606
607.After
608```rust
609fn main() {
610 if y { B } else { A }
611}
612```
613
614
615[discrete]
616=== `make_raw_string`
617**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L10[raw_string.rs]
618
619Adds `r#` to a plain string literal.
620
621.Before
622```rust
623fn main() {
624 "Hello,┃ World!";
625}
626```
627
628.After
629```rust
630fn main() {
631 r#"Hello, World!"#;
632}
633```
634
635
636[discrete]
637=== `make_usual_string`
638**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L39[raw_string.rs]
639
640Turns a raw string into a plain string.
641
642.Before
643```rust
644fn main() {
645 r#"Hello,┃ "World!""#;
646}
647```
648
649.After
650```rust
651fn main() {
652 "Hello, \"World!\"";
653}
654```
655
656
657[discrete]
658=== `merge_imports`
659**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/merge_imports.rs#L14[merge_imports.rs]
660
661Merges two imports with a common prefix.
662
663.Before
664```rust
665use std::┃fmt::Formatter;
666use std::io;
667```
668
669.After
670```rust
671use std::{fmt::Formatter, io};
672```
673
674
675[discrete]
676=== `merge_match_arms`
677**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/merge_match_arms.rs#L11[merge_match_arms.rs]
678
679Merges identical match arms.
680
681.Before
682```rust
683enum Action { Move { distance: u32 }, Stop }
684
685fn handle(action: Action) {
686 match action {
687 ┃Action::Move(..) => foo(),
688 Action::Stop => foo(),
689 }
690}
691```
692
693.After
694```rust
695enum Action { Move { distance: u32 }, Stop }
696
697fn handle(action: Action) {
698 match action {
699 Action::Move(..) | Action::Stop => foo(),
700 }
701}
702```
703
704
705[discrete]
706=== `move_arm_cond_to_match_guard`
707**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_guard.rs#L56[move_guard.rs]
708
709Moves if expression from match arm body into a guard.
710
711.Before
712```rust
713enum Action { Move { distance: u32 }, Stop }
714
715fn handle(action: Action) {
716 match action {
717 Action::Move { distance } => ┃if distance > 10 { foo() },
718 _ => (),
719 }
720}
721```
722
723.After
724```rust
725enum Action { Move { distance: u32 }, Stop }
726
727fn handle(action: Action) {
728 match action {
729 Action::Move { distance } if distance > 10 => foo(),
730 _ => (),
731 }
732}
733```
734
735
736[discrete]
737=== `move_bounds_to_where_clause`
738**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_bounds.rs#L10[move_bounds.rs]
739
740Moves inline type bounds to a where clause.
741
742.Before
743```rust
744fn apply<T, U, ┃F: FnOnce(T) -> U>(f: F, x: T) -> U {
745 f(x)
746}
747```
748
749.After
750```rust
751fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U {
752 f(x)
753}
754```
755
756
757[discrete]
758=== `move_guard_to_arm_body`
759**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_guard.rs#L8[move_guard.rs]
760
761Moves match guard into match arm body.
762
763.Before
764```rust
765enum Action { Move { distance: u32 }, Stop }
766
767fn handle(action: Action) {
768 match action {
769 Action::Move { distance } ┃if distance > 10 => foo(),
770 _ => (),
771 }
772}
773```
774
775.After
776```rust
777enum Action { Move { distance: u32 }, Stop }
778
779fn handle(action: Action) {
780 match action {
781 Action::Move { distance } => if distance > 10 { foo() },
782 _ => (),
783 }
784}
785```
786
787
788[discrete]
789=== `remove_dbg`
790**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/remove_dbg.rs#L8[remove_dbg.rs]
791
792Removes `dbg!()` macro call.
793
794.Before
795```rust
796fn main() {
797 ┃dbg!(92);
798}
799```
800
801.After
802```rust
803fn main() {
804 92;
805}
806```
807
808
809[discrete]
810=== `remove_hash`
811**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L89[raw_string.rs]
812
813Removes a hash from a raw string literal.
814
815.Before
816```rust
817fn main() {
818 r#"Hello,┃ World!"#;
819}
820```
821
822.After
823```rust
824fn main() {
825 r"Hello, World!";
826}
827```
828
829
830[discrete]
831=== `remove_mut`
832**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/remove_mut.rs#L5[remove_mut.rs]
833
834Removes the `mut` keyword.
835
836.Before
837```rust
838impl Walrus {
839 fn feed(&mut┃ self, amount: u32) {}
840}
841```
842
843.After
844```rust
845impl Walrus {
846 fn feed(&self, amount: u32) {}
847}
848```
849
850
851[discrete]
852=== `reorder_fields`
853**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/reorder_fields.rs#L10[reorder_fields.rs]
854
855Reorder the fields of record literals and record patterns in the same order as in
856the definition.
857
858.Before
859```rust
860struct Foo {foo: i32, bar: i32};
861const test: Foo = ┃Foo {bar: 0, foo: 1}
862```
863
864.After
865```rust
866struct Foo {foo: i32, bar: i32};
867const test: Foo = Foo {foo: 1, bar: 0}
868```
869
870
871[discrete]
872=== `replace_if_let_with_match`
873**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_if_let_with_match.rs#L13[replace_if_let_with_match.rs]
874
875Replaces `if let` with an else branch with a `match` expression.
876
877.Before
878```rust
879enum Action { Move { distance: u32 }, Stop }
880
881fn handle(action: Action) {
882 ┃if let Action::Move { distance } = action {
883 foo(distance)
884 } else {
885 bar()
886 }
887}
888```
889
890.After
891```rust
892enum Action { Move { distance: u32 }, Stop }
893
894fn handle(action: Action) {
895 match action {
896 Action::Move { distance } => foo(distance),
897 _ => bar(),
898 }
899}
900```
901
902
903[discrete]
904=== `replace_let_with_if_let`
905**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_let_with_if_let.rs#L14[replace_let_with_if_let.rs]
906
907Replaces `let` with an `if-let`.
908
909.Before
910```rust
911
912fn main(action: Action) {
913 ┃let x = compute();
914}
915
916fn compute() -> Option<i32> { None }
917```
918
919.After
920```rust
921
922fn main(action: Action) {
923 if let Some(x) = compute() {
924 }
925}
926
927fn compute() -> Option<i32> { None }
928```
929
930
931[discrete]
932=== `replace_qualified_name_with_use`
933**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs#L6[replace_qualified_name_with_use.rs]
934
935Adds a use statement for a given fully-qualified name.
936
937.Before
938```rust
939fn process(map: std::collections::┃HashMap<String, String>) {}
940```
941
942.After
943```rust
944use std::collections::HashMap;
945
946fn process(map: HashMap<String, String>) {}
947```
948
949
950[discrete]
951=== `replace_unwrap_with_match`
952**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs#L17[replace_unwrap_with_match.rs]
953
954Replaces `unwrap` a `match` expression. Works for Result and Option.
955
956.Before
957```rust
958enum Result<T, E> { Ok(T), Err(E) }
959fn main() {
960 let x: Result<i32, i32> = Result::Ok(92);
961 let y = x.┃unwrap();
962}
963```
964
965.After
966```rust
967enum Result<T, E> { Ok(T), Err(E) }
968fn main() {
969 let x: Result<i32, i32> = Result::Ok(92);
970 let y = match x {
971 Ok(a) => a,
972 $0_ => unreachable!(),
973 };
974}
975```
976
977
978[discrete]
979=== `split_import`
980**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/split_import.rs#L7[split_import.rs]
981
982Wraps the tail of import into braces.
983
984.Before
985```rust
986use std::┃collections::HashMap;
987```
988
989.After
990```rust
991use std::{collections::HashMap};
992```
993
994
995[discrete]
996=== `unwrap_block`
997**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/unwrap_block.rs#L9[unwrap_block.rs]
998
999This assist removes if...else, for, while and loop control statements to just keep the body.
1000
1001.Before
1002```rust
1003fn foo() {
1004 if true {┃
1005 println!("foo");
1006 }
1007}
1008```
1009
1010.After
1011```rust
1012fn foo() {
1013 println!("foo");
1014}
1015```
diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc
index 56538ca90..23a071cf1 100644
--- a/docs/user/generated_features.adoc
+++ b/docs/user/generated_features.adoc
@@ -76,7 +76,7 @@ Navigates to the type of an identifier.
76 76
77 77
78=== Hover 78=== Hover
79**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs#L63[hover.rs] 79**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs#L106[hover.rs]
80 80
81Shows additional information, like type of an expression or documentation for definition when "focusing" code. 81Shows additional information, like type of an expression or documentation for definition when "focusing" code.
82Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. 82Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
@@ -177,7 +177,7 @@ braces, so it won't confuse generics with comparisons.
177 177
178 178
179=== On Typing Assists 179=== On Typing Assists
180**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs#L35[typing.rs] 180**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs#L37[typing.rs]
181 181
182Some features trigger on typing certain characters: 182Some features trigger on typing certain characters:
183 183
@@ -199,7 +199,7 @@ Navigates to the parent module of the current module.
199 199
200 200
201=== Run 201=== Run
202**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs#L45[runnables.rs] 202**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs#L81[runnables.rs]
203 203
204Shows a popup suggesting to run a test/benchmark/binary **at the current cursor 204Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
205location**. Super useful for repeatedly running just a single test. Do bind this 205location**. Super useful for repeatedly running just a single test. Do bind this
@@ -213,7 +213,7 @@ to a shortcut!
213 213
214 214
215=== Semantic Syntax Highlighting 215=== Semantic Syntax Highlighting
216**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs#L33[syntax_highlighting.rs] 216**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs#L34[syntax_highlighting.rs]
217 217
218rust-analyzer highlights the code semantically. 218rust-analyzer highlights the code semantically.
219For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait. 219For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
@@ -275,7 +275,7 @@ String::from((y + 5).foo(z))
275 275
276 276
277=== Workspace Symbol 277=== Workspace Symbol
278**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs#L113[symbol_index.rs] 278**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs#L122[symbol_index.rs]
279 279
280Uses fuzzy-search to find types, modules and functions by name across your 280Uses fuzzy-search to find types, modules and functions by name across your
281project and dependencies. This is **the** most useful feature, which improves code 281project and dependencies. This is **the** most useful feature, which improves code
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 202783fd9..ea714f49a 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -269,6 +269,57 @@ Gnome Builder currently has support for RLS, and there's no way to configure the
2691. Rename, symlink or copy the `rust-analyzer` binary to `rls` and place it somewhere Builder can find (in `PATH`, or under `~/.cargo/bin`). 2691. Rename, symlink or copy the `rust-analyzer` binary to `rls` and place it somewhere Builder can find (in `PATH`, or under `~/.cargo/bin`).
2702. Enable the Rust Builder plugin. 2702. Enable the Rust Builder plugin.
271 271
272== Non-Cargo Based Projects
273
274rust-analyzer does not require Cargo.
275However, if you use some other build system, you'll have to describe the structure of your project for rust-analyzer in the `rust-project.json` format:
276
277[source,TypeScript]
278----
279interface JsonProject {
280 /// The set of paths containing the crates for this project.
281 /// Any `Crate` must be nested inside some `root`.
282 roots: string[];
283 /// The set of crates comprising the current project.
284 /// Must include all transitive dependencies as well as sysroot crate (libstd, libcore and such).
285 crates: Crate[];
286}
287
288interface Crate {
289 /// Path to the root module of the crate.
290 root_module: string;
291 /// Edition of the crate.
292 edition: "2015" | "2018";
293 /// Dependencies
294 deps: Dep[];
295 /// The set of cfgs activated for a given crate, like `["unix", "feature=foo", "feature=bar"]`.
296 cfg: string[];
297
298 /// value of the OUT_DIR env variable.
299 out_dir?: string;
300 /// For proc-macro crates, path to compiles proc-macro (.so file).
301 proc_macro_dylib_path?: string;
302}
303
304interface Dep {
305 /// Index of a crate in the `crates` array.
306 crate: number,
307 /// Name as should appear in the (implicit) `extern crate name` declaration.
308 name: string,
309}
310----
311
312This format is provisional and subject to change.
313Specifically, the `roots` setup will be different eventually.
314
315There are tree ways to feed `rust-project.json` to rust-analyzer:
316
317* Place `rust-project.json` file at the root of the project, and rust-anlayzer will discover it.
318* Specify `"rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ]` in the settings (and make sure that your LSP client sends settings as a part of initialize request).
319* Specify `"rust-analyzer.linkedProjects": [ { "roots": [...], "crates": [...] }]` inline.
320
321See https://github.com/rust-analyzer/rust-project.json-example for a small example.
322
272== Features 323== Features
273 324
274include::./generated_features.adoc[] 325include::./generated_features.adoc[]
diff --git a/editors/code/package.json b/editors/code/package.json
index 75dbafc05..e2027970d 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -238,7 +238,7 @@
238 }, 238 },
239 "rust-analyzer.cargo.allFeatures": { 239 "rust-analyzer.cargo.allFeatures": {
240 "type": "boolean", 240 "type": "boolean",
241 "default": true, 241 "default": false,
242 "description": "Activate all available features" 242 "description": "Activate all available features"
243 }, 243 },
244 "rust-analyzer.cargo.features": { 244 "rust-analyzer.cargo.features": {
@@ -318,9 +318,23 @@
318 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)" 318 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)"
319 }, 319 },
320 "rust-analyzer.checkOnSave.allFeatures": { 320 "rust-analyzer.checkOnSave.allFeatures": {
321 "type": "boolean", 321 "type": [
322 "default": true, 322 "null",
323 "markdownDescription": "Check with all features (will be passed as `--all-features`)" 323 "boolean"
324 ],
325 "default": null,
326 "markdownDescription": "Check with all features (will be passed as `--all-features`). Defaults to `rust-analyzer.cargo.allFeatures`."
327 },
328 "rust-analyzer.checkOnSave.features": {
329 "type": [
330 "null",
331 "array"
332 ],
333 "items": {
334 "type": "string"
335 },
336 "default": null,
337 "description": "List of features to activate. Defaults to `rust-analyzer.cargo.features`."
324 }, 338 },
325 "rust-analyzer.inlayHints.enable": { 339 "rust-analyzer.inlayHints.enable": {
326 "type": "boolean", 340 "type": "boolean",
@@ -462,17 +476,53 @@
462 "default": true 476 "default": true
463 }, 477 },
464 "rust-analyzer.lens.run": { 478 "rust-analyzer.lens.run": {
465 "markdownDescription": "Whether to show Run lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 479 "markdownDescription": "Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
466 "type": "boolean", 480 "type": "boolean",
467 "default": true 481 "default": true
468 }, 482 },
469 "rust-analyzer.lens.debug": { 483 "rust-analyzer.lens.debug": {
470 "markdownDescription": "Whether to show Debug lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 484 "markdownDescription": "Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
471 "type": "boolean", 485 "type": "boolean",
472 "default": true 486 "default": true
473 }, 487 },
474 "rust-analyzer.lens.implementations": { 488 "rust-analyzer.lens.implementations": {
475 "markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 489 "markdownDescription": "Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
490 "type": "boolean",
491 "default": true
492 },
493 "rust-analyzer.hoverActions.enable": {
494 "description": "Whether to show HoverActions in Rust files.",
495 "type": "boolean",
496 "default": true
497 },
498 "rust-analyzer.hoverActions.implementations": {
499 "markdownDescription": "Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.",
500 "type": "boolean",
501 "default": true
502 },
503 "rust-analyzer.hoverActions.run": {
504 "markdownDescription": "Whether to show `Run` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.",
505 "type": "boolean",
506 "default": true
507 },
508 "rust-analyzer.hoverActions.debug": {
509 "markdownDescription": "Whether to show `Debug` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.",
510 "type": "boolean",
511 "default": true
512 },
513 "rust-analyzer.linkedProjects": {
514 "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects. \nElements must be paths pointing to Cargo.toml, rust-project.json, or JSON objects in rust-project.json format",
515 "type": "array",
516 "items": {
517 "type": [
518 "string",
519 "object"
520 ]
521 },
522 "default": null
523 },
524 "rust-analyzer.withSysroot": {
525 "markdownDescription": "Internal config for debugging, disables loading of sysroot crates",
476 "type": "boolean", 526 "type": "boolean",
477 "default": true 527 "default": true
478 } 528 }
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index d64f9a3f9..65ad573d8 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -1,8 +1,25 @@
1import * as lc from 'vscode-languageclient'; 1import * as lc from 'vscode-languageclient';
2import * as vscode from 'vscode'; 2import * as vscode from 'vscode';
3import * as ra from '../src/lsp_ext';
4import * as Is from 'vscode-languageclient/lib/utils/is';
3 5
4import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; 6import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed';
5import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; 7import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed';
8import { assert } from './util';
9
10function renderCommand(cmd: ra.CommandLink) {
11 return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`;
12}
13
14function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString {
15 const text = actions.map(group =>
16 (group.title ? (group.title + " ") : "") + group.commands.map(renderCommand).join(' | ')
17 ).join('___');
18
19 const result = new vscode.MarkdownString(text);
20 result.isTrusted = true;
21 return result;
22}
6 23
7export function createClient(serverPath: string, cwd: string): lc.LanguageClient { 24export function createClient(serverPath: string, cwd: string): lc.LanguageClient {
8 // '.' Is the fallback if no folder is open 25 // '.' Is the fallback if no folder is open
@@ -32,6 +49,25 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
32 if (res === undefined) throw new Error('busy'); 49 if (res === undefined) throw new Error('busy');
33 return res; 50 return res;
34 }, 51 },
52 async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, _next: lc.ProvideHoverSignature) {
53 return client.sendRequest(lc.HoverRequest.type, client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), token).then(
54 (result) => {
55 const hover = client.protocol2CodeConverter.asHover(result);
56 if (hover) {
57 const actions = (<any>result).actions;
58 if (actions) {
59 hover.contents.push(renderHoverActions(actions));
60 }
61 }
62 return hover;
63 },
64 (error) => {
65 client.logFailedRequest(lc.HoverRequest.type, error);
66 return Promise.resolve(null);
67 });
68 },
69 // Using custom handling of CodeActions where each code action is resloved lazily
70 // That's why we are not waiting for any command or edits
35 async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { 71 async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) {
36 const params: lc.CodeActionParams = { 72 const params: lc.CodeActionParams = {
37 textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), 73 textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
@@ -43,32 +79,36 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
43 const result: (vscode.CodeAction | vscode.Command)[] = []; 79 const result: (vscode.CodeAction | vscode.Command)[] = [];
44 const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>(); 80 const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>();
45 for (const item of values) { 81 for (const item of values) {
82 // In our case we expect to get code edits only from diagnostics
46 if (lc.CodeAction.is(item)) { 83 if (lc.CodeAction.is(item)) {
84 assert(!item.command, "We don't expect to receive commands in CodeActions");
47 const action = client.protocol2CodeConverter.asCodeAction(item); 85 const action = client.protocol2CodeConverter.asCodeAction(item);
48 const group = actionGroup(item); 86 result.push(action);
49 if (isSnippetEdit(item) || group) { 87 continue;
50 action.command = { 88 }
51 command: "rust-analyzer.applySnippetWorkspaceEdit", 89 assert(isCodeActionWithoutEditsAndCommands(item), "We don't expect edits or commands here");
52 title: "", 90 const action = new vscode.CodeAction(item.title);
53 arguments: [action.edit], 91 const group = (item as any).group;
54 }; 92 const id = (item as any).id;
55 action.edit = undefined; 93 const resolveParams: ra.ResolveCodeActionParams = {
56 } 94 id: id,
57 95 codeActionParams: params
58 if (group) { 96 };
59 let entry = groups.get(group); 97 action.command = {
60 if (!entry) { 98 command: "rust-analyzer.resolveCodeAction",
61 entry = { index: result.length, items: [] }; 99 title: item.title,
62 groups.set(group, entry); 100 arguments: [resolveParams],
63 result.push(action); 101 };
64 } 102 if (group) {
65 entry.items.push(action); 103 let entry = groups.get(group);
66 } else { 104 if (!entry) {
105 entry = { index: result.length, items: [] };
106 groups.set(group, entry);
67 result.push(action); 107 result.push(action);
68 } 108 }
109 entry.items.push(action);
69 } else { 110 } else {
70 const command = client.protocol2CodeConverter.asCommand(item); 111 result.push(action);
71 result.push(command);
72 } 112 }
73 } 113 }
74 for (const [group, { index, items }] of groups) { 114 for (const [group, { index, items }] of groups) {
@@ -80,7 +120,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
80 command: "rust-analyzer.applyActionGroup", 120 command: "rust-analyzer.applyActionGroup",
81 title: "", 121 title: "",
82 arguments: [items.map((item) => { 122 arguments: [items.map((item) => {
83 return { label: item.title, edit: item.command!!.arguments!![0] }; 123 return { label: item.title, arguments: item.command!!.arguments!![0] };
84 })], 124 })],
85 }; 125 };
86 result[index] = action; 126 result[index] = action;
@@ -119,24 +159,18 @@ class ExperimentalFeatures implements lc.StaticFeature {
119 const caps: any = capabilities.experimental ?? {}; 159 const caps: any = capabilities.experimental ?? {};
120 caps.snippetTextEdit = true; 160 caps.snippetTextEdit = true;
121 caps.codeActionGroup = true; 161 caps.codeActionGroup = true;
162 caps.resolveCodeAction = true;
163 caps.hoverActions = true;
122 capabilities.experimental = caps; 164 capabilities.experimental = caps;
123 } 165 }
124 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { 166 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
125 } 167 }
126} 168}
127 169
128function isSnippetEdit(action: lc.CodeAction): boolean { 170function isCodeActionWithoutEditsAndCommands(value: any): boolean {
129 const documentChanges = action.edit?.documentChanges ?? []; 171 const candidate: lc.CodeAction = value;
130 for (const edit of documentChanges) { 172 return candidate && Is.string(candidate.title) &&
131 if (lc.TextDocumentEdit.is(edit)) { 173 (candidate.diagnostics === void 0 || Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) &&
132 if (edit.edits.some((indel) => (indel as any).insertTextFormat === lc.InsertTextFormat.Snippet)) { 174 (candidate.kind === void 0 || Is.string(candidate.kind)) &&
133 return true; 175 (candidate.edit === void 0 && candidate.command === void 0);
134 }
135 }
136 }
137 return false;
138}
139
140function actionGroup(action: lc.CodeAction): string | undefined {
141 return (action as any).group;
142} 176}
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 86302db37..3e9c3aa0e 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -8,6 +8,7 @@ import { spawnSync } from 'child_process';
8import { RunnableQuickPick, selectRunnable, createTask } from './run'; 8import { RunnableQuickPick, selectRunnable, createTask } from './run';
9import { AstInspector } from './ast_inspector'; 9import { AstInspector } from './ast_inspector';
10import { isRustDocument, sleep, isRustEditor } from './util'; 10import { isRustDocument, sleep, isRustEditor } from './util';
11import { startDebugSession, makeDebugConfig } from './debug';
11 12
12export * from './ast_inspector'; 13export * from './ast_inspector';
13export * from './run'; 14export * from './run';
@@ -197,20 +198,6 @@ export function toggleInlayHints(ctx: Ctx): Cmd {
197 }; 198 };
198} 199}
199 200
200export function run(ctx: Ctx): Cmd {
201 let prevRunnable: RunnableQuickPick | undefined;
202
203 return async () => {
204 const item = await selectRunnable(ctx, prevRunnable);
205 if (!item) return;
206
207 item.detail = 'rerun';
208 prevRunnable = item;
209 const task = createTask(item.runnable);
210 return await vscode.tasks.executeTask(task);
211 };
212}
213
214// Opens the virtual file that will show the syntax tree 201// Opens the virtual file that will show the syntax tree
215// 202//
216// The contents of the file come from the `TextDocumentContentProvider` 203// The contents of the file come from the `TextDocumentContentProvider`
@@ -356,10 +343,25 @@ export function showReferences(ctx: Ctx): Cmd {
356} 343}
357 344
358export function applyActionGroup(_ctx: Ctx): Cmd { 345export function applyActionGroup(_ctx: Ctx): Cmd {
359 return async (actions: { label: string; edit: vscode.WorkspaceEdit }[]) => { 346 return async (actions: { label: string; arguments: ra.ResolveCodeActionParams }[]) => {
360 const selectedAction = await vscode.window.showQuickPick(actions); 347 const selectedAction = await vscode.window.showQuickPick(actions);
361 if (!selectedAction) return; 348 if (!selectedAction) return;
362 await applySnippetWorkspaceEdit(selectedAction.edit); 349 vscode.commands.executeCommand(
350 'rust-analyzer.resolveCodeAction',
351 selectedAction.arguments,
352 );
353 };
354}
355
356export function resolveCodeAction(ctx: Ctx): Cmd {
357 const client = ctx.client;
358 return async (params: ra.ResolveCodeActionParams) => {
359 const item: lc.WorkspaceEdit = await client.sendRequest(ra.resolveCodeAction, params);
360 if (!item) {
361 return;
362 }
363 const edit = client.protocol2CodeConverter.asWorkspaceEdit(item);
364 await applySnippetWorkspaceEdit(edit);
363 }; 365 };
364} 366}
365 367
@@ -368,3 +370,62 @@ export function applySnippetWorkspaceEditCommand(_ctx: Ctx): Cmd {
368 await applySnippetWorkspaceEdit(edit); 370 await applySnippetWorkspaceEdit(edit);
369 }; 371 };
370} 372}
373
374export function run(ctx: Ctx): Cmd {
375 let prevRunnable: RunnableQuickPick | undefined;
376
377 return async () => {
378 const item = await selectRunnable(ctx, prevRunnable);
379 if (!item) return;
380
381 item.detail = 'rerun';
382 prevRunnable = item;
383 const task = createTask(item.runnable);
384 return await vscode.tasks.executeTask(task);
385 };
386}
387
388export function runSingle(ctx: Ctx): Cmd {
389 return async (runnable: ra.Runnable) => {
390 const editor = ctx.activeRustEditor;
391 if (!editor) return;
392
393 const task = createTask(runnable);
394 task.group = vscode.TaskGroup.Build;
395 task.presentationOptions = {
396 reveal: vscode.TaskRevealKind.Always,
397 panel: vscode.TaskPanelKind.Dedicated,
398 clear: true,
399 };
400
401 return vscode.tasks.executeTask(task);
402 };
403}
404
405export function debug(ctx: Ctx): Cmd {
406 let prevDebuggee: RunnableQuickPick | undefined;
407
408 return async () => {
409 const item = await selectRunnable(ctx, prevDebuggee, true);
410 if (!item) return;
411
412 item.detail = 'restart';
413 prevDebuggee = item;
414 return await startDebugSession(ctx, item.runnable);
415 };
416}
417
418export function debugSingle(ctx: Ctx): Cmd {
419 return async (config: ra.Runnable) => {
420 await startDebugSession(ctx, config);
421 };
422}
423
424export function newDebugConfig(ctx: Ctx): Cmd {
425 return async () => {
426 const item = await selectRunnable(ctx, undefined, true, false);
427 if (!item) return;
428
429 await makeDebugConfig(ctx, item.runnable);
430 };
431}
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index e8abf8284..d8f0037d4 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -16,10 +16,8 @@ export class Config {
16 "files", 16 "files",
17 "highlighting", 17 "highlighting",
18 "updates.channel", 18 "updates.channel",
19 "lens.enable", 19 "lens", // works as lens.*
20 "lens.run", 20 "hoverActions", // works as hoverActions.*
21 "lens.debug",
22 "lens.implementations",
23 ] 21 ]
24 .map(opt => `${this.rootSection}.${opt}`); 22 .map(opt => `${this.rootSection}.${opt}`);
25 23
@@ -132,4 +130,11 @@ export class Config {
132 implementations: this.get<boolean>("lens.implementations"), 130 implementations: this.get<boolean>("lens.implementations"),
133 }; 131 };
134 } 132 }
133
134 get hoverActions() {
135 return {
136 enable: this.get<boolean>("hoverActions.enable"),
137 implementations: this.get<boolean>("hoverActions.implementations"),
138 };
139 }
135} 140}
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
index 027504ecd..a0c9b3ab2 100644
--- a/editors/code/src/debug.ts
+++ b/editors/code/src/debug.ts
@@ -3,46 +3,59 @@ import * as vscode from 'vscode';
3import * as path from 'path'; 3import * as path from 'path';
4import * as ra from './lsp_ext'; 4import * as ra from './lsp_ext';
5 5
6import { Cargo } from './cargo'; 6import { Cargo } from './toolchain';
7import { Ctx } from "./ctx"; 7import { Ctx } from "./ctx";
8 8
9const debugOutput = vscode.window.createOutputChannel("Debug"); 9const debugOutput = vscode.window.createOutputChannel("Debug");
10type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>) => vscode.DebugConfiguration; 10type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>) => vscode.DebugConfiguration;
11 11
12function getLldbDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { 12export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<void> {
13 return { 13 const scope = ctx.activeRustEditor?.document.uri;
14 type: "lldb", 14 if (!scope) return;
15 request: "launch",
16 name: config.label,
17 program: executable,
18 args: config.extraArgs,
19 cwd: config.cwd,
20 sourceMap: sourceFileMap,
21 sourceLanguages: ["rust"]
22 };
23}
24 15
25function getCppvsDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { 16 const debugConfig = await getDebugConfiguration(ctx, runnable);
26 return { 17 if (!debugConfig) return;
27 type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg", 18
28 request: "launch", 19 const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope);
29 name: config.label, 20 const configurations = wsLaunchSection.get<any[]>("configurations") || [];
30 program: executable, 21
31 args: config.extraArgs, 22 const index = configurations.findIndex(c => c.name === debugConfig.name);
32 cwd: config.cwd, 23 if (index !== -1) {
33 sourceFileMap: sourceFileMap, 24 const answer = await vscode.window.showErrorMessage(`Launch configuration '${debugConfig.name}' already exists!`, 'Cancel', 'Update');
34 }; 25 if (answer === "Cancel") return;
26
27 configurations[index] = debugConfig;
28 } else {
29 configurations.push(debugConfig);
30 }
31
32 await wsLaunchSection.update("configurations", configurations);
35} 33}
36 34
37async function getDebugExecutable(config: ra.Runnable): Promise<string> { 35export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promise<boolean> {
38 const cargo = new Cargo(config.cwd || '.', debugOutput); 36 let debugConfig: vscode.DebugConfiguration | undefined = undefined;
39 const executable = await cargo.executableFromArgs(config.args); 37 let message = "";
40 38
41 // if we are here, there were no compilation errors. 39 const wsLaunchSection = vscode.workspace.getConfiguration("launch");
42 return executable; 40 const configurations = wsLaunchSection.get<any[]>("configurations") || [];
41
42 const index = configurations.findIndex(c => c.name === runnable.label);
43 if (-1 !== index) {
44 debugConfig = configurations[index];
45 message = " (from launch.json)";
46 debugOutput.clear();
47 } else {
48 debugConfig = await getDebugConfiguration(ctx, runnable);
49 }
50
51 if (!debugConfig) return false;
52
53 debugOutput.appendLine(`Launching debug configuration${message}:`);
54 debugOutput.appendLine(JSON.stringify(debugConfig, null, 2));
55 return vscode.debug.startDebugging(undefined, debugConfig);
43} 56}
44 57
45export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Promise<vscode.DebugConfiguration | undefined> { 58async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<vscode.DebugConfiguration | undefined> {
46 const editor = ctx.activeRustEditor; 59 const editor = ctx.activeRustEditor;
47 if (!editor) return; 60 if (!editor) return;
48 61
@@ -78,8 +91,8 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom
78 return path.normalize(p).replace(wsFolder, '${workspaceRoot}'); 91 return path.normalize(p).replace(wsFolder, '${workspaceRoot}');
79 } 92 }
80 93
81 const executable = await getDebugExecutable(config); 94 const executable = await getDebugExecutable(runnable);
82 const debugConfig = knownEngines[debugEngine.id](config, simplifyPath(executable), debugOptions.sourceFileMap); 95 const debugConfig = knownEngines[debugEngine.id](runnable, simplifyPath(executable), debugOptions.sourceFileMap);
83 if (debugConfig.type in debugOptions.engineSettings) { 96 if (debugConfig.type in debugOptions.engineSettings) {
84 const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; 97 const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type];
85 for (var key in settingsMap) { 98 for (var key in settingsMap) {
@@ -100,25 +113,35 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom
100 return debugConfig; 113 return debugConfig;
101} 114}
102 115
103export async function startDebugSession(ctx: Ctx, config: ra.Runnable): Promise<boolean> { 116async function getDebugExecutable(runnable: ra.Runnable): Promise<string> {
104 let debugConfig: vscode.DebugConfiguration | undefined = undefined; 117 const cargo = new Cargo(runnable.args.workspaceRoot || '.', debugOutput);
105 let message = ""; 118 const executable = await cargo.executableFromArgs(runnable.args.cargoArgs);
106
107 const wsLaunchSection = vscode.workspace.getConfiguration("launch");
108 const configurations = wsLaunchSection.get<any[]>("configurations") || [];
109 119
110 const index = configurations.findIndex(c => c.name === config.label); 120 // if we are here, there were no compilation errors.
111 if (-1 !== index) { 121 return executable;
112 debugConfig = configurations[index]; 122}
113 message = " (from launch.json)";
114 debugOutput.clear();
115 } else {
116 debugConfig = await getDebugConfiguration(ctx, config);
117 }
118 123
119 if (!debugConfig) return false; 124function getLldbDebugConfig(runnable: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration {
125 return {
126 type: "lldb",
127 request: "launch",
128 name: runnable.label,
129 program: executable,
130 args: runnable.args.executableArgs,
131 cwd: runnable.args.workspaceRoot,
132 sourceMap: sourceFileMap,
133 sourceLanguages: ["rust"]
134 };
135}
120 136
121 debugOutput.appendLine(`Launching debug configuration${message}:`); 137function getCppvsDebugConfig(runnable: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration {
122 debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); 138 return {
123 return vscode.debug.startDebugging(undefined, debugConfig); 139 type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg",
140 request: "launch",
141 name: runnable.label,
142 program: executable,
143 args: runnable.args.executableArgs,
144 cwd: runnable.args.workspaceRoot,
145 sourceFileMap: sourceFileMap,
146 };
124} 147}
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 4da12eb30..e16ea799c 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -33,6 +33,12 @@ export const matchingBrace = new lc.RequestType<MatchingBraceParams, lc.Position
33 33
34export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[], void>("experimental/parentModule"); 34export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[], void>("experimental/parentModule");
35 35
36export interface ResolveCodeActionParams {
37 id: string;
38 codeActionParams: lc.CodeActionParams;
39}
40export const resolveCodeAction = new lc.RequestType<ResolveCodeActionParams, lc.WorkspaceEdit, unknown>('experimental/resolveCodeAction');
41
36export interface JoinLinesParams { 42export interface JoinLinesParams {
37 textDocument: lc.TextDocumentIdentifier; 43 textDocument: lc.TextDocumentIdentifier;
38 ranges: lc.Range[]; 44 ranges: lc.Range[];
@@ -45,16 +51,18 @@ export interface RunnablesParams {
45 textDocument: lc.TextDocumentIdentifier; 51 textDocument: lc.TextDocumentIdentifier;
46 position: lc.Position | null; 52 position: lc.Position | null;
47} 53}
54
48export interface Runnable { 55export interface Runnable {
49 range: lc.Range;
50 label: string; 56 label: string;
51 bin: string; 57 location?: lc.LocationLink;
52 args: string[]; 58 kind: "cargo";
53 extraArgs: string[]; 59 args: {
54 env: { [key: string]: string }; 60 workspaceRoot?: string;
55 cwd: string | null; 61 cargoArgs: string[];
62 executableArgs: string[];
63 };
56} 64}
57export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("rust-analyzer/runnables"); 65export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("experimental/runnables");
58 66
59export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint; 67export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint;
60 68
@@ -82,3 +90,15 @@ export interface SsrParams {
82 parseOnly: boolean; 90 parseOnly: boolean;
83} 91}
84export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr'); 92export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr');
93
94export interface CommandLink extends lc.Command {
95 /**
96 * A tooltip for the command, when represented in the UI.
97 */
98 tooltip?: string;
99}
100
101export interface CommandLinkGroup {
102 title?: string;
103 commands: CommandLink[];
104}
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index b7337621c..a92c676fa 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -98,6 +98,7 @@ export async function activate(context: vscode.ExtensionContext) {
98 ctx.registerCommand('debugSingle', commands.debugSingle); 98 ctx.registerCommand('debugSingle', commands.debugSingle);
99 ctx.registerCommand('showReferences', commands.showReferences); 99 ctx.registerCommand('showReferences', commands.showReferences);
100 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); 100 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand);
101 ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction);
101 ctx.registerCommand('applyActionGroup', commands.applyActionGroup); 102 ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
102 103
103 ctx.pushCleanup(activateTaskProvider(workspaceFolder)); 104 ctx.pushCleanup(activateTaskProvider(workspaceFolder));
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts
index 8e1ba83ed..bb060cfe1 100644
--- a/editors/code/src/run.ts
+++ b/editors/code/src/run.ts
@@ -1,9 +1,10 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import * as ra from './lsp_ext'; 3import * as ra from './lsp_ext';
4import * as toolchain from "./toolchain";
4 5
5import { Ctx, Cmd } from './ctx'; 6import { Ctx } from './ctx';
6import { startDebugSession, getDebugConfiguration } from './debug'; 7import { makeDebugConfig } from './debug';
7 8
8const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; 9const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }];
9 10
@@ -64,7 +65,7 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick,
64 quickPick.onDidHide(() => close()), 65 quickPick.onDidHide(() => close()),
65 quickPick.onDidAccept(() => close(quickPick.selectedItems[0])), 66 quickPick.onDidAccept(() => close(quickPick.selectedItems[0])),
66 quickPick.onDidTriggerButton((_button) => { 67 quickPick.onDidTriggerButton((_button) => {
67 (async () => await makeDebugConfig(ctx, quickPick.activeItems[0]))(); 68 (async () => await makeDebugConfig(ctx, quickPick.activeItems[0].runnable))();
68 close(); 69 close();
69 }), 70 }),
70 quickPick.onDidChangeActive((active) => { 71 quickPick.onDidChangeActive((active) => {
@@ -83,74 +84,6 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick,
83 }); 84 });
84} 85}
85 86
86export function runSingle(ctx: Ctx): Cmd {
87 return async (runnable: ra.Runnable) => {
88 const editor = ctx.activeRustEditor;
89 if (!editor) return;
90
91 const task = createTask(runnable);
92 task.group = vscode.TaskGroup.Build;
93 task.presentationOptions = {
94 reveal: vscode.TaskRevealKind.Always,
95 panel: vscode.TaskPanelKind.Dedicated,
96 clear: true,
97 };
98
99 return vscode.tasks.executeTask(task);
100 };
101}
102
103export function debug(ctx: Ctx): Cmd {
104 let prevDebuggee: RunnableQuickPick | undefined;
105
106 return async () => {
107 const item = await selectRunnable(ctx, prevDebuggee, true);
108 if (!item) return;
109
110 item.detail = 'restart';
111 prevDebuggee = item;
112 return await startDebugSession(ctx, item.runnable);
113 };
114}
115
116export function debugSingle(ctx: Ctx): Cmd {
117 return async (config: ra.Runnable) => {
118 await startDebugSession(ctx, config);
119 };
120}
121
122async function makeDebugConfig(ctx: Ctx, item: RunnableQuickPick): Promise<void> {
123 const scope = ctx.activeRustEditor?.document.uri;
124 if (!scope) return;
125
126 const debugConfig = await getDebugConfiguration(ctx, item.runnable);
127 if (!debugConfig) return;
128
129 const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope);
130 const configurations = wsLaunchSection.get<any[]>("configurations") || [];
131
132 const index = configurations.findIndex(c => c.name === debugConfig.name);
133 if (index !== -1) {
134 const answer = await vscode.window.showErrorMessage(`Launch configuration '${debugConfig.name}' already exists!`, 'Cancel', 'Update');
135 if (answer === "Cancel") return;
136
137 configurations[index] = debugConfig;
138 } else {
139 configurations.push(debugConfig);
140 }
141
142 await wsLaunchSection.update("configurations", configurations);
143}
144
145export function newDebugConfig(ctx: Ctx): Cmd {
146 return async () => {
147 const item = await selectRunnable(ctx, undefined, true, false);
148 if (!item) return;
149
150 await makeDebugConfig(ctx, item);
151 };
152}
153
154export class RunnableQuickPick implements vscode.QuickPickItem { 87export class RunnableQuickPick implements vscode.QuickPickItem {
155 public label: string; 88 public label: string;
156 public description?: string | undefined; 89 public description?: string | undefined;
@@ -170,18 +103,27 @@ interface CargoTaskDefinition extends vscode.TaskDefinition {
170 env?: { [key: string]: string }; 103 env?: { [key: string]: string };
171} 104}
172 105
173export function createTask(spec: ra.Runnable): vscode.Task { 106export function createTask(runnable: ra.Runnable): vscode.Task {
174 const TASK_SOURCE = 'Rust'; 107 const TASK_SOURCE = 'Rust';
108
109 let command;
110 switch (runnable.kind) {
111 case "cargo": command = toolchain.getPathForExecutable("cargo");
112 }
113 const args = [...runnable.args.cargoArgs]; // should be a copy!
114 if (runnable.args.executableArgs.length > 0) {
115 args.push('--', ...runnable.args.executableArgs);
116 }
175 const definition: CargoTaskDefinition = { 117 const definition: CargoTaskDefinition = {
176 type: 'cargo', 118 type: 'cargo',
177 label: spec.label, 119 label: runnable.label,
178 command: spec.bin, 120 command,
179 args: spec.extraArgs ? [...spec.args, '--', ...spec.extraArgs] : spec.args, 121 args,
180 env: Object.assign({}, process.env, spec.env), 122 env: Object.assign({}, process.env as { [key: string]: string }, { "RUST_BACKTRACE": "short" }),
181 }; 123 };
182 124
183 const execOption: vscode.ShellExecutionOptions = { 125 const execOption: vscode.ShellExecutionOptions = {
184 cwd: spec.cwd || '.', 126 cwd: runnable.args.workspaceRoot || '.',
185 env: definition.env, 127 env: definition.env,
186 }; 128 };
187 const exec = new vscode.ShellExecution( 129 const exec = new vscode.ShellExecution(
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts
index 1366c76d6..9748824df 100644
--- a/editors/code/src/tasks.ts
+++ b/editors/code/src/tasks.ts
@@ -1,4 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as toolchain from "./toolchain";
2 3
3// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and 4// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
4// our configuration should be compatible with it so use the same key. 5// our configuration should be compatible with it so use the same key.
@@ -24,6 +25,8 @@ class CargoTaskProvider implements vscode.TaskProvider {
24 // set of tasks that always exist. These tasks cannot be removed in 25 // set of tasks that always exist. These tasks cannot be removed in
25 // tasks.json - only tweaked. 26 // tasks.json - only tweaked.
26 27
28 const cargoPath = toolchain.cargoPath();
29
27 return [ 30 return [
28 { command: 'build', group: vscode.TaskGroup.Build }, 31 { command: 'build', group: vscode.TaskGroup.Build },
29 { command: 'check', group: vscode.TaskGroup.Build }, 32 { command: 'check', group: vscode.TaskGroup.Build },
@@ -46,7 +49,7 @@ class CargoTaskProvider implements vscode.TaskProvider {
46 `cargo ${command}`, 49 `cargo ${command}`,
47 'rust', 50 'rust',
48 // What to do when this command is executed. 51 // What to do when this command is executed.
49 new vscode.ShellExecution('cargo', [command]), 52 new vscode.ShellExecution(cargoPath, [command]),
50 // Problem matchers. 53 // Problem matchers.
51 ['$rustc'], 54 ['$rustc'],
52 ); 55 );
@@ -80,4 +83,4 @@ class CargoTaskProvider implements vscode.TaskProvider {
80export function activateTaskProvider(target: vscode.WorkspaceFolder): vscode.Disposable { 83export function activateTaskProvider(target: vscode.WorkspaceFolder): vscode.Disposable {
81 const provider = new CargoTaskProvider(target); 84 const provider = new CargoTaskProvider(target);
82 return vscode.tasks.registerTaskProvider(TASK_TYPE, provider); 85 return vscode.tasks.registerTaskProvider(TASK_TYPE, provider);
83} \ No newline at end of file 86}
diff --git a/editors/code/src/cargo.ts b/editors/code/src/toolchain.ts
index a55b2f860..80a7915e9 100644
--- a/editors/code/src/cargo.ts
+++ b/editors/code/src/toolchain.ts
@@ -1,9 +1,10 @@
1import * as cp from 'child_process'; 1import * as cp from 'child_process';
2import * as os from 'os'; 2import * as os from 'os';
3import * as path from 'path'; 3import * as path from 'path';
4import * as fs from 'fs';
4import * as readline from 'readline'; 5import * as readline from 'readline';
5import { OutputChannel } from 'vscode'; 6import { OutputChannel } from 'vscode';
6import { isValidExecutable } from './util'; 7import { log, memoize } from './util';
7 8
8interface CompilationArtifact { 9interface CompilationArtifact {
9 fileName: string; 10 fileName: string;
@@ -17,33 +18,34 @@ export interface ArtifactSpec {
17 filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[]; 18 filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[];
18} 19}
19 20
20export function artifactSpec(args: readonly string[]): ArtifactSpec { 21export class Cargo {
21 const cargoArgs = [...args, "--message-format=json"]; 22 constructor(readonly rootFolder: string, readonly output: OutputChannel) { }
22 23
23 // arguments for a runnable from the quick pick should be updated. 24 // Made public for testing purposes
24 // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens 25 static artifactSpec(args: readonly string[]): ArtifactSpec {
25 switch (cargoArgs[0]) { 26 const cargoArgs = [...args, "--message-format=json"];
26 case "run": cargoArgs[0] = "build"; break; 27
27 case "test": { 28 // arguments for a runnable from the quick pick should be updated.
28 if (!cargoArgs.includes("--no-run")) { 29 // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
29 cargoArgs.push("--no-run"); 30 switch (cargoArgs[0]) {
31 case "run": cargoArgs[0] = "build"; break;
32 case "test": {
33 if (!cargoArgs.includes("--no-run")) {
34 cargoArgs.push("--no-run");
35 }
36 break;
30 } 37 }
31 break;
32 } 38 }
33 }
34 39
35 const result: ArtifactSpec = { cargoArgs: cargoArgs }; 40 const result: ArtifactSpec = { cargoArgs: cargoArgs };
36 if (cargoArgs[0] === "test") { 41 if (cargoArgs[0] === "test") {
37 // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests 42 // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
38 // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"} 43 // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
39 result.filter = (artifacts) => artifacts.filter(it => it.isTest); 44 result.filter = (artifacts) => artifacts.filter(it => it.isTest);
40 } 45 }
41
42 return result;
43}
44 46
45export class Cargo { 47 return result;
46 constructor(readonly rootFolder: string, readonly output: OutputChannel) { } 48 }
47 49
48 private async getArtifacts(spec: ArtifactSpec): Promise<CompilationArtifact[]> { 50 private async getArtifacts(spec: ArtifactSpec): Promise<CompilationArtifact[]> {
49 const artifacts: CompilationArtifact[] = []; 51 const artifacts: CompilationArtifact[] = [];
@@ -77,7 +79,7 @@ export class Cargo {
77 } 79 }
78 80
79 async executableFromArgs(args: readonly string[]): Promise<string> { 81 async executableFromArgs(args: readonly string[]): Promise<string> {
80 const artifacts = await this.getArtifacts(artifactSpec(args)); 82 const artifacts = await this.getArtifacts(Cargo.artifactSpec(args));
81 83
82 if (artifacts.length === 0) { 84 if (artifacts.length === 0) {
83 throw new Error('No compilation artifacts'); 85 throw new Error('No compilation artifacts');
@@ -94,14 +96,7 @@ export class Cargo {
94 onStderrString: (data: string) => void 96 onStderrString: (data: string) => void
95 ): Promise<number> { 97 ): Promise<number> {
96 return new Promise((resolve, reject) => { 98 return new Promise((resolve, reject) => {
97 let cargoPath; 99 const cargo = cp.spawn(cargoPath(), cargoArgs, {
98 try {
99 cargoPath = getCargoPathOrFail();
100 } catch (err) {
101 return reject(err);
102 }
103
104 const cargo = cp.spawn(cargoPath, cargoArgs, {
105 stdio: ['ignore', 'pipe', 'pipe'], 100 stdio: ['ignore', 'pipe', 'pipe'],
106 cwd: this.rootFolder 101 cwd: this.rootFolder
107 }); 102 });
@@ -126,26 +121,54 @@ export class Cargo {
126 } 121 }
127} 122}
128 123
129// Mirrors `ra_env::get_path_for_executable` implementation 124/** Mirrors `ra_toolchain::cargo()` implementation */
130function getCargoPathOrFail(): string { 125export function cargoPath(): string {
131 const envVar = process.env.CARGO; 126 return getPathForExecutable("cargo");
132 const executableName = "cargo"; 127}
128
129/** Mirrors `ra_toolchain::get_path_for_executable()` implementation */
130export const getPathForExecutable = memoize(
131 // We apply caching to decrease file-system interactions
132 (executableName: "cargo" | "rustc" | "rustup"): string => {
133 {
134 const envVar = process.env[executableName.toUpperCase()];
135 if (envVar) return envVar;
136 }
137
138 if (lookupInPath(executableName)) return executableName;
133 139
134 if (envVar) { 140 try {
135 if (isValidExecutable(envVar)) return envVar; 141 // hmm, `os.homedir()` seems to be infallible
142 // it is not mentioned in docs and cannot be infered by the type signature...
143 const standardPath = path.join(os.homedir(), ".cargo", "bin", executableName);
136 144
137 throw new Error(`\`${envVar}\` environment variable points to something that's not a valid executable`); 145 if (isFile(standardPath)) return standardPath;
146 } catch (err) {
147 log.error("Failed to read the fs info", err);
148 }
149 return executableName;
138 } 150 }
151);
139 152
140 if (isValidExecutable(executableName)) return executableName; 153function lookupInPath(exec: string): boolean {
154 const paths = process.env.PATH ?? "";;
141 155
142 const standardLocation = path.join(os.homedir(), '.cargo', 'bin', executableName); 156 const candidates = paths.split(path.delimiter).flatMap(dirInPath => {
157 const candidate = path.join(dirInPath, exec);
158 return os.type() === "Windows_NT"
159 ? [candidate, `${candidate}.exe`]
160 : [candidate];
161 });
143 162
144 if (isValidExecutable(standardLocation)) return standardLocation; 163 return candidates.some(isFile);
164}
145 165
146 throw new Error( 166function isFile(suspectPath: string): boolean {
147 `Failed to find \`${executableName}\` executable. ` + 167 // It is not mentionned in docs, but `statSync()` throws an error when
148 `Make sure \`${executableName}\` is in \`$PATH\`, ` + 168 // the path doesn't exist
149 `or set \`${envVar}\` to point to a valid executable.` 169 try {
150 ); 170 return fs.statSync(suspectPath).isFile();
171 } catch {
172 return false;
173 }
151} 174}
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 352ef9162..fe3fb71cd 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -99,3 +99,21 @@ export function isValidExecutable(path: string): boolean {
99export function setContextValue(key: string, value: any): Thenable<void> { 99export function setContextValue(key: string, value: any): Thenable<void> {
100 return vscode.commands.executeCommand('setContext', key, value); 100 return vscode.commands.executeCommand('setContext', key, value);
101} 101}
102
103/**
104 * Returns a higher-order function that caches the results of invoking the
105 * underlying function.
106 */
107export function memoize<Ret, TThis, Param extends string>(func: (this: TThis, arg: Param) => Ret) {
108 const cache = new Map<string, Ret>();
109
110 return function(this: TThis, arg: Param) {
111 const cached = cache.get(arg);
112 if (cached) return cached;
113
114 const result = func.call(this, arg);
115 cache.set(arg, result);
116
117 return result;
118 };
119}
diff --git a/editors/code/tests/unit/launch_config.test.ts b/editors/code/tests/unit/launch_config.test.ts
index d5cf1b74e..68794d53e 100644
--- a/editors/code/tests/unit/launch_config.test.ts
+++ b/editors/code/tests/unit/launch_config.test.ts
@@ -1,25 +1,25 @@
1import * as assert from 'assert'; 1import * as assert from 'assert';
2import * as cargo from '../../src/cargo'; 2import { Cargo } from '../../src/toolchain';
3 3
4suite('Launch configuration', () => { 4suite('Launch configuration', () => {
5 5
6 suite('Lens', () => { 6 suite('Lens', () => {
7 test('A binary', async () => { 7 test('A binary', async () => {
8 const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]); 8 const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]);
9 9
10 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]); 10 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
11 assert.deepEqual(args.filter, undefined); 11 assert.deepEqual(args.filter, undefined);
12 }); 12 });
13 13
14 test('One of Multiple Binaries', async () => { 14 test('One of Multiple Binaries', async () => {
15 const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]); 15 const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]);
16 16
17 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin1", "--message-format=json"]); 17 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin1", "--message-format=json"]);
18 assert.deepEqual(args.filter, undefined); 18 assert.deepEqual(args.filter, undefined);
19 }); 19 });
20 20
21 test('A test', async () => { 21 test('A test', async () => {
22 const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]); 22 const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]);
23 23
24 assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--no-run", "--message-format=json"]); 24 assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--no-run", "--message-format=json"]);
25 assert.notDeepEqual(args.filter, undefined); 25 assert.notDeepEqual(args.filter, undefined);
@@ -28,7 +28,7 @@ suite('Launch configuration', () => {
28 28
29 suite('QuickPick', () => { 29 suite('QuickPick', () => {
30 test('A binary', async () => { 30 test('A binary', async () => {
31 const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]); 31 const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]);
32 32
33 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]); 33 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
34 assert.deepEqual(args.filter, undefined); 34 assert.deepEqual(args.filter, undefined);
@@ -36,14 +36,14 @@ suite('Launch configuration', () => {
36 36
37 37
38 test('One of Multiple Binaries', async () => { 38 test('One of Multiple Binaries', async () => {
39 const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]); 39 const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]);
40 40
41 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin2", "--message-format=json"]); 41 assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin2", "--message-format=json"]);
42 assert.deepEqual(args.filter, undefined); 42 assert.deepEqual(args.filter, undefined);
43 }); 43 });
44 44
45 test('A test', async () => { 45 test('A test', async () => {
46 const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]); 46 const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]);
47 47
48 assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--message-format=json", "--no-run"]); 48 assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--message-format=json", "--no-run"]);
49 assert.notDeepEqual(args.filter, undefined); 49 assert.notDeepEqual(args.filter, undefined);
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index f60f0fb16..392648d71 100644
--- a/xtask/src/ast_src.rs
+++ b/xtask/src/ast_src.rs
@@ -1707,7 +1707,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
1707 /// ``` 1707 /// ```
1708 /// 1708 ///
1709 /// [Reference](https://doc.rust-lang.org/reference/items/generics.html#where-clauses) 1709 /// [Reference](https://doc.rust-lang.org/reference/items/generics.html#where-clauses)
1710 struct WherePred: TypeBoundsOwner { T![lifetime], TypeRef } 1710 struct WherePred: TypeBoundsOwner { T![for], TypeParamList, T![lifetime], TypeRef }
1711 1711
1712 /// Where clause. 1712 /// Where clause.
1713 /// 1713 ///
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index 5511c01d5..f5f4b964a 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -18,8 +18,10 @@ use std::{
18use crate::{not_bash::fs2, project_root, Result}; 18use crate::{not_bash::fs2, project_root, Result};
19 19
20pub use self::{ 20pub use self::{
21 gen_assists_docs::generate_assists_docs, gen_feature_docs::generate_feature_docs, 21 gen_assists_docs::{generate_assists_docs, generate_assists_tests},
22 gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax, 22 gen_feature_docs::generate_feature_docs,
23 gen_parser_tests::generate_parser_tests,
24 gen_syntax::generate_syntax,
23}; 25};
24 26
25const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar"; 27const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar";
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs
index 6c1be5350..526941f73 100644
--- a/xtask/src/codegen/gen_assists_docs.rs
+++ b/xtask/src/codegen/gen_assists_docs.rs
@@ -7,16 +7,17 @@ use crate::{
7 project_root, rust_files, Result, 7 project_root, rust_files, Result,
8}; 8};
9 9
10pub fn generate_assists_docs(mode: Mode) -> Result<()> { 10pub fn generate_assists_tests(mode: Mode) -> Result<()> {
11 let assists = Assist::collect()?; 11 let assists = Assist::collect()?;
12 generate_tests(&assists, mode)?; 12 generate_tests(&assists, mode)
13}
13 14
15pub fn generate_assists_docs(mode: Mode) -> Result<()> {
16 let assists = Assist::collect()?;
14 let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); 17 let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
15 let contents = contents.trim().to_string() + "\n"; 18 let contents = contents.trim().to_string() + "\n";
16 let dst = project_root().join("docs/user/generated_assists.adoc"); 19 let dst = project_root().join("docs/user/generated_assists.adoc");
17 codegen::update(&dst, &contents, mode)?; 20 codegen::update(&dst, &contents, mode)
18
19 Ok(())
20} 21}
21 22
22#[derive(Debug)] 23#[derive(Debug)]
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs
index 19d5594f5..745a25862 100644
--- a/xtask/src/codegen/gen_syntax.rs
+++ b/xtask/src/codegen/gen_syntax.rs
@@ -40,7 +40,7 @@ fn generate_tokens(grammar: AstSrc<'_>) -> Result<String> {
40 pub(crate) syntax: SyntaxToken, 40 pub(crate) syntax: SyntaxToken,
41 } 41 }
42 impl std::fmt::Display for #name { 42 impl std::fmt::Display for #name {
43 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 std::fmt::Display::fmt(&self.syntax, f) 44 std::fmt::Display::fmt(&self.syntax, f)
45 } 45 }
46 } 46 }
@@ -68,7 +68,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
68 .iter() 68 .iter()
69 .map(|node| { 69 .map(|node| {
70 let name = format_ident!("{}", node.name); 70 let name = format_ident!("{}", node.name);
71 let kind = format_ident!("{}", to_upper_snake_case(&name.to_string())); 71 let kind = format_ident!("{}", to_upper_snake_case(node.name));
72 let traits = node.traits.iter().map(|trait_name| { 72 let traits = node.traits.iter().map(|trait_name| {
73 let trait_name = format_ident!("{}", trait_name); 73 let trait_name = format_ident!("{}", trait_name);
74 quote!(impl ast::#trait_name for #name {}) 74 quote!(impl ast::#trait_name for #name {})
@@ -199,7 +199,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: AstSrc<'_>) -> Result<String> {
199 enum_names.chain(node_names.clone()).map(|it| format_ident!("{}", it)).map(|name| { 199 enum_names.chain(node_names.clone()).map(|it| format_ident!("{}", it)).map(|name| {
200 quote! { 200 quote! {
201 impl std::fmt::Display for #name { 201 impl std::fmt::Display for #name {
202 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 std::fmt::Display::fmt(self.syntax(), f) 203 std::fmt::Display::fmt(self.syntax(), f)
204 } 204 }
205 } 205 }
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
index 874957885..747654c1f 100644
--- a/xtask/src/lib.rs
+++ b/xtask/src/lib.rs
@@ -4,6 +4,7 @@
4 4
5pub mod not_bash; 5pub mod not_bash;
6pub mod install; 6pub mod install;
7pub mod release;
7pub mod dist; 8pub mod dist;
8pub mod pre_commit; 9pub mod pre_commit;
9 10
@@ -19,7 +20,7 @@ use walkdir::{DirEntry, WalkDir};
19 20
20use crate::{ 21use crate::{
21 codegen::Mode, 22 codegen::Mode,
22 not_bash::{date_iso, fs2, pushd, pushenv, rm_rf, run}, 23 not_bash::{fs2, pushd, pushenv, rm_rf, run},
23}; 24};
24 25
25pub use anyhow::{bail, Context as _, Result}; 26pub use anyhow::{bail, Context as _, Result};
@@ -153,58 +154,6 @@ pub fn run_pre_cache() -> Result<()> {
153 Ok(()) 154 Ok(())
154} 155}
155 156
156pub fn run_release(dry_run: bool) -> Result<()> {
157 if !dry_run {
158 run!("git switch release")?;
159 run!("git fetch upstream --tags --force")?;
160 run!("git reset --hard tags/nightly")?;
161 run!("git push")?;
162 }
163
164 let website_root = project_root().join("../rust-analyzer.github.io");
165 let changelog_dir = website_root.join("./thisweek/_posts");
166
167 let today = date_iso()?;
168 let commit = run!("git rev-parse HEAD")?;
169 let changelog_n = fs2::read_dir(changelog_dir.as_path())?.count();
170
171 let contents = format!(
172 "\
173= Changelog #{}
174:sectanchors:
175:page-layout: post
176
177Commit: commit:{}[] +
178Release: release:{}[]
179
180== New Features
181
182* pr:[] .
183
184== Fixes
185
186== Internal Improvements
187",
188 changelog_n, commit, today
189 );
190
191 let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
192 fs2::write(&path, &contents)?;
193
194 for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() {
195 let src = project_root().join("./docs/user/").join(adoc);
196 let dst = website_root.join(adoc);
197 fs2::copy(src, dst)?;
198 }
199
200 let tags = run!("git tag --list"; echo = false)?;
201 let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap();
202
203 println!("\n git log {}..HEAD --merges --reverse", prev_tag);
204
205 Ok(())
206}
207
208fn is_release_tag(tag: &str) -> bool { 157fn is_release_tag(tag: &str) -> bool {
209 tag.len() == "2020-02-24".len() && tag.starts_with(|c: char| c.is_ascii_digit()) 158 tag.len() == "2020-02-24".len() && tag.starts_with(|c: char| c.is_ascii_digit())
210} 159}
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 9d7cdd114..f7a79362d 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -16,8 +16,9 @@ use xtask::{
16 dist::run_dist, 16 dist::run_dist,
17 install::{ClientOpt, InstallCmd, ServerOpt}, 17 install::{ClientOpt, InstallCmd, ServerOpt},
18 not_bash::pushd, 18 not_bash::pushd,
19 pre_commit, project_root, run_clippy, run_fuzzer, run_pre_cache, run_release, run_rustfmt, 19 pre_commit, project_root,
20 Result, 20 release::ReleaseCmd,
21 run_clippy, run_fuzzer, run_pre_cache, run_rustfmt, Result,
21}; 22};
22 23
23fn main() -> Result<()> { 24fn main() -> Result<()> {
@@ -74,6 +75,7 @@ FLAGS:
74 args.finish()?; 75 args.finish()?;
75 codegen::generate_syntax(Mode::Overwrite)?; 76 codegen::generate_syntax(Mode::Overwrite)?;
76 codegen::generate_parser_tests(Mode::Overwrite)?; 77 codegen::generate_parser_tests(Mode::Overwrite)?;
78 codegen::generate_assists_tests(Mode::Overwrite)?;
77 codegen::generate_assists_docs(Mode::Overwrite)?; 79 codegen::generate_assists_docs(Mode::Overwrite)?;
78 codegen::generate_feature_docs(Mode::Overwrite)?; 80 codegen::generate_feature_docs(Mode::Overwrite)?;
79 Ok(()) 81 Ok(())
@@ -101,7 +103,7 @@ FLAGS:
101 "release" => { 103 "release" => {
102 let dry_run = args.contains("--dry-run"); 104 let dry_run = args.contains("--dry-run");
103 args.finish()?; 105 args.finish()?;
104 run_release(dry_run) 106 ReleaseCmd { dry_run }.run()
105 } 107 }
106 "dist" => { 108 "dist" => {
107 let nightly = args.contains("--nightly"); 109 let nightly = args.contains("--nightly");
diff --git a/xtask/src/release.rs b/xtask/src/release.rs
new file mode 100644
index 000000000..36c912184
--- /dev/null
+++ b/xtask/src/release.rs
@@ -0,0 +1,67 @@
1use crate::{
2 codegen, is_release_tag,
3 not_bash::{date_iso, fs2, run},
4 project_root, Mode, Result,
5};
6
7pub struct ReleaseCmd {
8 pub dry_run: bool,
9}
10
11impl ReleaseCmd {
12 pub fn run(self) -> Result<()> {
13 if !self.dry_run {
14 run!("git switch release")?;
15 run!("git fetch upstream --tags --force")?;
16 run!("git reset --hard tags/nightly")?;
17 run!("git push")?;
18 }
19 codegen::generate_assists_docs(Mode::Overwrite)?;
20 codegen::generate_feature_docs(Mode::Overwrite)?;
21
22 let website_root = project_root().join("../rust-analyzer.github.io");
23 let changelog_dir = website_root.join("./thisweek/_posts");
24
25 let today = date_iso()?;
26 let commit = run!("git rev-parse HEAD")?;
27 let changelog_n = fs2::read_dir(changelog_dir.as_path())?.count();
28
29 let contents = format!(
30 "\
31 = Changelog #{}
32 :sectanchors:
33 :page-layout: post
34
35 Commit: commit:{}[] +
36 Release: release:{}[]
37
38 == New Features
39
40 * pr:[] .
41
42 == Fixes
43
44 == Internal Improvements
45 ",
46 changelog_n, commit, today
47 );
48
49 let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
50 fs2::write(&path, &contents)?;
51
52 for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() {
53 let src = project_root().join("./docs/user/").join(adoc);
54 let dst = website_root.join(adoc);
55 fs2::copy(src, dst)?;
56 }
57
58 let tags = run!("git tag --list"; echo = false)?;
59 let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap();
60
61 let git_log = run!("git log {}..HEAD --merges --reverse", prev_tag; echo = false)?;
62 let git_log_dst = website_root.join("git.log");
63 fs2::write(git_log_dst, &git_log)?;
64
65 Ok(())
66 }
67}
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 4ac5d929f..d38ac7f17 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -25,19 +25,12 @@ fn generated_tests_are_fresh() {
25 25
26#[test] 26#[test]
27fn generated_assists_are_fresh() { 27fn generated_assists_are_fresh() {
28 if let Err(error) = codegen::generate_assists_docs(Mode::Verify) { 28 if let Err(error) = codegen::generate_assists_tests(Mode::Verify) {
29 panic!("{}. Please update assists by running `cargo xtask codegen`", error); 29 panic!("{}. Please update assists by running `cargo xtask codegen`", error);
30 } 30 }
31} 31}
32 32
33#[test] 33#[test]
34fn generated_features_are_fresh() {
35 if let Err(error) = codegen::generate_feature_docs(Mode::Verify) {
36 panic!("{}. Please update features by running `cargo xtask codegen`", error);
37 }
38}
39
40#[test]
41fn check_code_formatting() { 34fn check_code_formatting() {
42 if let Err(error) = run_rustfmt(Mode::Verify) { 35 if let Err(error) = run_rustfmt(Mode::Verify) {
43 panic!("{}. Please format the code by running `cargo format`", error); 36 panic!("{}. Please format the code by running `cargo format`", error);
@@ -180,13 +173,11 @@ impl TidyDocs {
180} 173}
181 174
182fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool { 175fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool {
183 let mut cur_path = p; 176 p.strip_prefix(project_root())
184 while let Some(path) = cur_path.parent() { 177 .unwrap()
185 if dirs_to_exclude.iter().any(|dir| path.ends_with(dir)) { 178 .components()
186 return true; 179 .rev()
187 } 180 .skip(1)
188 cur_path = path; 181 .filter_map(|it| it.as_os_str().to_str())
189 } 182 .any(|it| dirs_to_exclude.contains(&it))
190
191 false
192} 183}