aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock53
-rw-r--r--crates/ra_assists/src/ast_transform.rs9
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs4
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs8
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_cargo_watch/src/conv.rs128
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap172
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap76
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap102
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap114
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap76
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap76
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap148
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap108
-rw-r--r--crates/ra_cargo_watch/src/conv/test.rs148
-rw-r--r--crates/ra_cargo_watch/src/lib.rs32
-rw-r--r--crates/ra_db/src/fixture.rs63
-rw-r--r--crates/ra_db/src/input.rs53
-rw-r--r--crates/ra_db/src/lib.rs18
-rw-r--r--crates/ra_hir/Cargo.toml1
-rw-r--r--crates/ra_hir/src/code_model.rs56
-rw-r--r--crates/ra_hir/src/semantics.rs8
-rw-r--r--crates/ra_hir_def/src/body.rs12
-rw-r--r--crates/ra_hir_def/src/body/lower.rs48
-rw-r--r--crates/ra_hir_def/src/data.rs28
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs48
-rw-r--r--crates/ra_hir_def/src/nameres/tests.rs22
-rw-r--r--crates/ra_hir_def/src/resolver.rs15
-rw-r--r--crates/ra_hir_def/src/test_db.rs10
-rw-r--r--crates/ra_hir_def/src/visibility.rs33
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs228
-rw-r--r--crates/ra_hir_expand/src/lib.rs20
-rw-r--r--crates/ra_hir_expand/src/name.rs1
-rw-r--r--crates/ra_hir_expand/src/quote.rs1
-rw-r--r--crates/ra_hir_expand/src/test_db.rs9
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs28
-rw-r--r--crates/ra_hir_ty/src/test_db.rs7
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs40
-rw-r--r--crates/ra_ide/src/call_info.rs14
-rw-r--r--crates/ra_ide/src/completion.rs29
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs65
-rw-r--r--crates/ra_ide/src/completion/complete_fn_param.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_macro_in_item_position.rs3
-rw-r--r--crates/ra_ide/src/completion/complete_path.rs72
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs4
-rw-r--r--crates/ra_ide/src/completion/complete_record_literal.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_record_pattern.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_scope.rs42
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs146
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs5
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs22
-rw-r--r--crates/ra_ide/src/completion/presentation.rs86
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs29
-rw-r--r--crates/ra_ide/src/display.rs37
-rw-r--r--crates/ra_ide/src/display/function_signature.rs4
-rw-r--r--crates/ra_ide/src/goto_definition.rs15
-rw-r--r--crates/ra_ide/src/hover.rs89
-rw-r--r--crates/ra_ide/src/inlay_hints.rs113
-rw-r--r--crates/ra_ide/src/lib.rs33
-rw-r--r--crates/ra_ide/src/mock_analysis.rs2
-rw-r--r--crates/ra_ide/src/parent_module.rs1
-rw-r--r--crates/ra_ide/src/references/rename.rs208
-rw-r--r--crates/ra_ide/src/syntax_tree.rs16
-rw-r--r--crates/ra_ide/src/typing.rs200
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs216
-rw-r--r--crates/ra_ide_db/Cargo.toml2
-rw-r--r--crates/ra_ide_db/src/lib.rs17
-rw-r--r--crates/ra_ide_db/src/line_index.rs14
-rw-r--r--crates/ra_ide_db/src/line_index_utils.rs2
-rw-r--r--crates/ra_ide_db/src/search.rs28
-rw-r--r--crates/ra_ide_db/src/symbol_index.rs4
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs10
-rw-r--r--crates/ra_parser/src/grammar/params.rs64
-rw-r--r--crates/ra_project_model/src/lib.rs25
-rw-r--r--crates/ra_syntax/src/ast.rs2
-rw-r--r--crates/ra_syntax/src/ast/generated.rs635
-rw-r--r--crates/ra_syntax/src/parsing/lexer.rs4
-rw-r--r--crates/ra_syntax/src/tests.rs11
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0032_fn_pointer_type.txt3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0116_trait_fn_placeholder_parameter.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0116_trait_fn_placeholder_parameter.txt47
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0123_param_list_vararg.txt3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0156_fn_def_param.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0156_fn_def_param.txt44
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0158_binop_resets_statementness.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0158_binop_resets_statementness.txt38
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.txt3
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rs (renamed from crates/ra_syntax/test_data/parser/inline/ok/0153_trait_fn_patterns.rs)1
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.txt (renamed from crates/ra_syntax/test_data/parser/inline/ok/0153_trait_fn_patterns.txt)47
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0063_variadic_fun.rs5
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0063_variadic_fun.txt133
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rs (renamed from crates/ra_syntax/test_data/parser/inline/ok/0152_fn_patterns.rs)0
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.txt (renamed from crates/ra_syntax/test_data/parser/inline/ok/0152_fn_patterns.txt)0
-rw-r--r--crates/ra_text_edit/src/text_edit.rs10
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs42
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs12
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs9
-rw-r--r--crates/rust-analyzer/src/config.rs18
-rw-r--r--crates/rust-analyzer/src/conv.rs18
-rw-r--r--crates/rust-analyzer/src/feature_flags.rs (renamed from crates/ra_ide_db/src/feature_flags.rs)4
-rw-r--r--crates/rust-analyzer/src/lib.rs1
-rw-r--r--crates/rust-analyzer/src/main_loop.rs15
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs57
-rw-r--r--crates/rust-analyzer/src/req.rs1
-rw-r--r--crates/rust-analyzer/src/world.rs58
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs15
-rw-r--r--crates/test_utils/src/lib.rs16
-rw-r--r--docs/user/features.md5
-rw-r--r--editors/code/package-lock.json6
-rw-r--r--editors/code/package.json22
-rw-r--r--editors/code/src/client.ts9
-rw-r--r--editors/code/src/commands/runnables.ts22
-rw-r--r--editors/code/src/config.ts19
-rw-r--r--editors/code/src/inlay_hints.ts2
-rw-r--r--editors/code/src/main.ts1
-rw-r--r--editors/code/src/rust-analyzer-api.ts3
-rw-r--r--xtask/src/codegen/gen_syntax.rs12
-rw-r--r--xtask/src/lib.rs8
121 files changed, 3733 insertions, 1269 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e3ad50623..efe8dd189 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -11,9 +11,9 @@ dependencies = [
11 11
12[[package]] 12[[package]]
13name = "anyhow" 13name = "anyhow"
14version = "1.0.26" 14version = "1.0.27"
15source = "registry+https://github.com/rust-lang/crates.io-index" 15source = "registry+https://github.com/rust-lang/crates.io-index"
16checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" 16checksum = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785"
17 17
18[[package]] 18[[package]]
19name = "anymap" 19name = "anymap"
@@ -58,9 +58,9 @@ dependencies = [
58 58
59[[package]] 59[[package]]
60name = "backtrace-sys" 60name = "backtrace-sys"
61version = "0.1.33" 61version = "0.1.34"
62source = "registry+https://github.com/rust-lang/crates.io-index" 62source = "registry+https://github.com/rust-lang/crates.io-index"
63checksum = "e17b52e737c40a7d75abca20b29a19a0eb7ba9fc72c5a72dd282a0a3c2c0dc35" 63checksum = "ca797db0057bae1a7aa2eef3283a874695455cecf08a43bfb8507ee0ebc1ed69"
64dependencies = [ 64dependencies = [
65 "cc", 65 "cc",
66 "libc", 66 "libc",
@@ -80,29 +80,14 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
80 80
81[[package]] 81[[package]]
82name = "bstr" 82name = "bstr"
83version = "0.2.11" 83version = "0.2.12"
84source = "registry+https://github.com/rust-lang/crates.io-index" 84source = "registry+https://github.com/rust-lang/crates.io-index"
85checksum = "502ae1441a0a5adb8fbd38a5955a6416b9493e92b465de5e4a9bde6a539c2c48" 85checksum = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41"
86dependencies = [ 86dependencies = [
87 "memchr", 87 "memchr",
88] 88]
89 89
90[[package]] 90[[package]]
91name = "byteorder"
92version = "1.3.4"
93source = "registry+https://github.com/rust-lang/crates.io-index"
94checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
95
96[[package]]
97name = "c2-chacha"
98version = "0.2.3"
99source = "registry+https://github.com/rust-lang/crates.io-index"
100checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
101dependencies = [
102 "ppv-lite86",
103]
104
105[[package]]
106name = "cargo_metadata" 91name = "cargo_metadata"
107version = "0.9.1" 92version = "0.9.1"
108source = "registry+https://github.com/rust-lang/crates.io-index" 93source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -401,12 +386,9 @@ dependencies = [
401 386
402[[package]] 387[[package]]
403name = "fst" 388name = "fst"
404version = "0.3.5" 389version = "0.4.0"
405source = "registry+https://github.com/rust-lang/crates.io-index" 390source = "registry+https://github.com/rust-lang/crates.io-index"
406checksum = "927fb434ff9f0115b215dc0efd2e4fbdd7448522a92a1aa37c77d6a2f8f1ebd6" 391checksum = "3f7c13470d799474d44e2b9c6a0925807def7af4d120cd4de761433be76f7579"
407dependencies = [
408 "byteorder",
409]
410 392
411[[package]] 393[[package]]
412name = "fuchsia-zircon" 394name = "fuchsia-zircon"
@@ -958,6 +940,7 @@ dependencies = [
958name = "ra_hir" 940name = "ra_hir"
959version = "0.1.0" 941version = "0.1.0"
960dependencies = [ 942dependencies = [
943 "arrayvec",
961 "either", 944 "either",
962 "itertools", 945 "itertools",
963 "log", 946 "log",
@@ -1180,11 +1163,11 @@ dependencies = [
1180 1163
1181[[package]] 1164[[package]]
1182name = "rand_chacha" 1165name = "rand_chacha"
1183version = "0.2.1" 1166version = "0.2.2"
1184source = "registry+https://github.com/rust-lang/crates.io-index" 1167source = "registry+https://github.com/rust-lang/crates.io-index"
1185checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" 1168checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
1186dependencies = [ 1169dependencies = [
1187 "c2-chacha", 1170 "ppv-lite86",
1188 "rand_core", 1171 "rand_core",
1189] 1172]
1190 1173
@@ -1247,9 +1230,9 @@ checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
1247 1230
1248[[package]] 1231[[package]]
1249name = "regex" 1232name = "regex"
1250version = "1.3.4" 1233version = "1.3.5"
1251source = "registry+https://github.com/rust-lang/crates.io-index" 1234source = "registry+https://github.com/rust-lang/crates.io-index"
1252checksum = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" 1235checksum = "8900ebc1363efa7ea1c399ccc32daed870b4002651e0bed86e72d501ebbe0048"
1253dependencies = [ 1236dependencies = [
1254 "aho-corasick", 1237 "aho-corasick",
1255 "memchr", 1238 "memchr",
@@ -1259,9 +1242,9 @@ dependencies = [
1259 1242
1260[[package]] 1243[[package]]
1261name = "regex-syntax" 1244name = "regex-syntax"
1262version = "0.6.16" 1245version = "0.6.17"
1263source = "registry+https://github.com/rust-lang/crates.io-index" 1246source = "registry+https://github.com/rust-lang/crates.io-index"
1264checksum = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1" 1247checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"
1265 1248
1266[[package]] 1249[[package]]
1267name = "relative-path" 1250name = "relative-path"
@@ -1359,9 +1342,9 @@ dependencies = [
1359 1342
1360[[package]] 1343[[package]]
1361name = "ryu" 1344name = "ryu"
1362version = "1.0.2" 1345version = "1.0.3"
1363source = "registry+https://github.com/rust-lang/crates.io-index" 1346source = "registry+https://github.com/rust-lang/crates.io-index"
1364checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" 1347checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76"
1365 1348
1366[[package]] 1349[[package]]
1367name = "salsa" 1350name = "salsa"
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 42856f0ca..45558c448 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -37,7 +37,6 @@ pub struct SubstituteTypeParams<'a> {
37impl<'a> SubstituteTypeParams<'a> { 37impl<'a> SubstituteTypeParams<'a> {
38 pub fn for_trait_impl( 38 pub fn for_trait_impl(
39 source_scope: &'a SemanticsScope<'a, RootDatabase>, 39 source_scope: &'a SemanticsScope<'a, RootDatabase>,
40 db: &'a RootDatabase,
41 // FIXME: there's implicit invariant that `trait_` and `source_scope` match... 40 // FIXME: there's implicit invariant that `trait_` and `source_scope` match...
42 trait_: hir::Trait, 41 trait_: hir::Trait,
43 impl_def: ast::ImplDef, 42 impl_def: ast::ImplDef,
@@ -45,7 +44,7 @@ impl<'a> SubstituteTypeParams<'a> {
45 let substs = get_syntactic_substs(impl_def).unwrap_or_default(); 44 let substs = get_syntactic_substs(impl_def).unwrap_or_default();
46 let generic_def: hir::GenericDef = trait_.into(); 45 let generic_def: hir::GenericDef = trait_.into();
47 let substs_by_param: FxHashMap<_, _> = generic_def 46 let substs_by_param: FxHashMap<_, _> = generic_def
48 .params(db) 47 .params(source_scope.db)
49 .into_iter() 48 .into_iter()
50 // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky 49 // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
51 .skip(1) 50 .skip(1)
@@ -104,7 +103,6 @@ impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
104pub struct QualifyPaths<'a> { 103pub struct QualifyPaths<'a> {
105 target_scope: &'a SemanticsScope<'a, RootDatabase>, 104 target_scope: &'a SemanticsScope<'a, RootDatabase>,
106 source_scope: &'a SemanticsScope<'a, RootDatabase>, 105 source_scope: &'a SemanticsScope<'a, RootDatabase>,
107 db: &'a RootDatabase,
108 previous: Box<dyn AstTransform<'a> + 'a>, 106 previous: Box<dyn AstTransform<'a> + 'a>,
109} 107}
110 108
@@ -112,9 +110,8 @@ impl<'a> QualifyPaths<'a> {
112 pub fn new( 110 pub fn new(
113 target_scope: &'a SemanticsScope<'a, RootDatabase>, 111 target_scope: &'a SemanticsScope<'a, RootDatabase>,
114 source_scope: &'a SemanticsScope<'a, RootDatabase>, 112 source_scope: &'a SemanticsScope<'a, RootDatabase>,
115 db: &'a RootDatabase,
116 ) -> Self { 113 ) -> Self {
117 Self { target_scope, source_scope, db, previous: Box::new(NullTransformer) } 114 Self { target_scope, source_scope, previous: Box::new(NullTransformer) }
118 } 115 }
119 116
120 fn get_substitution_inner( 117 fn get_substitution_inner(
@@ -132,7 +129,7 @@ impl<'a> QualifyPaths<'a> {
132 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; 129 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
133 match resolution { 130 match resolution {
134 PathResolution::Def(def) => { 131 PathResolution::Def(def) => {
135 let found_path = from.find_use_path(self.db, def)?; 132 let found_path = from.find_use_path(self.source_scope.db, def)?;
136 let mut path = path_to_ast(found_path); 133 let mut path = path_to_ast(found_path);
137 134
138 let type_args = p 135 let type_args = p
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index 639180d37..e5920b6f6 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -142,8 +142,8 @@ fn add_missing_impl_members_inner(
142 let n_existing_items = impl_item_list.impl_items().count(); 142 let n_existing_items = impl_item_list.impl_items().count();
143 let source_scope = sema.scope_for_def(trait_); 143 let source_scope = sema.scope_for_def(trait_);
144 let target_scope = sema.scope(impl_item_list.syntax()); 144 let target_scope = sema.scope(impl_item_list.syntax());
145 let ast_transform = QualifyPaths::new(&target_scope, &source_scope, sema.db) 145 let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
146 .or(SubstituteTypeParams::for_trait_impl(&source_scope, sema.db, trait_, impl_node)); 146 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_node));
147 let items = missing_items 147 let items = missing_items
148 .into_iter() 148 .into_iter()
149 .map(|it| ast_transform::apply(&*ast_transform, it)) 149 .map(|it| ast_transform::apply(&*ast_transform, it))
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index e5d8c639d..97cf90ae4 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -2,7 +2,7 @@
2 2
3use std::iter; 3use std::iter;
4 4
5use hir::{db::HirDatabase, Adt, HasSource, Semantics}; 5use hir::{Adt, HasSource, Semantics};
6use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; 6use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner};
7 7
8use crate::{Assist, AssistCtx, AssistId}; 8use crate::{Assist, AssistCtx, AssistId};
@@ -88,11 +88,7 @@ fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
88 }) 88 })
89} 89}
90 90
91fn build_pat( 91fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
92 db: &impl HirDatabase,
93 module: hir::Module,
94 var: hir::EnumVariant,
95) -> Option<ast::Pat> {
96 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?); 92 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?);
97 93
98 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though 94 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 50a15f978..62fadcddd 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -235,7 +235,7 @@ mod helpers {
235 (Some(assist), ExpectedResult::Target(target)) => { 235 (Some(assist), ExpectedResult::Target(target)) => {
236 let action = assist.0[0].action.clone().unwrap(); 236 let action = assist.0[0].action.clone().unwrap();
237 let range = action.target.expect("expected target on action"); 237 let range = action.target.expect("expected target on action");
238 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); 238 assert_eq_text!(&before[range], target);
239 } 239 }
240 (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), 240 (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
241 (None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => { 241 (None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => {
diff --git a/crates/ra_cargo_watch/src/conv.rs b/crates/ra_cargo_watch/src/conv.rs
index 0246adfb5..c6f8ca329 100644
--- a/crates/ra_cargo_watch/src/conv.rs
+++ b/crates/ra_cargo_watch/src/conv.rs
@@ -8,6 +8,7 @@ use lsp_types::{
8 Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit, 8 Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit,
9}; 9};
10use std::{ 10use std::{
11 collections::HashMap,
11 fmt::Write, 12 fmt::Write,
12 path::{Component, Path, PathBuf, Prefix}, 13 path::{Component, Path, PathBuf, Prefix},
13 str::FromStr, 14 str::FromStr,
@@ -126,44 +127,34 @@ fn map_rust_child_diagnostic(
126 rd: &RustDiagnostic, 127 rd: &RustDiagnostic,
127 workspace_root: &PathBuf, 128 workspace_root: &PathBuf,
128) -> MappedRustChildDiagnostic { 129) -> MappedRustChildDiagnostic {
129 let span: &DiagnosticSpan = match rd.spans.iter().find(|s| s.is_primary) { 130 let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
130 Some(span) => span, 131 if spans.is_empty() {
131 None => { 132 // `rustc` uses these spanless children as a way to print multi-line
132 // `rustc` uses these spanless children as a way to print multi-line 133 // messages
133 // messages 134 return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
134 return MappedRustChildDiagnostic::MessageLine(rd.message.clone()); 135 }
136
137 let mut edit_map: HashMap<Url, Vec<TextEdit>> = HashMap::new();
138 for &span in &spans {
139 if let Some(suggested_replacement) = &span.suggested_replacement {
140 let location = map_span_to_location(span, workspace_root);
141 let edit = TextEdit::new(location.range, suggested_replacement.clone());
142 edit_map.entry(location.uri).or_default().push(edit);
135 } 143 }
136 }; 144 }
137
138 // If we have a primary span use its location, otherwise use the parent
139 let location = map_span_to_location(&span, workspace_root);
140
141 if let Some(suggested_replacement) = &span.suggested_replacement {
142 // Include our replacement in the title unless it's empty
143 let title = if !suggested_replacement.is_empty() {
144 format!("{}: '{}'", rd.message, suggested_replacement)
145 } else {
146 rd.message.clone()
147 };
148
149 let edit = {
150 let edits = vec![TextEdit::new(location.range, suggested_replacement.clone())];
151 let mut edit_map = std::collections::HashMap::new();
152 edit_map.insert(location.uri, edits);
153 WorkspaceEdit::new(edit_map)
154 };
155 145
146 if !edit_map.is_empty() {
156 MappedRustChildDiagnostic::SuggestedFix(CodeAction { 147 MappedRustChildDiagnostic::SuggestedFix(CodeAction {
157 title, 148 title: rd.message.clone(),
158 kind: Some("quickfix".to_string()), 149 kind: Some("quickfix".to_string()),
159 diagnostics: None, 150 diagnostics: None,
160 edit: Some(edit), 151 edit: Some(WorkspaceEdit::new(edit_map)),
161 command: None, 152 command: None,
162 is_preferred: None, 153 is_preferred: None,
163 }) 154 })
164 } else { 155 } else {
165 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation { 156 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation {
166 location, 157 location: map_span_to_location(spans[0], workspace_root),
167 message: rd.message.clone(), 158 message: rd.message.clone(),
168 }) 159 })
169 } 160 }
@@ -189,13 +180,13 @@ pub(crate) struct MappedRustDiagnostic {
189pub(crate) fn map_rust_diagnostic_to_lsp( 180pub(crate) fn map_rust_diagnostic_to_lsp(
190 rd: &RustDiagnostic, 181 rd: &RustDiagnostic,
191 workspace_root: &PathBuf, 182 workspace_root: &PathBuf,
192) -> Option<MappedRustDiagnostic> { 183) -> Vec<MappedRustDiagnostic> {
193 let primary_span = rd.spans.iter().find(|s| s.is_primary)?; 184 let primary_spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
194 185 if primary_spans.is_empty() {
195 let location = map_span_to_location(&primary_span, workspace_root); 186 return vec![];
187 }
196 188
197 let severity = map_level_to_severity(rd.level); 189 let severity = map_level_to_severity(rd.level);
198 let mut primary_span_label = primary_span.label.as_ref();
199 190
200 let mut source = String::from("rustc"); 191 let mut source = String::from("rustc");
201 let mut code = rd.code.as_ref().map(|c| c.code.clone()); 192 let mut code = rd.code.as_ref().map(|c| c.code.clone());
@@ -208,19 +199,10 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
208 } 199 }
209 } 200 }
210 201
202 let mut needs_primary_span_label = true;
211 let mut related_information = vec![]; 203 let mut related_information = vec![];
212 let mut tags = vec![]; 204 let mut tags = vec![];
213 205
214 // If error occurs from macro expansion, add related info pointing to
215 // where the error originated
216 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
217 let def_loc = map_span_to_location_naive(&primary_span, workspace_root);
218 related_information.push(DiagnosticRelatedInformation {
219 location: def_loc,
220 message: "Error originated from macro here".to_string(),
221 });
222 }
223
224 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { 206 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
225 let related = map_secondary_span_to_related(secondary_span, workspace_root); 207 let related = map_secondary_span_to_related(secondary_span, workspace_root);
226 if let Some(related) = related { 208 if let Some(related) = related {
@@ -240,15 +222,11 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
240 222
241 // These secondary messages usually duplicate the content of the 223 // These secondary messages usually duplicate the content of the
242 // primary span label. 224 // primary span label.
243 primary_span_label = None; 225 needs_primary_span_label = false;
244 } 226 }
245 } 227 }
246 } 228 }
247 229
248 if let Some(primary_span_label) = primary_span_label {
249 write!(&mut message, "\n{}", primary_span_label).unwrap();
250 }
251
252 if is_unused_or_unnecessary(rd) { 230 if is_unused_or_unnecessary(rd) {
253 tags.push(DiagnosticTag::Unnecessary); 231 tags.push(DiagnosticTag::Unnecessary);
254 } 232 }
@@ -257,21 +235,45 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
257 tags.push(DiagnosticTag::Deprecated); 235 tags.push(DiagnosticTag::Deprecated);
258 } 236 }
259 237
260 let diagnostic = Diagnostic { 238 primary_spans
261 range: location.range, 239 .iter()
262 severity, 240 .map(|primary_span| {
263 code: code.map(NumberOrString::String), 241 let location = map_span_to_location(&primary_span, workspace_root);
264 source: Some(source), 242
265 message, 243 let mut message = message.clone();
266 related_information: if !related_information.is_empty() { 244 if needs_primary_span_label {
267 Some(related_information) 245 if let Some(primary_span_label) = &primary_span.label {
268 } else { 246 write!(&mut message, "\n{}", primary_span_label).unwrap();
269 None 247 }
270 }, 248 }
271 tags: if !tags.is_empty() { Some(tags) } else { None }, 249
272 }; 250 // If error occurs from macro expansion, add related info pointing to
273 251 // where the error originated
274 Some(MappedRustDiagnostic { location, diagnostic, fixes }) 252 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
253 let def_loc = map_span_to_location_naive(&primary_span, workspace_root);
254 related_information.push(DiagnosticRelatedInformation {
255 location: def_loc,
256 message: "Error originated from macro here".to_string(),
257 });
258 }
259
260 let diagnostic = Diagnostic {
261 range: location.range,
262 severity,
263 code: code.clone().map(NumberOrString::String),
264 source: Some(source.clone()),
265 message,
266 related_information: if !related_information.is_empty() {
267 Some(related_information.clone())
268 } else {
269 None
270 },
271 tags: if !tags.is_empty() { Some(tags.clone()) } else { None },
272 };
273
274 MappedRustDiagnostic { location, diagnostic, fixes: fixes.clone() }
275 })
276 .collect()
275} 277}
276 278
277/// Returns a `Url` object from a given path, will lowercase drive letters if present. 279/// Returns a `Url` object from a given path, will lowercase drive letters if present.
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
index 95ca163dc..9e8f4eff4 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
@@ -2,98 +2,100 @@
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5MappedRustDiagnostic { 5[
6 location: Location { 6 MappedRustDiagnostic {
7 uri: "file:///test/compiler/mir/tagset.rs", 7 location: Location {
8 range: Range { 8 uri: "file:///test/compiler/mir/tagset.rs",
9 start: Position { 9 range: Range {
10 line: 41, 10 start: Position {
11 character: 23, 11 line: 41,
12 }, 12 character: 23,
13 end: Position { 13 },
14 line: 41, 14 end: Position {
15 character: 28, 15 line: 41,
16 character: 28,
17 },
16 }, 18 },
17 }, 19 },
18 }, 20 diagnostic: Diagnostic {
19 diagnostic: Diagnostic { 21 range: Range {
20 range: Range { 22 start: Position {
21 start: Position { 23 line: 41,
22 line: 41, 24 character: 23,
23 character: 23, 25 },
24 }, 26 end: Position {
25 end: Position { 27 line: 41,
26 line: 41, 28 character: 28,
27 character: 28, 29 },
28 }, 30 },
29 }, 31 severity: Some(
30 severity: Some( 32 Warning,
31 Warning,
32 ),
33 code: Some(
34 String(
35 "trivially_copy_pass_by_ref",
36 ), 33 ),
37 ), 34 code: Some(
38 source: Some( 35 String(
39 "clippy", 36 "trivially_copy_pass_by_ref",
40 ), 37 ),
41 message: "this argument is passed by reference, but would be more efficient if passed by value\n#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref", 38 ),
42 related_information: Some( 39 source: Some(
43 [ 40 "clippy",
44 DiagnosticRelatedInformation { 41 ),
45 location: Location { 42 message: "this argument is passed by reference, but would be more efficient if passed by value\n#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
46 uri: "file:///test/compiler/lib.rs", 43 related_information: Some(
47 range: Range { 44 [
48 start: Position { 45 DiagnosticRelatedInformation {
49 line: 0, 46 location: Location {
50 character: 8, 47 uri: "file:///test/compiler/lib.rs",
51 }, 48 range: Range {
52 end: Position { 49 start: Position {
53 line: 0, 50 line: 0,
54 character: 19, 51 character: 8,
52 },
53 end: Position {
54 line: 0,
55 character: 19,
56 },
55 }, 57 },
56 }, 58 },
59 message: "lint level defined here",
57 }, 60 },
58 message: "lint level defined here", 61 ],
59 },
60 ],
61 ),
62 tags: None,
63 },
64 fixes: [
65 CodeAction {
66 title: "consider passing by value instead: \'self\'",
67 kind: Some(
68 "quickfix",
69 ), 62 ),
70 diagnostics: None, 63 tags: None,
71 edit: Some( 64 },
72 WorkspaceEdit { 65 fixes: [
73 changes: Some( 66 CodeAction {
74 { 67 title: "consider passing by value instead",
75 "file:///test/compiler/mir/tagset.rs": [ 68 kind: Some(
76 TextEdit { 69 "quickfix",
77 range: Range { 70 ),
78 start: Position { 71 diagnostics: None,
79 line: 41, 72 edit: Some(
80 character: 23, 73 WorkspaceEdit {
81 }, 74 changes: Some(
82 end: Position { 75 {
83 line: 41, 76 "file:///test/compiler/mir/tagset.rs": [
84 character: 28, 77 TextEdit {
78 range: Range {
79 start: Position {
80 line: 41,
81 character: 23,
82 },
83 end: Position {
84 line: 41,
85 character: 28,
86 },
85 }, 87 },
88 new_text: "self",
86 }, 89 },
87 new_text: "self", 90 ],
88 }, 91 },
89 ], 92 ),
90 }, 93 document_changes: None,
91 ), 94 },
92 document_changes: None, 95 ),
93 }, 96 command: None,
94 ), 97 is_preferred: None,
95 command: None, 98 },
96 is_preferred: None, 99 ],
97 }, 100 },
98 ], 101]
99}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap
index 12eb32df4..61ae0c9ae 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap
@@ -2,45 +2,47 @@
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5MappedRustDiagnostic { 5[
6 location: Location { 6 MappedRustDiagnostic {
7 uri: "file:///test/src/main.rs", 7 location: Location {
8 range: Range { 8 uri: "file:///test/src/main.rs",
9 start: Position { 9 range: Range {
10 line: 1, 10 start: Position {
11 character: 4, 11 line: 1,
12 }, 12 character: 4,
13 end: Position { 13 },
14 line: 1, 14 end: Position {
15 character: 26, 15 line: 1,
16 character: 26,
17 },
16 }, 18 },
17 }, 19 },
18 }, 20 diagnostic: Diagnostic {
19 diagnostic: Diagnostic { 21 range: Range {
20 range: Range { 22 start: Position {
21 start: Position { 23 line: 1,
22 line: 1, 24 character: 4,
23 character: 4, 25 },
26 end: Position {
27 line: 1,
28 character: 26,
29 },
24 }, 30 },
25 end: Position { 31 severity: Some(
26 line: 1, 32 Error,
27 character: 26,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0277",
36 ), 33 ),
37 ), 34 code: Some(
38 source: Some( 35 String(
39 "rustc", 36 "E0277",
40 ), 37 ),
41 message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", 38 ),
42 related_information: None, 39 source: Some(
43 tags: None, 40 "rustc",
41 ),
42 message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
43 related_information: None,
44 tags: None,
45 },
46 fixes: [],
44 }, 47 },
45 fixes: [], 48]
46}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap
index 7b83a7cd0..641da1a58 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap
@@ -2,60 +2,62 @@
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5MappedRustDiagnostic { 5[
6 location: Location { 6 MappedRustDiagnostic {
7 uri: "file:///test/crates/ra_hir_def/src/data.rs", 7 location: Location {
8 range: Range { 8 uri: "file:///test/crates/ra_hir_def/src/data.rs",
9 start: Position { 9 range: Range {
10 line: 79, 10 start: Position {
11 character: 15, 11 line: 79,
12 }, 12 character: 15,
13 end: Position { 13 },
14 line: 79, 14 end: Position {
15 character: 41, 15 line: 79,
16 character: 41,
17 },
16 }, 18 },
17 }, 19 },
18 }, 20 diagnostic: Diagnostic {
19 diagnostic: Diagnostic { 21 range: Range {
20 range: Range { 22 start: Position {
21 start: Position { 23 line: 79,
22 line: 79, 24 character: 15,
23 character: 15, 25 },
24 }, 26 end: Position {
25 end: Position { 27 line: 79,
26 line: 79, 28 character: 41,
27 character: 41, 29 },
28 }, 30 },
29 }, 31 severity: Some(
30 severity: Some( 32 Error,
31 Error, 33 ),
32 ), 34 code: None,
33 code: None, 35 source: Some(
34 source: Some( 36 "rustc",
35 "rustc", 37 ),
36 ), 38 message: "Please register your known path in the path module",
37 message: "Please register your known path in the path module", 39 related_information: Some(
38 related_information: Some( 40 [
39 [ 41 DiagnosticRelatedInformation {
40 DiagnosticRelatedInformation { 42 location: Location {
41 location: Location { 43 uri: "file:///test/crates/ra_hir_def/src/path.rs",
42 uri: "file:///test/crates/ra_hir_def/src/path.rs", 44 range: Range {
43 range: Range { 45 start: Position {
44 start: Position { 46 line: 264,
45 line: 264, 47 character: 8,
46 character: 8, 48 },
47 }, 49 end: Position {
48 end: Position { 50 line: 264,
49 line: 264, 51 character: 76,
50 character: 76, 52 },
51 }, 53 },
52 }, 54 },
55 message: "Error originated from macro here",
53 }, 56 },
54 message: "Error originated from macro here", 57 ],
55 }, 58 ),
56 ], 59 tags: None,
57 ), 60 },
58 tags: None, 61 fixes: [],
59 }, 62 },
60 fixes: [], 63]
61}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap
new file mode 100644
index 000000000..0557a2e79
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap
@@ -0,0 +1,114 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5[
6 MappedRustDiagnostic {
7 location: Location {
8 uri: "file:///test/src/main.rs",
9 range: Range {
10 start: Position {
11 line: 3,
12 character: 4,
13 },
14 end: Position {
15 line: 3,
16 character: 5,
17 },
18 },
19 },
20 diagnostic: Diagnostic {
21 range: Range {
22 start: Position {
23 line: 3,
24 character: 4,
25 },
26 end: Position {
27 line: 3,
28 character: 5,
29 },
30 },
31 severity: Some(
32 Warning,
33 ),
34 code: Some(
35 String(
36 "let_and_return",
37 ),
38 ),
39 source: Some(
40 "clippy",
41 ),
42 message: "returning the result of a let binding from a block\n`#[warn(clippy::let_and_return)]` on by default\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return",
43 related_information: Some(
44 [
45 DiagnosticRelatedInformation {
46 location: Location {
47 uri: "file:///test/src/main.rs",
48 range: Range {
49 start: Position {
50 line: 2,
51 character: 4,
52 },
53 end: Position {
54 line: 2,
55 character: 30,
56 },
57 },
58 },
59 message: "unnecessary let binding",
60 },
61 ],
62 ),
63 tags: None,
64 },
65 fixes: [
66 CodeAction {
67 title: "return the expression directly",
68 kind: Some(
69 "quickfix",
70 ),
71 diagnostics: None,
72 edit: Some(
73 WorkspaceEdit {
74 changes: Some(
75 {
76 "file:///test/src/main.rs": [
77 TextEdit {
78 range: Range {
79 start: Position {
80 line: 2,
81 character: 4,
82 },
83 end: Position {
84 line: 2,
85 character: 30,
86 },
87 },
88 new_text: "",
89 },
90 TextEdit {
91 range: Range {
92 start: Position {
93 line: 3,
94 character: 4,
95 },
96 end: Position {
97 line: 3,
98 character: 5,
99 },
100 },
101 new_text: "(0..10).collect()",
102 },
103 ],
104 },
105 ),
106 document_changes: None,
107 },
108 ),
109 command: None,
110 is_preferred: None,
111 },
112 ],
113 },
114]
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap
index 54679c5db..754bc33a4 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap
@@ -2,45 +2,47 @@
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5MappedRustDiagnostic { 5[
6 location: Location { 6 MappedRustDiagnostic {
7 uri: "file:///test/compiler/ty/list_iter.rs", 7 location: Location {
8 range: Range { 8 uri: "file:///test/compiler/ty/list_iter.rs",
9 start: Position { 9 range: Range {
10 line: 51, 10 start: Position {
11 character: 4, 11 line: 51,
12 }, 12 character: 4,
13 end: Position { 13 },
14 line: 51, 14 end: Position {
15 character: 47, 15 line: 51,
16 character: 47,
17 },
16 }, 18 },
17 }, 19 },
18 }, 20 diagnostic: Diagnostic {
19 diagnostic: Diagnostic { 21 range: Range {
20 range: Range { 22 start: Position {
21 start: Position { 23 line: 51,
22 line: 51, 24 character: 4,
23 character: 4, 25 },
26 end: Position {
27 line: 51,
28 character: 47,
29 },
24 }, 30 },
25 end: Position { 31 severity: Some(
26 line: 51, 32 Error,
27 character: 47,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0053",
36 ), 33 ),
37 ), 34 code: Some(
38 source: Some( 35 String(
39 "rustc", 36 "E0053",
40 ), 37 ),
41 message: "method `next` has an incompatible type for trait\nexpected type `fn(&mut ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&\'list ty::Ref<M>>`", 38 ),
42 related_information: None, 39 source: Some(
43 tags: None, 40 "rustc",
41 ),
42 message: "method `next` has an incompatible type for trait\nexpected type `fn(&mut ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&\'list ty::Ref<M>>`",
43 related_information: None,
44 tags: None,
45 },
46 fixes: [],
44 }, 47 },
45 fixes: [], 48]
46}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap
index 57df4ceaf..78b7f7cc8 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap
@@ -2,45 +2,47 @@
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5MappedRustDiagnostic { 5[
6 location: Location { 6 MappedRustDiagnostic {
7 uri: "file:///test/runtime/compiler_support.rs", 7 location: Location {
8 range: Range { 8 uri: "file:///test/runtime/compiler_support.rs",
9 start: Position { 9 range: Range {
10 line: 47, 10 start: Position {
11 character: 64, 11 line: 47,
12 }, 12 character: 64,
13 end: Position { 13 },
14 line: 47, 14 end: Position {
15 character: 69, 15 line: 47,
16 character: 69,
17 },
16 }, 18 },
17 }, 19 },
18 }, 20 diagnostic: Diagnostic {
19 diagnostic: Diagnostic { 21 range: Range {
20 range: Range { 22 start: Position {
21 start: Position { 23 line: 47,
22 line: 47, 24 character: 64,
23 character: 64, 25 },
26 end: Position {
27 line: 47,
28 character: 69,
29 },
24 }, 30 },
25 end: Position { 31 severity: Some(
26 line: 47, 32 Error,
27 character: 69,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0308",
36 ), 33 ),
37 ), 34 code: Some(
38 source: Some( 35 String(
39 "rustc", 36 "E0308",
40 ), 37 ),
41 message: "mismatched types\nexpected usize, found u32", 38 ),
42 related_information: None, 39 source: Some(
43 tags: None, 40 "rustc",
41 ),
42 message: "mismatched types\nexpected usize, found u32",
43 related_information: None,
44 tags: None,
45 },
46 fixes: [],
44 }, 47 },
45 fixes: [], 48]
46}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap
index 3e1fe736c..5989ed202 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap
@@ -2,83 +2,85 @@
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5MappedRustDiagnostic { 5[
6 location: Location { 6 MappedRustDiagnostic {
7 uri: "file:///test/driver/subcommand/repl.rs", 7 location: Location {
8 range: Range { 8 uri: "file:///test/driver/subcommand/repl.rs",
9 start: Position { 9 range: Range {
10 line: 290, 10 start: Position {
11 character: 8, 11 line: 290,
12 }, 12 character: 8,
13 end: Position { 13 },
14 line: 290, 14 end: Position {
15 character: 11, 15 line: 290,
16 character: 11,
17 },
16 }, 18 },
17 }, 19 },
18 }, 20 diagnostic: Diagnostic {
19 diagnostic: Diagnostic { 21 range: Range {
20 range: Range { 22 start: Position {
21 start: Position { 23 line: 290,
22 line: 290, 24 character: 8,
23 character: 8, 25 },
24 }, 26 end: Position {
25 end: Position { 27 line: 290,
26 line: 290, 28 character: 11,
27 character: 11, 29 },
28 }, 30 },
29 }, 31 severity: Some(
30 severity: Some( 32 Warning,
31 Warning,
32 ),
33 code: Some(
34 String(
35 "unused_variables",
36 ), 33 ),
37 ), 34 code: Some(
38 source: Some( 35 String(
39 "rustc", 36 "unused_variables",
40 ), 37 ),
41 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
42 related_information: None,
43 tags: Some(
44 [
45 Unnecessary,
46 ],
47 ),
48 },
49 fixes: [
50 CodeAction {
51 title: "consider prefixing with an underscore: \'_foo\'",
52 kind: Some(
53 "quickfix",
54 ), 38 ),
55 diagnostics: None, 39 source: Some(
56 edit: Some( 40 "rustc",
57 WorkspaceEdit { 41 ),
58 changes: Some( 42 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
59 { 43 related_information: None,
60 "file:///test/driver/subcommand/repl.rs": [ 44 tags: Some(
61 TextEdit { 45 [
62 range: Range { 46 Unnecessary,
63 start: Position { 47 ],
64 line: 290,
65 character: 8,
66 },
67 end: Position {
68 line: 290,
69 character: 11,
70 },
71 },
72 new_text: "_foo",
73 },
74 ],
75 },
76 ),
77 document_changes: None,
78 },
79 ), 48 ),
80 command: None,
81 is_preferred: None,
82 }, 49 },
83 ], 50 fixes: [
84} 51 CodeAction {
52 title: "consider prefixing with an underscore",
53 kind: Some(
54 "quickfix",
55 ),
56 diagnostics: None,
57 edit: Some(
58 WorkspaceEdit {
59 changes: Some(
60 {
61 "file:///test/driver/subcommand/repl.rs": [
62 TextEdit {
63 range: Range {
64 start: Position {
65 line: 290,
66 character: 8,
67 },
68 end: Position {
69 line: 290,
70 character: 11,
71 },
72 },
73 new_text: "_foo",
74 },
75 ],
76 },
77 ),
78 document_changes: None,
79 },
80 ),
81 command: None,
82 is_preferred: None,
83 },
84 ],
85 },
86]
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap
index 69301078d..e34b546dc 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap
@@ -2,64 +2,66 @@
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5MappedRustDiagnostic { 5[
6 location: Location { 6 MappedRustDiagnostic {
7 uri: "file:///test/compiler/ty/select.rs", 7 location: Location {
8 range: Range { 8 uri: "file:///test/compiler/ty/select.rs",
9 start: Position { 9 range: Range {
10 line: 103, 10 start: Position {
11 character: 17, 11 line: 103,
12 }, 12 character: 17,
13 end: Position { 13 },
14 line: 103, 14 end: Position {
15 character: 29, 15 line: 103,
16 character: 29,
17 },
16 }, 18 },
17 }, 19 },
18 }, 20 diagnostic: Diagnostic {
19 diagnostic: Diagnostic { 21 range: Range {
20 range: Range { 22 start: Position {
21 start: Position { 23 line: 103,
22 line: 103, 24 character: 17,
23 character: 17, 25 },
24 }, 26 end: Position {
25 end: Position { 27 line: 103,
26 line: 103, 28 character: 29,
27 character: 29, 29 },
28 }, 30 },
29 }, 31 severity: Some(
30 severity: Some( 32 Error,
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0061",
36 ), 33 ),
37 ), 34 code: Some(
38 source: Some( 35 String(
39 "rustc", 36 "E0061",
40 ), 37 ),
41 message: "this function takes 2 parameters but 3 parameters were supplied\nexpected 2 parameters", 38 ),
42 related_information: Some( 39 source: Some(
43 [ 40 "rustc",
44 DiagnosticRelatedInformation { 41 ),
45 location: Location { 42 message: "this function takes 2 parameters but 3 parameters were supplied\nexpected 2 parameters",
46 uri: "file:///test/compiler/ty/select.rs", 43 related_information: Some(
47 range: Range { 44 [
48 start: Position { 45 DiagnosticRelatedInformation {
49 line: 218, 46 location: Location {
50 character: 4, 47 uri: "file:///test/compiler/ty/select.rs",
51 }, 48 range: Range {
52 end: Position { 49 start: Position {
53 line: 230, 50 line: 218,
54 character: 5, 51 character: 4,
52 },
53 end: Position {
54 line: 230,
55 character: 5,
56 },
55 }, 57 },
56 }, 58 },
59 message: "defined here",
57 }, 60 },
58 message: "defined here", 61 ],
59 }, 62 ),
60 ], 63 tags: None,
61 ), 64 },
62 tags: None, 65 fixes: [],
63 }, 66 },
64 fixes: [], 67]
65}
diff --git a/crates/ra_cargo_watch/src/conv/test.rs b/crates/ra_cargo_watch/src/conv/test.rs
index 6b86525b8..4e81455ca 100644
--- a/crates/ra_cargo_watch/src/conv/test.rs
+++ b/crates/ra_cargo_watch/src/conv/test.rs
@@ -58,7 +58,7 @@ fn snap_rustc_incompatible_type_for_trait() {
58 ); 58 );
59 59
60 let workspace_root = PathBuf::from("/test/"); 60 let workspace_root = PathBuf::from("/test/");
61 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); 61 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
62 insta::assert_debug_snapshot!(diag); 62 insta::assert_debug_snapshot!(diag);
63} 63}
64 64
@@ -141,7 +141,7 @@ fn snap_rustc_unused_variable() {
141 ); 141 );
142 142
143 let workspace_root = PathBuf::from("/test/"); 143 let workspace_root = PathBuf::from("/test/");
144 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); 144 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
145 insta::assert_debug_snapshot!(diag); 145 insta::assert_debug_snapshot!(diag);
146} 146}
147 147
@@ -266,7 +266,7 @@ fn snap_rustc_wrong_number_of_parameters() {
266 ); 266 );
267 267
268 let workspace_root = PathBuf::from("/test/"); 268 let workspace_root = PathBuf::from("/test/");
269 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); 269 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
270 insta::assert_debug_snapshot!(diag); 270 insta::assert_debug_snapshot!(diag);
271} 271}
272 272
@@ -387,7 +387,7 @@ fn snap_clippy_pass_by_ref() {
387 ); 387 );
388 388
389 let workspace_root = PathBuf::from("/test/"); 389 let workspace_root = PathBuf::from("/test/");
390 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); 390 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
391 insta::assert_debug_snapshot!(diag); 391 insta::assert_debug_snapshot!(diag);
392} 392}
393 393
@@ -431,7 +431,7 @@ fn snap_rustc_mismatched_type() {
431 ); 431 );
432 432
433 let workspace_root = PathBuf::from("/test/"); 433 let workspace_root = PathBuf::from("/test/");
434 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); 434 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
435 insta::assert_debug_snapshot!(diag); 435 insta::assert_debug_snapshot!(diag);
436} 436}
437 437
@@ -703,7 +703,7 @@ fn snap_handles_macro_location() {
703 ); 703 );
704 704
705 let workspace_root = PathBuf::from("/test/"); 705 let workspace_root = PathBuf::from("/test/");
706 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); 706 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
707 insta::assert_debug_snapshot!(diag); 707 insta::assert_debug_snapshot!(diag);
708} 708}
709 709
@@ -933,6 +933,140 @@ fn snap_macro_compiler_error() {
933 ); 933 );
934 934
935 let workspace_root = PathBuf::from("/test/"); 935 let workspace_root = PathBuf::from("/test/");
936 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); 936 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
937 insta::assert_debug_snapshot!(diag);
938}
939
940#[test]
941#[cfg(not(windows))]
942fn snap_multi_line_fix() {
943 let diag = parse_diagnostic(
944 r##"{
945 "rendered": "warning: returning the result of a let binding from a block\n --> src/main.rs:4:5\n |\n3 | let a = (0..10).collect();\n | -------------------------- unnecessary let binding\n4 | a\n | ^\n |\n = note: `#[warn(clippy::let_and_return)]` on by default\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return\nhelp: return the expression directly\n |\n3 | \n4 | (0..10).collect()\n |\n\n",
946 "children": [
947 {
948 "children": [],
949 "code": null,
950 "level": "note",
951 "message": "`#[warn(clippy::let_and_return)]` on by default",
952 "rendered": null,
953 "spans": []
954 },
955 {
956 "children": [],
957 "code": null,
958 "level": "help",
959 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return",
960 "rendered": null,
961 "spans": []
962 },
963 {
964 "children": [],
965 "code": null,
966 "level": "help",
967 "message": "return the expression directly",
968 "rendered": null,
969 "spans": [
970 {
971 "byte_end": 55,
972 "byte_start": 29,
973 "column_end": 31,
974 "column_start": 5,
975 "expansion": null,
976 "file_name": "src/main.rs",
977 "is_primary": true,
978 "label": null,
979 "line_end": 3,
980 "line_start": 3,
981 "suggested_replacement": "",
982 "suggestion_applicability": "MachineApplicable",
983 "text": [
984 {
985 "highlight_end": 31,
986 "highlight_start": 5,
987 "text": " let a = (0..10).collect();"
988 }
989 ]
990 },
991 {
992 "byte_end": 61,
993 "byte_start": 60,
994 "column_end": 6,
995 "column_start": 5,
996 "expansion": null,
997 "file_name": "src/main.rs",
998 "is_primary": true,
999 "label": null,
1000 "line_end": 4,
1001 "line_start": 4,
1002 "suggested_replacement": "(0..10).collect()",
1003 "suggestion_applicability": "MachineApplicable",
1004 "text": [
1005 {
1006 "highlight_end": 6,
1007 "highlight_start": 5,
1008 "text": " a"
1009 }
1010 ]
1011 }
1012 ]
1013 }
1014 ],
1015 "code": {
1016 "code": "clippy::let_and_return",
1017 "explanation": null
1018 },
1019 "level": "warning",
1020 "message": "returning the result of a let binding from a block",
1021 "spans": [
1022 {
1023 "byte_end": 55,
1024 "byte_start": 29,
1025 "column_end": 31,
1026 "column_start": 5,
1027 "expansion": null,
1028 "file_name": "src/main.rs",
1029 "is_primary": false,
1030 "label": "unnecessary let binding",
1031 "line_end": 3,
1032 "line_start": 3,
1033 "suggested_replacement": null,
1034 "suggestion_applicability": null,
1035 "text": [
1036 {
1037 "highlight_end": 31,
1038 "highlight_start": 5,
1039 "text": " let a = (0..10).collect();"
1040 }
1041 ]
1042 },
1043 {
1044 "byte_end": 61,
1045 "byte_start": 60,
1046 "column_end": 6,
1047 "column_start": 5,
1048 "expansion": null,
1049 "file_name": "src/main.rs",
1050 "is_primary": true,
1051 "label": null,
1052 "line_end": 4,
1053 "line_start": 4,
1054 "suggested_replacement": null,
1055 "suggestion_applicability": null,
1056 "text": [
1057 {
1058 "highlight_end": 6,
1059 "highlight_start": 5,
1060 "text": " a"
1061 }
1062 ]
1063 }
1064 ]
1065 }
1066 "##,
1067 );
1068
1069 let workspace_root = PathBuf::from("/test/");
1070 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
937 insta::assert_debug_snapshot!(diag); 1071 insta::assert_debug_snapshot!(diag);
938} 1072}
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs
index f07c34549..94b9c03d0 100644
--- a/crates/ra_cargo_watch/src/lib.rs
+++ b/crates/ra_cargo_watch/src/lib.rs
@@ -197,23 +197,23 @@ impl CheckWatcherThread {
197 } 197 }
198 198
199 CheckEvent::Msg(Message::CompilerMessage(msg)) => { 199 CheckEvent::Msg(Message::CompilerMessage(msg)) => {
200 let map_result = 200 let map_result = map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root);
201 match map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root) { 201 if map_result.is_empty() {
202 Some(map_result) => map_result, 202 return;
203 None => return, 203 }
204 };
205
206 let MappedRustDiagnostic { location, diagnostic, fixes } = map_result;
207 let fixes = fixes
208 .into_iter()
209 .map(|fix| {
210 CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into()
211 })
212 .collect();
213 204
214 task_send 205 for MappedRustDiagnostic { location, diagnostic, fixes } in map_result {
215 .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes }) 206 let fixes = fixes
216 .unwrap(); 207 .into_iter()
208 .map(|fix| {
209 CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into()
210 })
211 .collect();
212
213 task_send
214 .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes })
215 .unwrap();
216 }
217 } 217 }
218 218
219 CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {} 219 CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index 947d6ad56..3dc86ca2d 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -5,7 +5,7 @@ use std::sync::Arc;
5 5
6use ra_cfg::CfgOptions; 6use ra_cfg::CfgOptions;
7use rustc_hash::FxHashMap; 7use rustc_hash::FxHashMap;
8use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; 8use test_utils::{extract_offset, parse_fixture, parse_single_fixture, CURSOR_MARKER};
9 9
10use crate::{ 10use crate::{
11 input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf, 11 input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf,
@@ -45,23 +45,45 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
45 45
46impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {} 46impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {}
47 47
48fn with_single_file(db: &mut dyn SourceDatabaseExt, text: &str) -> FileId { 48fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId {
49 let file_id = FileId(0); 49 let file_id = FileId(0);
50 let rel_path: RelativePathBuf = "/main.rs".into(); 50 let rel_path: RelativePathBuf = "/main.rs".into();
51 51
52 let mut source_root = SourceRoot::new_local(); 52 let mut source_root = SourceRoot::new_local();
53 source_root.insert_file(rel_path.clone(), file_id); 53 source_root.insert_file(rel_path.clone(), file_id);
54 54
55 let mut crate_graph = CrateGraph::default(); 55 let fixture = parse_single_fixture(ra_fixture);
56 crate_graph.add_crate_root( 56
57 file_id, 57 let crate_graph = if let Some(entry) = fixture {
58 Edition::Edition2018, 58 let meta = match parse_meta(&entry.meta) {
59 None, 59 ParsedMeta::File(it) => it,
60 CfgOptions::default(), 60 _ => panic!("with_single_file only support file meta"),
61 Env::default(), 61 };
62 ); 62
63 63 let mut crate_graph = CrateGraph::default();
64 db.set_file_text(file_id, Arc::new(text.to_string())); 64 crate_graph.add_crate_root(
65 file_id,
66 meta.edition,
67 meta.krate,
68 meta.cfg,
69 meta.env,
70 Default::default(),
71 );
72 crate_graph
73 } else {
74 let mut crate_graph = CrateGraph::default();
75 crate_graph.add_crate_root(
76 file_id,
77 Edition::Edition2018,
78 None,
79 CfgOptions::default(),
80 Env::default(),
81 Default::default(),
82 );
83 crate_graph
84 };
85
86 db.set_file_text(file_id, Arc::new(ra_fixture.to_string()));
65 db.set_file_relative_path(file_id, rel_path); 87 db.set_file_relative_path(file_id, rel_path);
66 db.set_file_source_root(file_id, WORKSPACE); 88 db.set_file_source_root(file_id, WORKSPACE);
67 db.set_source_root(WORKSPACE, Arc::new(source_root)); 89 db.set_source_root(WORKSPACE, Arc::new(source_root));
@@ -104,7 +126,8 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
104 meta.edition, 126 meta.edition,
105 Some(krate.clone()), 127 Some(krate.clone()),
106 meta.cfg, 128 meta.cfg,
107 Env::default(), 129 meta.env,
130 Default::default(),
108 ); 131 );
109 let prev = crates.insert(krate.clone(), crate_id); 132 let prev = crates.insert(krate.clone(), crate_id);
110 assert!(prev.is_none()); 133 assert!(prev.is_none());
@@ -141,6 +164,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
141 None, 164 None,
142 CfgOptions::default(), 165 CfgOptions::default(),
143 Env::default(), 166 Env::default(),
167 Default::default(),
144 ); 168 );
145 } else { 169 } else {
146 for (from, to) in crate_deps { 170 for (from, to) in crate_deps {
@@ -167,9 +191,10 @@ struct FileMeta {
167 deps: Vec<String>, 191 deps: Vec<String>,
168 cfg: CfgOptions, 192 cfg: CfgOptions,
169 edition: Edition, 193 edition: Edition,
194 env: Env,
170} 195}
171 196
172//- /lib.rs crate:foo deps:bar,baz 197//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo)
173fn parse_meta(meta: &str) -> ParsedMeta { 198fn parse_meta(meta: &str) -> ParsedMeta {
174 let components = meta.split_ascii_whitespace().collect::<Vec<_>>(); 199 let components = meta.split_ascii_whitespace().collect::<Vec<_>>();
175 200
@@ -186,6 +211,7 @@ fn parse_meta(meta: &str) -> ParsedMeta {
186 let mut deps = Vec::new(); 211 let mut deps = Vec::new();
187 let mut edition = Edition::Edition2018; 212 let mut edition = Edition::Edition2018;
188 let mut cfg = CfgOptions::default(); 213 let mut cfg = CfgOptions::default();
214 let mut env = Env::default();
189 for component in components[1..].iter() { 215 for component in components[1..].iter() {
190 let (key, value) = split1(component, ':').unwrap(); 216 let (key, value) = split1(component, ':').unwrap();
191 match key { 217 match key {
@@ -200,11 +226,18 @@ fn parse_meta(meta: &str) -> ParsedMeta {
200 } 226 }
201 } 227 }
202 } 228 }
229 "env" => {
230 for key in value.split(',') {
231 if let Some((k, v)) = split1(key, '=') {
232 env.set(k.into(), v.into());
233 }
234 }
235 }
203 _ => panic!("bad component: {:?}", component), 236 _ => panic!("bad component: {:?}", component),
204 } 237 }
205 } 238 }
206 239
207 ParsedMeta::File(FileMeta { path, krate, deps, edition, cfg }) 240 ParsedMeta::File(FileMeta { path, krate, deps, edition, cfg, env })
208} 241}
209 242
210fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> { 243fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index b77640b2b..06d40db96 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -113,6 +113,7 @@ pub struct CrateData {
113 pub display_name: Option<String>, 113 pub display_name: Option<String>,
114 pub cfg_options: CfgOptions, 114 pub cfg_options: CfgOptions,
115 pub env: Env, 115 pub env: Env,
116 pub extern_source: ExternSource,
116 pub dependencies: Vec<Dependency>, 117 pub dependencies: Vec<Dependency>,
117} 118}
118 119
@@ -122,11 +123,22 @@ pub enum Edition {
122 Edition2015, 123 Edition2015,
123} 124}
124 125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
127pub struct ExternSourceId(pub u32);
128
125#[derive(Default, Debug, Clone, PartialEq, Eq)] 129#[derive(Default, Debug, Clone, PartialEq, Eq)]
126pub struct Env { 130pub struct Env {
127 entries: FxHashMap<String, String>, 131 entries: FxHashMap<String, String>,
128} 132}
129 133
134// FIXME: Redesign vfs for solve the following limitation ?
135// Note: Some env variables (e.g. OUT_DIR) are located outside of the
136// crate. We store a map to allow remap it to ExternSourceId
137#[derive(Default, Debug, Clone, PartialEq, Eq)]
138pub struct ExternSource {
139 extern_paths: FxHashMap<String, ExternSourceId>,
140}
141
130#[derive(Debug, Clone, PartialEq, Eq)] 142#[derive(Debug, Clone, PartialEq, Eq)]
131pub struct Dependency { 143pub struct Dependency {
132 pub crate_id: CrateId, 144 pub crate_id: CrateId,
@@ -141,6 +153,7 @@ impl CrateGraph {
141 display_name: Option<String>, 153 display_name: Option<String>,
142 cfg_options: CfgOptions, 154 cfg_options: CfgOptions,
143 env: Env, 155 env: Env,
156 extern_source: ExternSource,
144 ) -> CrateId { 157 ) -> CrateId {
145 let data = CrateData { 158 let data = CrateData {
146 root_file_id: file_id, 159 root_file_id: file_id,
@@ -148,6 +161,7 @@ impl CrateGraph {
148 display_name, 161 display_name,
149 cfg_options, 162 cfg_options,
150 env, 163 env,
164 extern_source,
151 dependencies: Vec::new(), 165 dependencies: Vec::new(),
152 }; 166 };
153 let crate_id = CrateId(self.arena.len() as u32); 167 let crate_id = CrateId(self.arena.len() as u32);
@@ -261,6 +275,37 @@ impl fmt::Display for Edition {
261 } 275 }
262} 276}
263 277
278impl Env {
279 pub fn set(&mut self, env: &str, value: String) {
280 self.entries.insert(env.to_owned(), value);
281 }
282
283 pub fn get(&self, env: &str) -> Option<String> {
284 self.entries.get(env).cloned()
285 }
286}
287
288impl ExternSource {
289 pub fn extern_path(&self, path: &str) -> Option<(ExternSourceId, RelativePathBuf)> {
290 self.extern_paths.iter().find_map(|(root_path, id)| {
291 if path.starts_with(root_path) {
292 let mut rel_path = &path[root_path.len()..];
293 if rel_path.starts_with("/") {
294 rel_path = &rel_path[1..];
295 }
296 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
297 Some((id.clone(), rel_path))
298 } else {
299 None
300 }
301 })
302 }
303
304 pub fn set_extern_path(&mut self, root_path: &str, root: ExternSourceId) {
305 self.extern_paths.insert(root_path.to_owned(), root);
306 }
307}
308
264#[derive(Debug)] 309#[derive(Debug)]
265pub struct ParseEditionError { 310pub struct ParseEditionError {
266 invalid_input: String, 311 invalid_input: String,
@@ -290,6 +335,7 @@ mod tests {
290 None, 335 None,
291 CfgOptions::default(), 336 CfgOptions::default(),
292 Env::default(), 337 Env::default(),
338 Default::default(),
293 ); 339 );
294 let crate2 = graph.add_crate_root( 340 let crate2 = graph.add_crate_root(
295 FileId(2u32), 341 FileId(2u32),
@@ -297,6 +343,7 @@ mod tests {
297 None, 343 None,
298 CfgOptions::default(), 344 CfgOptions::default(),
299 Env::default(), 345 Env::default(),
346 Default::default(),
300 ); 347 );
301 let crate3 = graph.add_crate_root( 348 let crate3 = graph.add_crate_root(
302 FileId(3u32), 349 FileId(3u32),
@@ -304,6 +351,7 @@ mod tests {
304 None, 351 None,
305 CfgOptions::default(), 352 CfgOptions::default(),
306 Env::default(), 353 Env::default(),
354 Default::default(),
307 ); 355 );
308 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); 356 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
309 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); 357 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
@@ -319,6 +367,7 @@ mod tests {
319 None, 367 None,
320 CfgOptions::default(), 368 CfgOptions::default(),
321 Env::default(), 369 Env::default(),
370 Default::default(),
322 ); 371 );
323 let crate2 = graph.add_crate_root( 372 let crate2 = graph.add_crate_root(
324 FileId(2u32), 373 FileId(2u32),
@@ -326,6 +375,7 @@ mod tests {
326 None, 375 None,
327 CfgOptions::default(), 376 CfgOptions::default(),
328 Env::default(), 377 Env::default(),
378 Default::default(),
329 ); 379 );
330 let crate3 = graph.add_crate_root( 380 let crate3 = graph.add_crate_root(
331 FileId(3u32), 381 FileId(3u32),
@@ -333,6 +383,7 @@ mod tests {
333 None, 383 None,
334 CfgOptions::default(), 384 CfgOptions::default(),
335 Env::default(), 385 Env::default(),
386 Default::default(),
336 ); 387 );
337 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); 388 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
338 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); 389 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
@@ -347,6 +398,7 @@ mod tests {
347 None, 398 None,
348 CfgOptions::default(), 399 CfgOptions::default(),
349 Env::default(), 400 Env::default(),
401 Default::default(),
350 ); 402 );
351 let crate2 = graph.add_crate_root( 403 let crate2 = graph.add_crate_root(
352 FileId(2u32), 404 FileId(2u32),
@@ -354,6 +406,7 @@ mod tests {
354 None, 406 None,
355 CfgOptions::default(), 407 CfgOptions::default(),
356 Env::default(), 408 Env::default(),
409 Default::default(),
357 ); 410 );
358 assert!(graph 411 assert!(graph
359 .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) 412 .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2)
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index fb002d717..d500d5e85 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -11,7 +11,8 @@ use ra_syntax::{ast, Parse, SourceFile, TextRange, TextUnit};
11pub use crate::{ 11pub use crate::{
12 cancellation::Canceled, 12 cancellation::Canceled,
13 input::{ 13 input::{
14 CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, SourceRoot, SourceRootId, 14 CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId,
15 FileId, SourceRoot, SourceRootId,
15 }, 16 },
16}; 17};
17pub use relative_path::{RelativePath, RelativePathBuf}; 18pub use relative_path::{RelativePath, RelativePathBuf};
@@ -87,6 +88,12 @@ pub trait FileLoader {
87 fn resolve_relative_path(&self, anchor: FileId, relative_path: &RelativePath) 88 fn resolve_relative_path(&self, anchor: FileId, relative_path: &RelativePath)
88 -> Option<FileId>; 89 -> Option<FileId>;
89 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>; 90 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>;
91
92 fn resolve_extern_path(
93 &self,
94 extern_id: ExternSourceId,
95 relative_path: &RelativePath,
96 ) -> Option<FileId>;
90} 97}
91 98
92/// Database which stores all significant input facts: source code and project 99/// Database which stores all significant input facts: source code and project
@@ -164,4 +171,13 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
164 let source_root = self.0.file_source_root(file_id); 171 let source_root = self.0.file_source_root(file_id);
165 self.0.source_root_crates(source_root) 172 self.0.source_root_crates(source_root)
166 } 173 }
174
175 fn resolve_extern_path(
176 &self,
177 extern_id: ExternSourceId,
178 relative_path: &RelativePath,
179 ) -> Option<FileId> {
180 let source_root = self.0.source_root(SourceRootId(extern_id.0));
181 source_root.file_by_relative_path(&relative_path)
182 }
167} 183}
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml
index 266c4cff3..42193b492 100644
--- a/crates/ra_hir/Cargo.toml
+++ b/crates/ra_hir/Cargo.toml
@@ -11,6 +11,7 @@ doctest = false
11log = "0.4.8" 11log = "0.4.8"
12rustc-hash = "1.1.0" 12rustc-hash = "1.1.0"
13either = "1.5.3" 13either = "1.5.3"
14arrayvec = "0.5.1"
14 15
15itertools = "0.8.2" 16itertools = "0.8.2"
16 17
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 41d4e2ed3..ff041150b 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -1,6 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use arrayvec::ArrayVec;
4use either::Either; 5use either::Either;
5use hir_def::{ 6use hir_def::{
6 adt::StructKind, 7 adt::StructKind,
@@ -226,7 +227,9 @@ impl Module {
226 Some((name, def)) 227 Some((name, def))
227 } 228 }
228 }) 229 })
229 .map(|(name, def)| (name.clone(), def.into())) 230 .flat_map(|(name, def)| {
231 ScopeDef::all_items(def).into_iter().map(move |item| (name.clone(), item))
232 })
230 .collect() 233 .collect()
231 } 234 }
232 235
@@ -308,7 +311,11 @@ impl StructField {
308 self.parent.variant_data(db).fields()[self.id].name.clone() 311 self.parent.variant_data(db).fields()[self.id].name.clone()
309 } 312 }
310 313
311 pub fn ty(&self, db: &impl HirDatabase) -> Type { 314 /// Returns the type as in the signature of the struct (i.e., with
315 /// placeholder types for type parameters). This is good for showing
316 /// signature help, but not so good to actually get the type of the field
317 /// when you actually have a variable of the struct.
318 pub fn signature_ty(&self, db: &impl HirDatabase) -> Type {
312 let var_id = self.parent.into(); 319 let var_id = self.parent.into();
313 let generic_def_id: GenericDefId = match self.parent { 320 let generic_def_id: GenericDefId = match self.parent {
314 VariantDef::Struct(it) => it.id.into(), 321 VariantDef::Struct(it) => it.id.into(),
@@ -482,6 +489,10 @@ impl Adt {
482 let subst = db.generic_defaults(self.into()); 489 let subst = db.generic_defaults(self.into());
483 subst.iter().any(|ty| ty == &Ty::Unknown) 490 subst.iter().any(|ty| ty == &Ty::Unknown)
484 } 491 }
492
493 /// Turns this ADT into a type. Any type parameters of the ADT will be
494 /// turned into unknown types, which is good for e.g. finding the most
495 /// general set of completions, but will not look very nice when printed.
485 pub fn ty(self, db: &impl HirDatabase) -> Type { 496 pub fn ty(self, db: &impl HirDatabase) -> Type {
486 let id = AdtId::from(self); 497 let id = AdtId::from(self);
487 Type::from_def(db, id.module(db).krate, id) 498 Type::from_def(db, id.module(db).krate, id)
@@ -1028,7 +1039,7 @@ impl Type {
1028 krate: CrateId, 1039 krate: CrateId,
1029 def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>, 1040 def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>,
1030 ) -> Type { 1041 ) -> Type {
1031 let substs = Substs::type_params(db, def); 1042 let substs = Substs::build_for_def(db, def).fill_with_unknown().build();
1032 let ty = db.ty(def.into()).subst(&substs); 1043 let ty = db.ty(def.into()).subst(&substs);
1033 Type::new(db, krate, def, ty) 1044 Type::new(db, krate, def, ty)
1034 } 1045 }
@@ -1288,15 +1299,36 @@ pub enum ScopeDef {
1288 Unknown, 1299 Unknown,
1289} 1300}
1290 1301
1291impl From<PerNs> for ScopeDef { 1302impl ScopeDef {
1292 fn from(def: PerNs) -> Self { 1303 pub fn all_items(def: PerNs) -> ArrayVec<[Self; 3]> {
1293 def.take_types() 1304 let mut items = ArrayVec::new();
1294 .or_else(|| def.take_values()) 1305
1295 .map(|module_def_id| ScopeDef::ModuleDef(module_def_id.into())) 1306 match (def.take_types(), def.take_values()) {
1296 .or_else(|| { 1307 (Some(m1), None) => items.push(ScopeDef::ModuleDef(m1.into())),
1297 def.take_macros().map(|macro_def_id| ScopeDef::MacroDef(macro_def_id.into())) 1308 (None, Some(m2)) => items.push(ScopeDef::ModuleDef(m2.into())),
1298 }) 1309 (Some(m1), Some(m2)) => {
1299 .unwrap_or(ScopeDef::Unknown) 1310 // Some items, like unit structs and enum variants, are
1311 // returned as both a type and a value. Here we want
1312 // to de-duplicate them.
1313 if m1 != m2 {
1314 items.push(ScopeDef::ModuleDef(m1.into()));
1315 items.push(ScopeDef::ModuleDef(m2.into()));
1316 } else {
1317 items.push(ScopeDef::ModuleDef(m1.into()));
1318 }
1319 }
1320 (None, None) => {}
1321 };
1322
1323 if let Some(macro_def_id) = def.take_macros() {
1324 items.push(ScopeDef::MacroDef(macro_def_id.into()));
1325 }
1326
1327 if items.is_empty() {
1328 items.push(ScopeDef::Unknown);
1329 }
1330
1331 items
1300 } 1332 }
1301} 1333}
1302 1334
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 3782a9984..788bb3eb7 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -344,7 +344,13 @@ impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> {
344 344
345 resolver.process_all_names(self.db, &mut |name, def| { 345 resolver.process_all_names(self.db, &mut |name, def| {
346 let def = match def { 346 let def = match def {
347 resolver::ScopeDef::PerNs(it) => it.into(), 347 resolver::ScopeDef::PerNs(it) => {
348 let items = ScopeDef::all_items(it);
349 for item in items {
350 f(name.clone(), item);
351 }
352 return;
353 }
348 resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), 354 resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()),
349 resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), 355 resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()),
350 resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), 356 resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }),
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 57ba45b45..2bc405a59 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -47,13 +47,19 @@ impl Expander {
47 pub(crate) fn enter_expand<T: ast::AstNode, DB: DefDatabase>( 47 pub(crate) fn enter_expand<T: ast::AstNode, DB: DefDatabase>(
48 &mut self, 48 &mut self,
49 db: &DB, 49 db: &DB,
50 local_scope: Option<&ItemScope>,
50 macro_call: ast::MacroCall, 51 macro_call: ast::MacroCall,
51 ) -> Option<(Mark, T)> { 52 ) -> Option<(Mark, T)> {
52 let macro_call = InFile::new(self.current_file_id, &macro_call); 53 let macro_call = InFile::new(self.current_file_id, &macro_call);
53 54
54 if let Some(call_id) = 55 if let Some(call_id) = macro_call.as_call_id(db, |path| {
55 macro_call.as_call_id(db, |path| self.resolve_path_as_macro(db, &path)) 56 if let Some(local_scope) = local_scope {
56 { 57 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
58 return Some(def);
59 }
60 }
61 self.resolve_path_as_macro(db, &path)
62 }) {
57 let file_id = call_id.as_file(); 63 let file_id = call_id.as_file();
58 if let Some(node) = db.parse_or_expand(file_id) { 64 if let Some(node) = db.parse_or_expand(file_id) {
59 if let Some(expr) = T::cast(node) { 65 if let Some(expr) = T::cast(node) {
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index ec1b0c2e7..54b5591d3 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -3,7 +3,10 @@
3 3
4use either::Either; 4use either::Either;
5 5
6use hir_expand::name::{name, AsName, Name}; 6use hir_expand::{
7 name::{name, AsName, Name},
8 MacroDefId, MacroDefKind,
9};
7use ra_arena::Arena; 10use ra_arena::Arena;
8use ra_syntax::{ 11use ra_syntax::{
9 ast::{ 12 ast::{
@@ -452,19 +455,30 @@ where
452 None => self.alloc_expr(Expr::Missing, syntax_ptr), 455 None => self.alloc_expr(Expr::Missing, syntax_ptr),
453 } 456 }
454 } 457 }
455 // FIXME expand to statements in statement position
456 ast::Expr::MacroCall(e) => { 458 ast::Expr::MacroCall(e) => {
457 let macro_call = self.expander.to_source(AstPtr::new(&e)); 459 if let Some(name) = is_macro_rules(&e) {
458 match self.expander.enter_expand(self.db, e) { 460 let mac = MacroDefId {
459 Some((mark, expansion)) => { 461 krate: Some(self.expander.module.krate),
460 self.source_map 462 ast_id: Some(self.expander.ast_id(&e)),
461 .expansions 463 kind: MacroDefKind::Declarative,
462 .insert(macro_call, self.expander.current_file_id); 464 };
463 let id = self.collect_expr(expansion); 465 self.body.item_scope.define_legacy_macro(name, mac);
464 self.expander.exit(self.db, mark); 466
465 id 467 // FIXME: do we still need to allocate this as missing ?
468 self.alloc_expr(Expr::Missing, syntax_ptr)
469 } else {
470 let macro_call = self.expander.to_source(AstPtr::new(&e));
471 match self.expander.enter_expand(self.db, Some(&self.body.item_scope), e) {
472 Some((mark, expansion)) => {
473 self.source_map
474 .expansions
475 .insert(macro_call, self.expander.current_file_id);
476 let id = self.collect_expr(expansion);
477 self.expander.exit(self.db, mark);
478 id
479 }
480 None => self.alloc_expr(Expr::Missing, syntax_ptr),
466 } 481 }
467 None => self.alloc_expr(Expr::Missing, syntax_ptr),
468 } 482 }
469 } 483 }
470 484
@@ -686,6 +700,16 @@ where
686 } 700 }
687} 701}
688 702
703fn is_macro_rules(m: &ast::MacroCall) -> Option<Name> {
704 let name = m.path()?.segment()?.name_ref()?.as_name();
705
706 if name == name![macro_rules] {
707 Some(m.name()?.as_name())
708 } else {
709 None
710 }
711}
712
689impl From<ast::BinOp> for BinaryOp { 713impl From<ast::BinOp> for BinaryOp {
690 fn from(ast_op: ast::BinOp) -> Self { 714 fn from(ast_op: ast::BinOp) -> Self {
691 match ast_op { 715 match ast_op {
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index a72eb5369..c0b16b7fa 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -34,7 +34,8 @@ pub struct FunctionData {
34 34
35impl FunctionData { 35impl FunctionData {
36 pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> { 36 pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> {
37 let src = func.lookup(db).source(db); 37 let loc = func.lookup(db);
38 let src = loc.source(db);
38 let name = src.value.name().map(|n| n.as_name()).unwrap_or_else(Name::missing); 39 let name = src.value.name().map(|n| n.as_name()).unwrap_or_else(Name::missing);
39 let mut params = Vec::new(); 40 let mut params = Vec::new();
40 let mut has_self_param = false; 41 let mut has_self_param = false;
@@ -76,7 +77,9 @@ impl FunctionData {
76 ret_type 77 ret_type
77 }; 78 };
78 79
79 let visibility = RawVisibility::from_ast(db, src.map(|s| s.visibility())); 80 let vis_default = RawVisibility::default_for_container(loc.container);
81 let visibility =
82 RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility()));
80 83
81 let sig = FunctionData { name, params, ret_type, has_self_param, visibility }; 84 let sig = FunctionData { name, params, ret_type, has_self_param, visibility };
82 Arc::new(sig) 85 Arc::new(sig)
@@ -105,10 +108,13 @@ impl TypeAliasData {
105 db: &impl DefDatabase, 108 db: &impl DefDatabase,
106 typ: TypeAliasId, 109 typ: TypeAliasId,
107 ) -> Arc<TypeAliasData> { 110 ) -> Arc<TypeAliasData> {
108 let node = typ.lookup(db).source(db); 111 let loc = typ.lookup(db);
112 let node = loc.source(db);
109 let name = node.value.name().map_or_else(Name::missing, |n| n.as_name()); 113 let name = node.value.name().map_or_else(Name::missing, |n| n.as_name());
110 let type_ref = node.value.type_ref().map(TypeRef::from_ast); 114 let type_ref = node.value.type_ref().map(TypeRef::from_ast);
111 let visibility = RawVisibility::from_ast(db, node.map(|n| n.visibility())); 115 let vis_default = RawVisibility::default_for_container(loc.container);
116 let visibility =
117 RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility()));
112 Arc::new(TypeAliasData { name, type_ref, visibility }) 118 Arc::new(TypeAliasData { name, type_ref, visibility })
113 } 119 }
114} 120}
@@ -230,22 +236,26 @@ pub struct ConstData {
230 236
231impl ConstData { 237impl ConstData {
232 pub(crate) fn const_data_query(db: &impl DefDatabase, konst: ConstId) -> Arc<ConstData> { 238 pub(crate) fn const_data_query(db: &impl DefDatabase, konst: ConstId) -> Arc<ConstData> {
233 let node = konst.lookup(db).source(db); 239 let loc = konst.lookup(db);
234 Arc::new(ConstData::new(db, node)) 240 let node = loc.source(db);
241 let vis_default = RawVisibility::default_for_container(loc.container);
242 Arc::new(ConstData::new(db, vis_default, node))
235 } 243 }
236 244
237 pub(crate) fn static_data_query(db: &impl DefDatabase, konst: StaticId) -> Arc<ConstData> { 245 pub(crate) fn static_data_query(db: &impl DefDatabase, konst: StaticId) -> Arc<ConstData> {
238 let node = konst.lookup(db).source(db); 246 let node = konst.lookup(db).source(db);
239 Arc::new(ConstData::new(db, node)) 247 Arc::new(ConstData::new(db, RawVisibility::private(), node))
240 } 248 }
241 249
242 fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>( 250 fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>(
243 db: &impl DefDatabase, 251 db: &impl DefDatabase,
252 vis_default: RawVisibility,
244 node: InFile<N>, 253 node: InFile<N>,
245 ) -> ConstData { 254 ) -> ConstData {
246 let name = node.value.name().map(|n| n.as_name()); 255 let name = node.value.name().map(|n| n.as_name());
247 let type_ref = TypeRef::from_ast_opt(node.value.ascribed_type()); 256 let type_ref = TypeRef::from_ast_opt(node.value.ascribed_type());
248 let visibility = RawVisibility::from_ast(db, node.map(|n| n.visibility())); 257 let visibility =
258 RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility()));
249 ConstData { name, type_ref, visibility } 259 ConstData { name, type_ref, visibility }
250 } 260 }
251} 261}
@@ -280,7 +290,7 @@ fn collect_impl_items_in_macro(
280 return Vec::new(); 290 return Vec::new();
281 } 291 }
282 292
283 if let Some((mark, items)) = expander.enter_expand(db, m) { 293 if let Some((mark, items)) = expander.enter_expand(db, None, m) {
284 let items: InFile<ast::MacroItems> = expander.to_source(items); 294 let items: InFile<ast::MacroItems> = expander.to_source(items);
285 let mut res = collect_impl_items( 295 let mut res = collect_impl_items(
286 db, 296 db,
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index d0459d9b0..db9838cb5 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -102,6 +102,7 @@ struct MacroDirective {
102 module_id: LocalModuleId, 102 module_id: LocalModuleId,
103 ast_id: AstIdWithPath<ast::MacroCall>, 103 ast_id: AstIdWithPath<ast::MacroCall>,
104 legacy: Option<MacroCallId>, 104 legacy: Option<MacroCallId>,
105 depth: usize,
105} 106}
106 107
107#[derive(Clone, Debug, Eq, PartialEq)] 108#[derive(Clone, Debug, Eq, PartialEq)]
@@ -134,6 +135,7 @@ where
134 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; 135 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
135 ModCollector { 136 ModCollector {
136 def_collector: &mut *self, 137 def_collector: &mut *self,
138 macro_depth: 0,
137 module_id, 139 module_id,
138 file_id: file_id.into(), 140 file_id: file_id.into(),
139 raw_items: &raw_items, 141 raw_items: &raw_items,
@@ -516,7 +518,7 @@ where
516 macros.retain(|directive| { 518 macros.retain(|directive| {
517 if let Some(call_id) = directive.legacy { 519 if let Some(call_id) = directive.legacy {
518 res = ReachedFixedPoint::No; 520 res = ReachedFixedPoint::No;
519 resolved.push((directive.module_id, call_id)); 521 resolved.push((directive.module_id, call_id, directive.depth));
520 return false; 522 return false;
521 } 523 }
522 524
@@ -530,7 +532,7 @@ where
530 ); 532 );
531 resolved_res.resolved_def.take_macros() 533 resolved_res.resolved_def.take_macros()
532 }) { 534 }) {
533 resolved.push((directive.module_id, call_id)); 535 resolved.push((directive.module_id, call_id, directive.depth));
534 res = ReachedFixedPoint::No; 536 res = ReachedFixedPoint::No;
535 return false; 537 return false;
536 } 538 }
@@ -541,7 +543,7 @@ where
541 if let Some(call_id) = 543 if let Some(call_id) =
542 directive.ast_id.as_call_id(self.db, |path| self.resolve_attribute_macro(&path)) 544 directive.ast_id.as_call_id(self.db, |path| self.resolve_attribute_macro(&path))
543 { 545 {
544 resolved.push((directive.module_id, call_id)); 546 resolved.push((directive.module_id, call_id, 0));
545 res = ReachedFixedPoint::No; 547 res = ReachedFixedPoint::No;
546 return false; 548 return false;
547 } 549 }
@@ -552,8 +554,12 @@ where
552 self.unexpanded_macros = macros; 554 self.unexpanded_macros = macros;
553 self.unexpanded_attribute_macros = attribute_macros; 555 self.unexpanded_attribute_macros = attribute_macros;
554 556
555 for (module_id, macro_call_id) in resolved { 557 for (module_id, macro_call_id, depth) in resolved {
556 self.collect_macro_expansion(module_id, macro_call_id); 558 if depth > 1024 {
559 log::debug!("Max macro expansion depth reached");
560 continue;
561 }
562 self.collect_macro_expansion(module_id, macro_call_id, depth);
557 } 563 }
558 564
559 res 565 res
@@ -573,12 +579,18 @@ where
573 None 579 None
574 } 580 }
575 581
576 fn collect_macro_expansion(&mut self, module_id: LocalModuleId, macro_call_id: MacroCallId) { 582 fn collect_macro_expansion(
583 &mut self,
584 module_id: LocalModuleId,
585 macro_call_id: MacroCallId,
586 depth: usize,
587 ) {
577 let file_id: HirFileId = macro_call_id.as_file(); 588 let file_id: HirFileId = macro_call_id.as_file();
578 let raw_items = self.db.raw_items(file_id); 589 let raw_items = self.db.raw_items(file_id);
579 let mod_dir = self.mod_dirs[&module_id].clone(); 590 let mod_dir = self.mod_dirs[&module_id].clone();
580 ModCollector { 591 ModCollector {
581 def_collector: &mut *self, 592 def_collector: &mut *self,
593 macro_depth: depth,
582 file_id, 594 file_id,
583 module_id, 595 module_id,
584 raw_items: &raw_items, 596 raw_items: &raw_items,
@@ -595,6 +607,7 @@ where
595/// Walks a single module, populating defs, imports and macros 607/// Walks a single module, populating defs, imports and macros
596struct ModCollector<'a, D> { 608struct ModCollector<'a, D> {
597 def_collector: D, 609 def_collector: D,
610 macro_depth: usize,
598 module_id: LocalModuleId, 611 module_id: LocalModuleId,
599 file_id: HirFileId, 612 file_id: HirFileId,
600 raw_items: &'a raw::RawItems, 613 raw_items: &'a raw::RawItems,
@@ -684,6 +697,7 @@ where
684 697
685 ModCollector { 698 ModCollector {
686 def_collector: &mut *self.def_collector, 699 def_collector: &mut *self.def_collector,
700 macro_depth: self.macro_depth,
687 module_id, 701 module_id,
688 file_id: self.file_id, 702 file_id: self.file_id,
689 raw_items: self.raw_items, 703 raw_items: self.raw_items,
@@ -713,6 +727,7 @@ where
713 let raw_items = self.def_collector.db.raw_items(file_id.into()); 727 let raw_items = self.def_collector.db.raw_items(file_id.into());
714 ModCollector { 728 ModCollector {
715 def_collector: &mut *self.def_collector, 729 def_collector: &mut *self.def_collector,
730 macro_depth: self.macro_depth,
716 module_id, 731 module_id,
717 file_id: file_id.into(), 732 file_id: file_id.into(),
718 raw_items: &raw_items, 733 raw_items: &raw_items,
@@ -887,6 +902,7 @@ where
887 module_id: self.module_id, 902 module_id: self.module_id,
888 ast_id, 903 ast_id,
889 legacy: Some(macro_call_id), 904 legacy: Some(macro_call_id),
905 depth: self.macro_depth + 1,
890 }); 906 });
891 907
892 return; 908 return;
@@ -902,6 +918,7 @@ where
902 module_id: self.module_id, 918 module_id: self.module_id,
903 ast_id, 919 ast_id,
904 legacy: None, 920 legacy: None,
921 depth: self.macro_depth + 1,
905 }); 922 });
906 } 923 }
907 924
@@ -971,13 +988,26 @@ mod tests {
971 } 988 }
972 989
973 #[test] 990 #[test]
974 fn test_macro_expand_will_stop() { 991 fn test_macro_expand_will_stop_1() {
992 do_resolve(
993 r#"
994 macro_rules! foo {
995 ($($ty:ty)*) => { foo!($($ty)*); }
996 }
997 foo!(KABOOM);
998 "#,
999 );
1000 }
1001
1002 #[ignore] // this test does succeed, but takes quite a while :/
1003 #[test]
1004 fn test_macro_expand_will_stop_2() {
975 do_resolve( 1005 do_resolve(
976 r#" 1006 r#"
977 macro_rules! foo { 1007 macro_rules! foo {
978 ($($ty:ty)*) => { foo!($($ty)*, $($ty)*); } 1008 ($($ty:ty)*) => { foo!($($ty)* $($ty)*); }
979 } 1009 }
980foo!(KABOOM); 1010 foo!(KABOOM);
981 "#, 1011 "#,
982 ); 1012 );
983 } 1013 }
diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs
index dda5ed699..3f33a75b9 100644
--- a/crates/ra_hir_def/src/nameres/tests.rs
+++ b/crates/ra_hir_def/src/nameres/tests.rs
@@ -102,6 +102,28 @@ fn crate_def_map_super_super() {
102} 102}
103 103
104#[test] 104#[test]
105fn crate_def_map_fn_mod_same_name() {
106 let map = def_map(
107 "
108 //- /lib.rs
109 mod m {
110 pub mod z {}
111 pub fn z() {}
112 }
113 ",
114 );
115 assert_snapshot!(map, @r###"
116 â‹®crate
117 â‹®m: t
118 â‹®
119 â‹®crate::m
120 â‹®z: t v
121 â‹®
122 â‹®crate::m::z
123 "###)
124}
125
126#[test]
105fn bogus_paths() { 127fn bogus_paths() {
106 covers!(bogus_paths); 128 covers!(bogus_paths);
107 let map = def_map( 129 let map = def_map(
diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs
index 2734d51a0..123fae72a 100644
--- a/crates/ra_hir_def/src/resolver.rs
+++ b/crates/ra_hir_def/src/resolver.rs
@@ -381,6 +381,11 @@ impl Resolver {
381 db: &impl DefDatabase, 381 db: &impl DefDatabase,
382 path: &ModPath, 382 path: &ModPath,
383 ) -> Option<MacroDefId> { 383 ) -> Option<MacroDefId> {
384 // Search item scope legacy macro first
385 if let Some(def) = self.resolve_local_macro_def(path) {
386 return Some(def);
387 }
388
384 let (item_map, module) = self.module_scope()?; 389 let (item_map, module) = self.module_scope()?;
385 item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros() 390 item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros()
386 } 391 }
@@ -413,6 +418,16 @@ impl Resolver {
413 }) 418 })
414 } 419 }
415 420
421 fn resolve_local_macro_def(&self, path: &ModPath) -> Option<MacroDefId> {
422 let name = path.as_ident()?;
423 self.scopes.iter().rev().find_map(|scope| {
424 if let Scope::LocalItemsScope(body) = scope {
425 return body.item_scope.get_legacy_macro(name);
426 }
427 None
428 })
429 }
430
416 pub fn module(&self) -> Option<ModuleId> { 431 pub fn module(&self) -> Option<ModuleId> {
417 let (def_map, local_id) = self.module_scope()?; 432 let (def_map, local_id) = self.module_scope()?;
418 Some(ModuleId { krate: def_map.krate, local_id }) 433 Some(ModuleId { krate: def_map.krate, local_id })
diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs
index 1568820e9..0756916a8 100644
--- a/crates/ra_hir_def/src/test_db.rs
+++ b/crates/ra_hir_def/src/test_db.rs
@@ -6,7 +6,7 @@ use std::{
6}; 6};
7 7
8use crate::db::DefDatabase; 8use crate::db::DefDatabase;
9use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath}; 9use ra_db::{salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath};
10 10
11#[salsa::database( 11#[salsa::database(
12 ra_db::SourceDatabaseExtStorage, 12 ra_db::SourceDatabaseExtStorage,
@@ -52,6 +52,14 @@ impl FileLoader for TestDB {
52 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 52 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
53 FileLoaderDelegate(self).relevant_crates(file_id) 53 FileLoaderDelegate(self).relevant_crates(file_id)
54 } 54 }
55
56 fn resolve_extern_path(
57 &self,
58 extern_id: ExternSourceId,
59 relative_path: &RelativePath,
60 ) -> Option<FileId> {
61 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
62 }
55} 63}
56 64
57impl TestDB { 65impl TestDB {
diff --git a/crates/ra_hir_def/src/visibility.rs b/crates/ra_hir_def/src/visibility.rs
index d8296da4b..e0c59e905 100644
--- a/crates/ra_hir_def/src/visibility.rs
+++ b/crates/ra_hir_def/src/visibility.rs
@@ -6,7 +6,7 @@ use ra_syntax::ast;
6use crate::{ 6use crate::{
7 db::DefDatabase, 7 db::DefDatabase,
8 path::{ModPath, PathKind}, 8 path::{ModPath, PathKind},
9 ModuleId, 9 AssocContainerId, ModuleId,
10}; 10};
11 11
12/// Visibility of an item, not yet resolved. 12/// Visibility of an item, not yet resolved.
@@ -20,11 +20,30 @@ pub enum RawVisibility {
20} 20}
21 21
22impl RawVisibility { 22impl RawVisibility {
23 const fn private() -> RawVisibility { 23 pub(crate) const fn private() -> RawVisibility {
24 let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() }; 24 let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() };
25 RawVisibility::Module(path) 25 RawVisibility::Module(path)
26 } 26 }
27 27
28 pub(crate) fn default_for_container(container_id: AssocContainerId) -> Self {
29 match container_id {
30 AssocContainerId::TraitId(_) => RawVisibility::Public,
31 _ => RawVisibility::private(),
32 }
33 }
34
35 pub(crate) fn from_ast_with_default(
36 db: &impl DefDatabase,
37 default: RawVisibility,
38 node: InFile<Option<ast::Visibility>>,
39 ) -> RawVisibility {
40 Self::from_ast_with_hygiene_and_default(
41 node.value,
42 default,
43 &Hygiene::new(db, node.file_id),
44 )
45 }
46
28 pub(crate) fn from_ast( 47 pub(crate) fn from_ast(
29 db: &impl DefDatabase, 48 db: &impl DefDatabase,
30 node: InFile<Option<ast::Visibility>>, 49 node: InFile<Option<ast::Visibility>>,
@@ -36,8 +55,16 @@ impl RawVisibility {
36 node: Option<ast::Visibility>, 55 node: Option<ast::Visibility>,
37 hygiene: &Hygiene, 56 hygiene: &Hygiene,
38 ) -> RawVisibility { 57 ) -> RawVisibility {
58 Self::from_ast_with_hygiene_and_default(node, RawVisibility::private(), hygiene)
59 }
60
61 pub(crate) fn from_ast_with_hygiene_and_default(
62 node: Option<ast::Visibility>,
63 default: RawVisibility,
64 hygiene: &Hygiene,
65 ) -> RawVisibility {
39 let node = match node { 66 let node = match node {
40 None => return RawVisibility::private(), 67 None => return default,
41 Some(node) => node, 68 Some(node) => node,
42 }; 69 };
43 match node.kind() { 70 match node.kind() {
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index 3f60b1cca..f9d3787f6 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -88,17 +88,18 @@ register_builtin! {
88 (compile_error, CompileError) => compile_error_expand, 88 (compile_error, CompileError) => compile_error_expand,
89 (file, File) => file_expand, 89 (file, File) => file_expand,
90 (line, Line) => line_expand, 90 (line, Line) => line_expand,
91 (assert, Assert) => assert_expand,
91 (stringify, Stringify) => stringify_expand, 92 (stringify, Stringify) => stringify_expand,
92 (format_args, FormatArgs) => format_args_expand, 93 (format_args, FormatArgs) => format_args_expand,
93 (env, Env) => env_expand,
94 (option_env, OptionEnv) => option_env_expand,
95 // format_args_nl only differs in that it adds a newline in the end, 94 // format_args_nl only differs in that it adds a newline in the end,
96 // so we use the same stub expansion for now 95 // so we use the same stub expansion for now
97 (format_args_nl, FormatArgsNl) => format_args_expand, 96 (format_args_nl, FormatArgsNl) => format_args_expand,
98 97
99 EAGER: 98 EAGER:
100 (concat, Concat) => concat_expand, 99 (concat, Concat) => concat_expand,
101 (include, Include) => include_expand 100 (include, Include) => include_expand,
101 (env, Env) => env_expand,
102 (option_env, OptionEnv) => option_env_expand
102} 103}
103 104
104fn line_expand( 105fn line_expand(
@@ -137,42 +138,56 @@ fn stringify_expand(
137 Ok(expanded) 138 Ok(expanded)
138} 139}
139 140
140fn env_expand( 141fn column_expand(
141 _db: &dyn AstDatabase, 142 _db: &dyn AstDatabase,
142 _id: LazyMacroId, 143 _id: LazyMacroId,
143 _tt: &tt::Subtree, 144 _tt: &tt::Subtree,
144) -> Result<tt::Subtree, mbe::ExpandError> { 145) -> Result<tt::Subtree, mbe::ExpandError> {
145 // dummy implementation for type-checking purposes 146 // dummy implementation for type-checking purposes
146 // we cannot use an empty string here, because for 147 let col_num = 0;
147 // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become 148 let expanded = quote! {
148 // `include!("foo.rs"), which maybe infinite loop 149 #col_num
149 let expanded = quote! { "__RA_UNIMPLEMENTATED__" }; 150 };
150 151
151 Ok(expanded) 152 Ok(expanded)
152} 153}
153 154
154fn option_env_expand( 155fn assert_expand(
155 _db: &dyn AstDatabase, 156 _db: &dyn AstDatabase,
156 _id: LazyMacroId, 157 _id: LazyMacroId,
157 _tt: &tt::Subtree, 158 tt: &tt::Subtree,
158) -> Result<tt::Subtree, mbe::ExpandError> { 159) -> Result<tt::Subtree, mbe::ExpandError> {
159 // dummy implementation for type-checking purposes 160 // A hacky implementation for goto def and hover
160 let expanded = quote! { std::option::Option::None::<&str> }; 161 // We expand `assert!(cond, arg1, arg2)` to
162 // ```
163 // {(cond, &(arg1), &(arg2));}
164 // ```,
165 // which is wrong but useful.
161 166
162 Ok(expanded) 167 let mut args = Vec::new();
163} 168 let mut current = Vec::new();
169 for tt in tt.token_trees.iter().cloned() {
170 match tt {
171 tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
172 args.push(current);
173 current = Vec::new();
174 }
175 _ => {
176 current.push(tt);
177 }
178 }
179 }
180 if !current.is_empty() {
181 args.push(current);
182 }
183
184 let arg_tts = args.into_iter().flat_map(|arg| {
185 quote! { &(##arg), }
186 }.token_trees).collect::<Vec<_>>();
164 187
165fn column_expand(
166 _db: &dyn AstDatabase,
167 _id: LazyMacroId,
168 _tt: &tt::Subtree,
169) -> Result<tt::Subtree, mbe::ExpandError> {
170 // dummy implementation for type-checking purposes
171 let col_num = 0;
172 let expanded = quote! { 188 let expanded = quote! {
173 #col_num 189 { { (##arg_tts); } }
174 }; 190 };
175
176 Ok(expanded) 191 Ok(expanded)
177} 192}
178 193
@@ -278,30 +293,37 @@ fn concat_expand(
278 293
279fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> { 294fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> {
280 let call_site = call_id.as_file().original_file(db); 295 let call_site = call_id.as_file().original_file(db);
281 let path = RelativePath::new(&path);
282 296
283 let res = db.resolve_relative_path(call_site, &path)?; 297 // Handle trivial case
284 // Prevent include itself 298 if let Some(res) = db.resolve_relative_path(call_site, &RelativePath::new(&path)) {
285 if res == call_site { 299 // Prevent include itself
286 return None; 300 return if res == call_site { None } else { Some(res) };
287 } 301 }
288 Some(res) 302
303 // Extern paths ?
304 let krate = db.relevant_crates(call_site).get(0)?.clone();
305 let (extern_source_id, relative_file) =
306 db.crate_graph()[krate].extern_source.extern_path(path)?;
307
308 db.resolve_extern_path(extern_source_id, &relative_file)
289} 309}
290 310
291fn include_expand( 311fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> {
292 db: &dyn AstDatabase, 312 tt.token_trees
293 arg_id: EagerMacroId,
294 tt: &tt::Subtree,
295) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
296 let path = tt
297 .token_trees
298 .get(0) 313 .get(0)
299 .and_then(|tt| match tt { 314 .and_then(|tt| match tt {
300 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(&it), 315 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(&it),
301 _ => None, 316 _ => None,
302 }) 317 })
303 .ok_or_else(|| mbe::ExpandError::ConversionError)?; 318 .ok_or_else(|| mbe::ExpandError::ConversionError)
319}
304 320
321fn include_expand(
322 db: &dyn AstDatabase,
323 arg_id: EagerMacroId,
324 tt: &tt::Subtree,
325) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
326 let path = parse_string(tt)?;
305 let file_id = 327 let file_id =
306 relative_file(db, arg_id.into(), &path).ok_or_else(|| mbe::ExpandError::ConversionError)?; 328 relative_file(db, arg_id.into(), &path).ok_or_else(|| mbe::ExpandError::ConversionError)?;
307 329
@@ -314,12 +336,58 @@ fn include_expand(
314 Ok((res, FragmentKind::Items)) 336 Ok((res, FragmentKind::Items))
315} 337}
316 338
339fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> {
340 let call_id: MacroCallId = arg_id.into();
341 let original_file = call_id.as_file().original_file(db);
342
343 let krate = db.relevant_crates(original_file).get(0)?.clone();
344 db.crate_graph()[krate].env.get(key)
345}
346
347fn env_expand(
348 db: &dyn AstDatabase,
349 arg_id: EagerMacroId,
350 tt: &tt::Subtree,
351) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
352 let key = parse_string(tt)?;
353
354 // FIXME:
355 // If the environment variable is not defined int rustc, then a compilation error will be emitted.
356 // We might do the same if we fully support all other stuffs.
357 // But for now on, we should return some dummy string for better type infer purpose.
358 // However, we cannot use an empty string here, because for
359 // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
360 // `include!("foo.rs"), which might go to infinite loop
361 let s = get_env_inner(db, arg_id, &key).unwrap_or("__RA_UNIMPLEMENTATED__".to_string());
362 let expanded = quote! { #s };
363
364 Ok((expanded, FragmentKind::Expr))
365}
366
367fn option_env_expand(
368 db: &dyn AstDatabase,
369 arg_id: EagerMacroId,
370 tt: &tt::Subtree,
371) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
372 let key = parse_string(tt)?;
373 let expanded = match get_env_inner(db, arg_id, &key) {
374 None => quote! { std::option::Option::None::<&str> },
375 Some(s) => quote! { std::option::Some(#s) },
376 };
377
378 Ok((expanded, FragmentKind::Expr))
379}
380
317#[cfg(test)] 381#[cfg(test)]
318mod tests { 382mod tests {
319 use super::*; 383 use super::*;
320 use crate::{name::AsName, test_db::TestDB, AstNode, MacroCallId, MacroCallKind, MacroCallLoc}; 384 use crate::{
385 name::AsName, test_db::TestDB, AstNode, EagerCallLoc, MacroCallId, MacroCallKind,
386 MacroCallLoc,
387 };
321 use ra_db::{fixture::WithFixture, SourceDatabase}; 388 use ra_db::{fixture::WithFixture, SourceDatabase};
322 use ra_syntax::ast::NameOwner; 389 use ra_syntax::ast::NameOwner;
390 use std::sync::Arc;
323 391
324 fn expand_builtin_macro(ra_fixture: &str) -> String { 392 fn expand_builtin_macro(ra_fixture: &str) -> String {
325 let (db, file_id) = TestDB::with_single_file(&ra_fixture); 393 let (db, file_id) = TestDB::with_single_file(&ra_fixture);
@@ -330,27 +398,61 @@ mod tests {
330 let ast_id_map = db.ast_id_map(file_id.into()); 398 let ast_id_map = db.ast_id_map(file_id.into());
331 399
332 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap(); 400 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap();
333 let expander = expander.left().unwrap();
334 401
335 // the first one should be a macro_rules 402 let file_id = match expander {
336 let def = MacroDefId { 403 Either::Left(expander) => {
337 krate: Some(CrateId(0)), 404 // the first one should be a macro_rules
338 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))), 405 let def = MacroDefId {
339 kind: MacroDefKind::BuiltIn(expander), 406 krate: Some(CrateId(0)),
340 }; 407 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
408 kind: MacroDefKind::BuiltIn(expander),
409 };
341 410
342 let loc = MacroCallLoc { 411 let loc = MacroCallLoc {
343 def, 412 def,
344 kind: MacroCallKind::FnLike(AstId::new( 413 kind: MacroCallKind::FnLike(AstId::new(
345 file_id.into(), 414 file_id.into(),
346 ast_id_map.ast_id(&macro_calls[1]), 415 ast_id_map.ast_id(&macro_calls[1]),
347 )), 416 )),
348 }; 417 };
418
419 let id: MacroCallId = db.intern_macro(loc).into();
420 id.as_file()
421 }
422 Either::Right(expander) => {
423 // the first one should be a macro_rules
424 let def = MacroDefId {
425 krate: Some(CrateId(0)),
426 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
427 kind: MacroDefKind::BuiltInEager(expander),
428 };
349 429
350 let id: MacroCallId = db.intern_macro(loc).into(); 430 let args = macro_calls[1].token_tree().unwrap();
351 let parsed = db.parse_or_expand(id.as_file()).unwrap(); 431 let parsed_args = mbe::ast_to_token_tree(&args).unwrap().0;
432
433 let arg_id = db.intern_eager_expansion({
434 EagerCallLoc {
435 def,
436 fragment: FragmentKind::Expr,
437 subtree: Arc::new(parsed_args.clone()),
438 file_id: file_id.into(),
439 }
440 });
441
442 let (subtree, fragment) = expander.expand(&db, arg_id, &parsed_args).unwrap();
443 let eager = EagerCallLoc {
444 def,
445 fragment,
446 subtree: Arc::new(subtree),
447 file_id: file_id.into(),
448 };
352 449
353 parsed.text().to_string() 450 let id: MacroCallId = db.intern_eager_expansion(eager.into()).into();
451 id.as_file()
452 }
453 };
454
455 db.parse_or_expand(file_id).unwrap().to_string()
354 } 456 }
355 457
356 #[test] 458 #[test]
@@ -432,6 +534,22 @@ mod tests {
432 } 534 }
433 535
434 #[test] 536 #[test]
537 fn test_assert_expand() {
538 let expanded = expand_builtin_macro(
539 r#"
540 #[rustc_builtin_macro]
541 macro_rules! assert {
542 ($cond:expr) => ({ /* compiler built-in */ });
543 ($cond:expr, $($args:tt)*) => ({ /* compiler built-in */ })
544 }
545 assert!(true, "{} {:?}", arg1(a, b, c), arg2);
546 "#,
547 );
548
549 assert_eq!(expanded, "{{(&(true), &(\"{} {:?}\"), &(arg1(a,b,c)), &(arg2),);}}");
550 }
551
552 #[test]
435 fn test_compile_error_expand() { 553 fn test_compile_error_expand() {
436 let expanded = expand_builtin_macro( 554 let expanded = expand_builtin_macro(
437 r#" 555 r#"
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index 3fce73e8a..7b72eb7a0 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -17,7 +17,7 @@ pub mod eager;
17use std::hash::Hash; 17use std::hash::Hash;
18use std::sync::Arc; 18use std::sync::Arc;
19 19
20use ra_db::{salsa, CrateId, FileId}; 20use ra_db::{impl_intern_key, salsa, CrateId, FileId};
21use ra_syntax::{ 21use ra_syntax::{
22 algo, 22 algo,
23 ast::{self, AstNode}, 23 ast::{self, AstNode},
@@ -174,25 +174,11 @@ pub enum MacroCallId {
174 174
175#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 175#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
176pub struct LazyMacroId(salsa::InternId); 176pub struct LazyMacroId(salsa::InternId);
177impl salsa::InternKey for LazyMacroId { 177impl_intern_key!(LazyMacroId);
178 fn from_intern_id(v: salsa::InternId) -> Self {
179 LazyMacroId(v)
180 }
181 fn as_intern_id(&self) -> salsa::InternId {
182 self.0
183 }
184}
185 178
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 179#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
187pub struct EagerMacroId(salsa::InternId); 180pub struct EagerMacroId(salsa::InternId);
188impl salsa::InternKey for EagerMacroId { 181impl_intern_key!(EagerMacroId);
189 fn from_intern_id(v: salsa::InternId) -> Self {
190 EagerMacroId(v)
191 }
192 fn as_intern_id(&self) -> salsa::InternId {
193 self.0
194 }
195}
196 182
197impl From<LazyMacroId> for MacroCallId { 183impl From<LazyMacroId> for MacroCallId {
198 fn from(it: LazyMacroId) -> Self { 184 fn from(it: LazyMacroId) -> Self {
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index 6d201256f..25cc1e9fc 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -172,6 +172,7 @@ pub mod known {
172 column, 172 column,
173 compile_error, 173 compile_error,
174 line, 174 line,
175 assert,
175 stringify, 176 stringify,
176 concat, 177 concat,
177 include, 178 include,
diff --git a/crates/ra_hir_expand/src/quote.rs b/crates/ra_hir_expand/src/quote.rs
index 57e7eebf9..3fd4233da 100644
--- a/crates/ra_hir_expand/src/quote.rs
+++ b/crates/ra_hir_expand/src/quote.rs
@@ -99,6 +99,7 @@ macro_rules! __quote {
99 ( & ) => {$crate::__quote!(@PUNCT '&')}; 99 ( & ) => {$crate::__quote!(@PUNCT '&')};
100 ( , ) => {$crate::__quote!(@PUNCT ',')}; 100 ( , ) => {$crate::__quote!(@PUNCT ',')};
101 ( : ) => {$crate::__quote!(@PUNCT ':')}; 101 ( : ) => {$crate::__quote!(@PUNCT ':')};
102 ( ; ) => {$crate::__quote!(@PUNCT ';')};
102 ( :: ) => {$crate::__quote!(@PUNCT ':', ':')}; 103 ( :: ) => {$crate::__quote!(@PUNCT ':', ':')};
103 ( . ) => {$crate::__quote!(@PUNCT '.')}; 104 ( . ) => {$crate::__quote!(@PUNCT '.')};
104 ( < ) => {$crate::__quote!(@PUNCT '<')}; 105 ( < ) => {$crate::__quote!(@PUNCT '<')};
diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs
index 918736e2a..c1fb762de 100644
--- a/crates/ra_hir_expand/src/test_db.rs
+++ b/crates/ra_hir_expand/src/test_db.rs
@@ -5,7 +5,7 @@ use std::{
5 sync::{Arc, Mutex}, 5 sync::{Arc, Mutex},
6}; 6};
7 7
8use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath}; 8use ra_db::{salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath};
9 9
10#[salsa::database( 10#[salsa::database(
11 ra_db::SourceDatabaseExtStorage, 11 ra_db::SourceDatabaseExtStorage,
@@ -51,4 +51,11 @@ impl FileLoader for TestDB {
51 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 51 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
52 FileLoaderDelegate(self).relevant_crates(file_id) 52 FileLoaderDelegate(self).relevant_crates(file_id)
53 } 53 }
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 }
54} 61}
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index b7e8855fb..7f5e1469e 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
<