aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/ast_transform.rs28
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs40
-rw-r--r--crates/hir/src/code_model.rs40
-rw-r--r--crates/hir/src/semantics.rs19
-rw-r--r--crates/hir_def/src/diagnostics.rs42
-rw-r--r--crates/hir_def/src/item_tree.rs8
-rw-r--r--crates/hir_def/src/item_tree/lower.rs7
-rw-r--r--crates/hir_def/src/item_tree/tests.rs6
-rw-r--r--crates/hir_def/src/nameres.rs93
-rw-r--r--crates/hir_def/src/nameres/collector.rs418
-rw-r--r--crates/hir_def/src/nameres/tests.rs1
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs131
-rw-r--r--crates/hir_def/src/nameres/tests/mod_resolution.rs36
-rw-r--r--crates/hir_def/src/path.rs4
-rw-r--r--crates/hir_def/src/test_db.rs42
-rw-r--r--crates/hir_expand/src/db.rs4
-rw-r--r--crates/hir_expand/src/eager.rs2
-rw-r--r--crates/hir_expand/src/hygiene.rs2
-rw-r--r--crates/hir_expand/src/lib.rs2
-rw-r--r--crates/ide/src/completion/completion_context.rs10
-rw-r--r--crates/ide/src/completion/presentation.rs123
-rw-r--r--crates/ide/src/diagnostics.rs62
-rw-r--r--crates/project_model/src/lib.rs8
-rw-r--r--crates/project_model/src/project_json.rs21
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/bin/main.rs4
-rw-r--r--crates/rust-analyzer/src/caps.rs14
-rw-r--r--crates/rust-analyzer/src/config.rs5
-rw-r--r--crates/rust-analyzer/src/handlers.rs13
-rw-r--r--crates/rust-analyzer/src/reload.rs13
-rw-r--r--crates/rust-analyzer/src/to_proto.rs12
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/main.rs36
-rw-r--r--crates/syntax/src/ast/edit.rs16
-rw-r--r--crates/syntax/src/ast/make.rs4
34 files changed, 970 insertions, 298 deletions
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs
index bbcd2d488..835da3bb2 100644
--- a/crates/assists/src/ast_transform.rs
+++ b/crates/assists/src/ast_transform.rs
@@ -18,6 +18,34 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
18 .rewrite_ast(&node) 18 .rewrite_ast(&node)
19} 19}
20 20
21/// `AstTransform` helps with applying bulk transformations to syntax nodes.
22///
23/// This is mostly useful for IDE code generation. If you paste some existing
24/// code into a new context (for example, to add method overrides to an `impl`
25/// block), you generally want to appropriately qualify the names, and sometimes
26/// you might want to substitute generic parameters as well:
27///
28/// ```
29/// mod x {
30/// pub struct A;
31/// pub trait T<U> { fn foo(&self, _: U) -> A; }
32/// }
33///
34/// mod y {
35/// use x::T;
36///
37/// impl T<()> for () {
38/// // If we invoke **Add Missing Members** here, we want to copy-paste `foo`.
39/// // But we want a slightly-modified version of it:
40/// fn foo(&self, _: ()) -> x::A {}
41/// }
42/// }
43/// ```
44///
45/// So, a single `AstTransform` describes such function from `SyntaxNode` to
46/// `SyntaxNode`. Note that the API here is a bit too high-order and high-brow.
47/// We'd want to somehow express this concept simpler, but so far nobody got to
48/// simplifying this!
21pub trait AstTransform<'a> { 49pub trait AstTransform<'a> {
22 fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode>; 50 fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode>;
23 51
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs
index 83a2ada9a..8df1d786b 100644
--- a/crates/assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/assists/src/handlers/add_missing_impl_members.rs
@@ -111,8 +111,6 @@ fn add_missing_impl_members_inner(
111) -> Option<()> { 111) -> Option<()> {
112 let _p = profile::span("add_missing_impl_members_inner"); 112 let _p = profile::span("add_missing_impl_members_inner");
113 let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; 113 let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
114 let impl_item_list = impl_def.assoc_item_list()?;
115
116 let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; 114 let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
117 115
118 let def_name = |item: &ast::AssocItem| -> Option<SmolStr> { 116 let def_name = |item: &ast::AssocItem| -> Option<SmolStr> {
@@ -148,11 +146,14 @@ fn add_missing_impl_members_inner(
148 146
149 let target = impl_def.syntax().text_range(); 147 let target = impl_def.syntax().text_range();
150 acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { 148 acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
149 let impl_item_list = impl_def.assoc_item_list().unwrap_or(make::assoc_item_list());
150
151 let n_existing_items = impl_item_list.assoc_items().count(); 151 let n_existing_items = impl_item_list.assoc_items().count();
152 let source_scope = ctx.sema.scope_for_def(trait_); 152 let source_scope = ctx.sema.scope_for_def(trait_);
153 let target_scope = ctx.sema.scope(impl_item_list.syntax()); 153 let target_scope = ctx.sema.scope(impl_def.syntax());
154 let ast_transform = QualifyPaths::new(&target_scope, &source_scope) 154 let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
155 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def)); 155 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone()));
156
156 let items = missing_items 157 let items = missing_items
157 .into_iter() 158 .into_iter()
158 .map(|it| ast_transform::apply(&*ast_transform, it)) 159 .map(|it| ast_transform::apply(&*ast_transform, it))
@@ -162,12 +163,14 @@ fn add_missing_impl_members_inner(
162 _ => it, 163 _ => it,
163 }) 164 })
164 .map(|it| edit::remove_attrs_and_docs(&it)); 165 .map(|it| edit::remove_attrs_and_docs(&it));
166
165 let new_impl_item_list = impl_item_list.append_items(items); 167 let new_impl_item_list = impl_item_list.append_items(items);
166 let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap(); 168 let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list);
169 let first_new_item =
170 new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap();
167 171
168 let original_range = impl_item_list.syntax().text_range();
169 match ctx.config.snippet_cap { 172 match ctx.config.snippet_cap {
170 None => builder.replace(original_range, new_impl_item_list.to_string()), 173 None => builder.replace(target, new_impl_def.to_string()),
171 Some(cap) => { 174 Some(cap) => {
172 let mut cursor = Cursor::Before(first_new_item.syntax()); 175 let mut cursor = Cursor::Before(first_new_item.syntax());
173 let placeholder; 176 let placeholder;
@@ -181,8 +184,8 @@ fn add_missing_impl_members_inner(
181 } 184 }
182 builder.replace_snippet( 185 builder.replace_snippet(
183 cap, 186 cap,
184 original_range, 187 target,
185 render_snippet(cap, new_impl_item_list.syntax(), cursor), 188 render_snippet(cap, new_impl_def.syntax(), cursor),
186 ) 189 )
187 } 190 }
188 }; 191 };
@@ -311,6 +314,25 @@ impl Foo for S {
311 } 314 }
312 315
313 #[test] 316 #[test]
317 fn test_impl_def_without_braces() {
318 check_assist(
319 add_missing_impl_members,
320 r#"
321trait Foo { fn foo(&self); }
322struct S;
323impl Foo for S<|>"#,
324 r#"
325trait Foo { fn foo(&self); }
326struct S;
327impl Foo for S {
328 fn foo(&self) {
329 ${0:todo!()}
330 }
331}"#,
332 );
333 }
334
335 #[test]
314 fn fill_in_type_params_1() { 336 fn fill_in_type_params_1() {
315 check_assist( 337 check_assist(
316 add_missing_impl_members, 338 add_missing_impl_members,
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 7a9747fc7..a2a166e0a 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -709,11 +709,23 @@ impl Function {
709 } 709 }
710 710
711 pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> { 711 pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> {
712 let resolver = self.id.resolver(db.upcast());
713 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
714 let environment = TraitEnvironment::lower(db, &resolver);
712 db.function_data(self.id) 715 db.function_data(self.id)
713 .params 716 .params
714 .iter() 717 .iter()
715 .skip(if self.self_param(db).is_some() { 1 } else { 0 }) 718 .skip(if self.self_param(db).is_some() { 1 } else { 0 })
716 .map(|_| Param { _ty: () }) 719 .map(|type_ref| {
720 let ty = Type {
721 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate,
722 ty: InEnvironment {
723 value: Ty::from_hir_ext(&ctx, type_ref).0,
724 environment: environment.clone(),
725 },
726 };
727 Param { ty }
728 })
717 .collect() 729 .collect()
718 } 730 }
719 731
@@ -742,15 +754,21 @@ impl From<Mutability> for Access {
742 } 754 }
743} 755}
744 756
757pub struct Param {
758 ty: Type,
759}
760
761impl Param {
762 pub fn ty(&self) -> &Type {
763 &self.ty
764 }
765}
766
745#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 767#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
746pub struct SelfParam { 768pub struct SelfParam {
747 func: FunctionId, 769 func: FunctionId,
748} 770}
749 771
750pub struct Param {
751 _ty: (),
752}
753
754impl SelfParam { 772impl SelfParam {
755 pub fn access(self, db: &dyn HirDatabase) -> Access { 773 pub fn access(self, db: &dyn HirDatabase) -> Access {
756 let func_data = db.function_data(self.func); 774 let func_data = db.function_data(self.func);
@@ -908,12 +926,12 @@ impl MacroDef {
908 926
909 /// Indicate it is a proc-macro 927 /// Indicate it is a proc-macro
910 pub fn is_proc_macro(&self) -> bool { 928 pub fn is_proc_macro(&self) -> bool {
911 matches!(self.id.kind, MacroDefKind::CustomDerive(_)) 929 matches!(self.id.kind, MacroDefKind::ProcMacro(_))
912 } 930 }
913 931
914 /// Indicate it is a derive macro 932 /// Indicate it is a derive macro
915 pub fn is_derive_macro(&self) -> bool { 933 pub fn is_derive_macro(&self) -> bool {
916 matches!(self.id.kind, MacroDefKind::CustomDerive(_) | MacroDefKind::BuiltInDerive(_)) 934 matches!(self.id.kind, MacroDefKind::ProcMacro(_) | MacroDefKind::BuiltInDerive(_))
917 } 935 }
918} 936}
919 937
@@ -1276,6 +1294,14 @@ impl Type {
1276 ) 1294 )
1277 } 1295 }
1278 1296
1297 pub fn remove_ref(&self) -> Option<Type> {
1298 if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(_), .. }) = self.ty.value {
1299 self.ty.value.substs().map(|substs| self.derived(substs[0].clone()))
1300 } else {
1301 None
1302 }
1303 }
1304
1279 pub fn is_unknown(&self) -> bool { 1305 pub fn is_unknown(&self) -> bool {
1280 matches!(self.ty.value, Ty::Unknown) 1306 matches!(self.ty.value, Ty::Unknown)
1281 } 1307 }
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 0516a05b4..c61a430e1 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -697,6 +697,25 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode {
697 node.ancestors().last().unwrap() 697 node.ancestors().last().unwrap()
698} 698}
699 699
700/// `SemanticScope` encapsulates the notion of a scope (the set of visible
701/// names) at a particular program point.
702///
703/// It is a bit tricky, as scopes do not really exist inside the compiler.
704/// Rather, the compiler directly computes for each reference the definition it
705/// refers to. It might transiently compute the explicit scope map while doing
706/// so, but, generally, this is not something left after the analysis.
707///
708/// However, we do very much need explicit scopes for IDE purposes --
709/// completion, at its core, lists the contents of the current scope. The notion
710/// of scope is also useful to answer questions like "what would be the meaning
711/// of this piece of code if we inserted it into this position?".
712///
713/// So `SemanticsScope` is constructed from a specific program point (a syntax
714/// node or just a raw offset) and provides access to the set of visible names
715/// on a somewhat best-effort basis.
716///
717/// Note that if you are wondering "what does this specific existing name mean?",
718/// you'd better use the `resolve_` family of methods.
700#[derive(Debug)] 719#[derive(Debug)]
701pub struct SemanticsScope<'a> { 720pub struct SemanticsScope<'a> {
702 pub db: &'a dyn HirDatabase, 721 pub db: &'a dyn HirDatabase,
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs
index 3e19d9117..2ec0fd3fb 100644
--- a/crates/hir_def/src/diagnostics.rs
+++ b/crates/hir_def/src/diagnostics.rs
@@ -28,3 +28,45 @@ impl Diagnostic for UnresolvedModule {
28 self 28 self
29 } 29 }
30} 30}
31
32#[derive(Debug)]
33pub struct UnresolvedExternCrate {
34 pub file: HirFileId,
35 pub item: AstPtr<ast::ExternCrate>,
36}
37
38impl Diagnostic for UnresolvedExternCrate {
39 fn code(&self) -> DiagnosticCode {
40 DiagnosticCode("unresolved-extern-crate")
41 }
42 fn message(&self) -> String {
43 "unresolved extern crate".to_string()
44 }
45 fn display_source(&self) -> InFile<SyntaxNodePtr> {
46 InFile::new(self.file, self.item.clone().into())
47 }
48 fn as_any(&self) -> &(dyn Any + Send + 'static) {
49 self
50 }
51}
52
53#[derive(Debug)]
54pub struct UnresolvedImport {
55 pub file: HirFileId,
56 pub node: AstPtr<ast::UseTree>,
57}
58
59impl Diagnostic for UnresolvedImport {
60 fn code(&self) -> DiagnosticCode {
61 DiagnosticCode("unresolved-import")
62 }
63 fn message(&self) -> String {
64 "unresolved import".to_string()
65 }
66 fn display_source(&self) -> InFile<SyntaxNodePtr> {
67 InFile::new(self.file, self.node.clone().into())
68 }
69 fn as_any(&self) -> &(dyn Any + Send + 'static) {
70 self
71 }
72}
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 52abb8e7f..0fd91b9d0 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -291,7 +291,6 @@ pub enum AttrOwner {
291 291
292 Variant(Idx<Variant>), 292 Variant(Idx<Variant>),
293 Field(Idx<Field>), 293 Field(Idx<Field>),
294 // FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`.
295} 294}
296 295
297macro_rules! from_attrs { 296macro_rules! from_attrs {
@@ -483,11 +482,16 @@ pub struct Import {
483 /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many 482 /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many
484 /// `Import`s can map to the same `use` item. 483 /// `Import`s can map to the same `use` item.
485 pub ast_id: FileAstId<ast::Use>, 484 pub ast_id: FileAstId<ast::Use>,
485 /// Index of this `Import` when the containing `Use` is visited via `ModPath::expand_use_item`.
486 ///
487 /// This can be used to get the `UseTree` this `Import` corresponds to and allows emitting
488 /// precise diagnostics.
489 pub index: usize,
486} 490}
487 491
488#[derive(Debug, Clone, Eq, PartialEq)] 492#[derive(Debug, Clone, Eq, PartialEq)]
489pub struct ExternCrate { 493pub struct ExternCrate {
490 pub path: ModPath, 494 pub name: Name,
491 pub alias: Option<ImportAlias>, 495 pub alias: Option<ImportAlias>,
492 pub visibility: RawVisibilityId, 496 pub visibility: RawVisibilityId,
493 /// Whether this is a `#[macro_use] extern crate ...`. 497 /// Whether this is a `#[macro_use] extern crate ...`.
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index d93377c3b..54814f141 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -483,7 +483,7 @@ impl Ctx {
483 ModPath::expand_use_item( 483 ModPath::expand_use_item(
484 InFile::new(self.file, use_item.clone()), 484 InFile::new(self.file, use_item.clone()),
485 &self.hygiene, 485 &self.hygiene,
486 |path, _tree, is_glob, alias| { 486 |path, _use_tree, is_glob, alias| {
487 imports.push(id(tree.imports.alloc(Import { 487 imports.push(id(tree.imports.alloc(Import {
488 path, 488 path,
489 alias, 489 alias,
@@ -491,6 +491,7 @@ impl Ctx {
491 is_glob, 491 is_glob,
492 is_prelude, 492 is_prelude,
493 ast_id, 493 ast_id,
494 index: imports.len(),
494 }))); 495 })));
495 }, 496 },
496 ); 497 );
@@ -502,7 +503,7 @@ impl Ctx {
502 &mut self, 503 &mut self,
503 extern_crate: &ast::ExternCrate, 504 extern_crate: &ast::ExternCrate,
504 ) -> Option<FileItemTreeId<ExternCrate>> { 505 ) -> Option<FileItemTreeId<ExternCrate>> {
505 let path = ModPath::from_name_ref(&extern_crate.name_ref()?); 506 let name = extern_crate.name_ref()?.as_name();
506 let alias = extern_crate.rename().map(|a| { 507 let alias = extern_crate.rename().map(|a| {
507 a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias) 508 a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
508 }); 509 });
@@ -511,7 +512,7 @@ impl Ctx {
511 // FIXME: cfg_attr 512 // FIXME: cfg_attr
512 let is_macro_use = extern_crate.has_atom_attr("macro_use"); 513 let is_macro_use = extern_crate.has_atom_attr("macro_use");
513 514
514 let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id }; 515 let res = ExternCrate { name, alias, visibility, is_macro_use, ast_id };
515 Some(id(self.data().extern_crates.alloc(res))) 516 Some(id(self.data().extern_crates.alloc(res)))
516 } 517 }
517 518
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs
index eed3d0d6f..1a806cda5 100644
--- a/crates/hir_def/src/item_tree/tests.rs
+++ b/crates/hir_def/src/item_tree/tests.rs
@@ -228,11 +228,11 @@ fn smoke() {
228 228
229 top-level items: 229 top-level items:
230 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] 230 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }]
231 Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0) } 231 Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0), index: 0 }
232 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] 232 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }]
233 Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0) } 233 Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0), index: 1 }
234 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }] 234 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }]
235 ExternCrate { path: ModPath { kind: Plain, segments: [Name(Text("krate"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<syntax::ast::generated::nodes::ExternCrate>(1) } 235 ExternCrate { name: Name(Text("krate")), alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<syntax::ast::generated::nodes::ExternCrate>(1) }
236 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }] 236 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }]
237 Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<syntax::ast::generated::nodes::Trait>(2) } 237 Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<syntax::ast::generated::nodes::Trait>(2) }
238 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }] 238 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }]
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index bf302172d..5e4d73c1f 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -288,31 +288,70 @@ pub enum ModuleSource {
288 288
289mod diagnostics { 289mod diagnostics {
290 use hir_expand::diagnostics::DiagnosticSink; 290 use hir_expand::diagnostics::DiagnosticSink;
291 use hir_expand::hygiene::Hygiene;
292 use hir_expand::InFile;
291 use syntax::{ast, AstPtr}; 293 use syntax::{ast, AstPtr};
292 294
293 use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId}; 295 use crate::path::ModPath;
296 use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId};
294 297
295 #[derive(Debug, PartialEq, Eq)] 298 #[derive(Debug, PartialEq, Eq)]
296 pub(super) enum DefDiagnostic { 299 enum DiagnosticKind {
297 UnresolvedModule { 300 UnresolvedModule { declaration: AstId<ast::Module>, candidate: String },
298 module: LocalModuleId, 301
299 declaration: AstId<ast::Module>, 302 UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
300 candidate: String, 303
301 }, 304 UnresolvedImport { ast: AstId<ast::Use>, index: usize },
305 }
306
307 #[derive(Debug, PartialEq, Eq)]
308 pub(super) struct DefDiagnostic {
309 in_module: LocalModuleId,
310 kind: DiagnosticKind,
302 } 311 }
303 312
304 impl DefDiagnostic { 313 impl DefDiagnostic {
314 pub(super) fn unresolved_module(
315 container: LocalModuleId,
316 declaration: AstId<ast::Module>,
317 candidate: String,
318 ) -> Self {
319 Self {
320 in_module: container,
321 kind: DiagnosticKind::UnresolvedModule { declaration, candidate },
322 }
323 }
324
325 pub(super) fn unresolved_extern_crate(
326 container: LocalModuleId,
327 declaration: AstId<ast::ExternCrate>,
328 ) -> Self {
329 Self {
330 in_module: container,
331 kind: DiagnosticKind::UnresolvedExternCrate { ast: declaration },
332 }
333 }
334
335 pub(super) fn unresolved_import(
336 container: LocalModuleId,
337 ast: AstId<ast::Use>,
338 index: usize,
339 ) -> Self {
340 Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } }
341 }
342
305 pub(super) fn add_to( 343 pub(super) fn add_to(
306 &self, 344 &self,
307 db: &dyn DefDatabase, 345 db: &dyn DefDatabase,
308 target_module: LocalModuleId, 346 target_module: LocalModuleId,
309 sink: &mut DiagnosticSink, 347 sink: &mut DiagnosticSink,
310 ) { 348 ) {
311 match self { 349 if self.in_module != target_module {
312 DefDiagnostic::UnresolvedModule { module, declaration, candidate } => { 350 return;
313 if *module != target_module { 351 }
314 return; 352
315 } 353 match &self.kind {
354 DiagnosticKind::UnresolvedModule { declaration, candidate } => {
316 let decl = declaration.to_node(db.upcast()); 355 let decl = declaration.to_node(db.upcast());
317 sink.push(UnresolvedModule { 356 sink.push(UnresolvedModule {
318 file: declaration.file_id, 357 file: declaration.file_id,
@@ -320,6 +359,36 @@ mod diagnostics {
320 candidate: candidate.clone(), 359 candidate: candidate.clone(),
321 }) 360 })
322 } 361 }
362
363 DiagnosticKind::UnresolvedExternCrate { ast } => {
364 let item = ast.to_node(db.upcast());
365 sink.push(UnresolvedExternCrate {
366 file: ast.file_id,
367 item: AstPtr::new(&item),
368 });
369 }
370
371 DiagnosticKind::UnresolvedImport { ast, index } => {
372 let use_item = ast.to_node(db.upcast());
373 let hygiene = Hygiene::new(db.upcast(), ast.file_id);
374 let mut cur = 0;
375 let mut tree = None;
376 ModPath::expand_use_item(
377 InFile::new(ast.file_id, use_item),
378 &hygiene,
379 |_mod_path, use_tree, _is_glob, _alias| {
380 if cur == *index {
381 tree = Some(use_tree.clone());
382 }
383
384 cur += 1;
385 },
386 );
387
388 if let Some(tree) = tree {
389 sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
390 }
391 }
323 } 392 }
324 } 393 }
325 } 394 }
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 3e99c8773..4c3993ff0 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -3,8 +3,11 @@
3//! `DefCollector::collect` contains the fixed-point iteration loop which 3//! `DefCollector::collect` contains the fixed-point iteration loop which
4//! resolves imports and expands macros. 4//! resolves imports and expands macros.
5 5
6use std::iter;
7
6use base_db::{CrateId, FileId, ProcMacroId}; 8use base_db::{CrateId, FileId, ProcMacroId};
7use cfg::CfgOptions; 9use cfg::CfgOptions;
10use hir_expand::InFile;
8use hir_expand::{ 11use hir_expand::{
9 ast_id_map::FileAstId, 12 ast_id_map::FileAstId,
10 builtin_derive::find_builtin_derive, 13 builtin_derive::find_builtin_derive,
@@ -14,6 +17,7 @@ use hir_expand::{
14 HirFileId, MacroCallId, MacroDefId, MacroDefKind, 17 HirFileId, MacroCallId, MacroDefId, MacroDefKind,
15}; 18};
16use rustc_hash::FxHashMap; 19use rustc_hash::FxHashMap;
20use rustc_hash::FxHashSet;
17use syntax::ast; 21use syntax::ast;
18use test_utils::mark; 22use test_utils::mark;
19 23
@@ -21,9 +25,7 @@ use crate::{
21 attr::Attrs, 25 attr::Attrs,
22 db::DefDatabase, 26 db::DefDatabase,
23 item_scope::{ImportType, PerNsGlobImports}, 27 item_scope::{ImportType, PerNsGlobImports},
24 item_tree::{ 28 item_tree::{self, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind},
25 self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind,
26 },
27 nameres::{ 29 nameres::{
28 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, 30 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
29 BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, 31 BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
@@ -112,6 +114,12 @@ impl PartialResolvedImport {
112} 114}
113 115
114#[derive(Clone, Debug, Eq, PartialEq)] 116#[derive(Clone, Debug, Eq, PartialEq)]
117enum ImportSource {
118 Import(ItemTreeId<item_tree::Import>),
119 ExternCrate(ItemTreeId<item_tree::ExternCrate>),
120}
121
122#[derive(Clone, Debug, Eq, PartialEq)]
115struct Import { 123struct Import {
116 pub path: ModPath, 124 pub path: ModPath,
117 pub alias: Option<ImportAlias>, 125 pub alias: Option<ImportAlias>,
@@ -120,11 +128,12 @@ struct Import {
120 pub is_prelude: bool, 128 pub is_prelude: bool,
121 pub is_extern_crate: bool, 129 pub is_extern_crate: bool,
122 pub is_macro_use: bool, 130 pub is_macro_use: bool,
131 source: ImportSource,
123} 132}
124 133
125impl Import { 134impl Import {
126 fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self { 135 fn from_use(tree: &ItemTree, id: ItemTreeId<item_tree::Import>) -> Self {
127 let it = &tree[id]; 136 let it = &tree[id.value];
128 let visibility = &tree[it.visibility]; 137 let visibility = &tree[it.visibility];
129 Self { 138 Self {
130 path: it.path.clone(), 139 path: it.path.clone(),
@@ -134,20 +143,22 @@ impl Import {
134 is_prelude: it.is_prelude, 143 is_prelude: it.is_prelude,
135 is_extern_crate: false, 144 is_extern_crate: false,
136 is_macro_use: false, 145 is_macro_use: false,
146 source: ImportSource::Import(id),
137 } 147 }
138 } 148 }
139 149
140 fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self { 150 fn from_extern_crate(tree: &ItemTree, id: ItemTreeId<item_tree::ExternCrate>) -> Self {
141 let it = &tree[id]; 151 let it = &tree[id.value];
142 let visibility = &tree[it.visibility]; 152 let visibility = &tree[it.visibility];
143 Self { 153 Self {
144 path: it.path.clone(), 154 path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())),
145 alias: it.alias.clone(), 155 alias: it.alias.clone(),
146 visibility: visibility.clone(), 156 visibility: visibility.clone(),
147 is_glob: false, 157 is_glob: false,
148 is_prelude: false, 158 is_prelude: false,
149 is_extern_crate: true, 159 is_extern_crate: true,
150 is_macro_use: it.is_macro_use, 160 is_macro_use: it.is_macro_use,
161 source: ImportSource::ExternCrate(id),
151 } 162 }
152 } 163 }
153} 164}
@@ -245,9 +256,10 @@ impl DefCollector<'_> {
245 256
246 let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); 257 let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
247 // show unresolved imports in completion, etc 258 // show unresolved imports in completion, etc
248 for directive in unresolved_imports { 259 for directive in &unresolved_imports {
249 self.record_resolved_import(&directive) 260 self.record_resolved_import(directive)
250 } 261 }
262 self.unresolved_imports = unresolved_imports;
251 263
252 // Record proc-macros 264 // Record proc-macros
253 self.collect_proc_macro(); 265 self.collect_proc_macro();
@@ -261,7 +273,7 @@ impl DefCollector<'_> {
261 let macro_id = MacroDefId { 273 let macro_id = MacroDefId {
262 ast_id: None, 274 ast_id: None,
263 krate: Some(krate), 275 krate: Some(krate),
264 kind: MacroDefKind::CustomDerive(expander), 276 kind: MacroDefKind::ProcMacro(expander),
265 local_inner: false, 277 local_inner: false,
266 }; 278 };
267 279
@@ -346,20 +358,15 @@ impl DefCollector<'_> {
346 fn import_macros_from_extern_crate( 358 fn import_macros_from_extern_crate(
347 &mut self, 359 &mut self,
348 current_module_id: LocalModuleId, 360 current_module_id: LocalModuleId,
349 import: &item_tree::ExternCrate, 361 extern_crate: &item_tree::ExternCrate,
350 ) { 362 ) {
351 log::debug!( 363 log::debug!(
352 "importing macros from extern crate: {:?} ({:?})", 364 "importing macros from extern crate: {:?} ({:?})",
353 import, 365 extern_crate,
354 self.def_map.edition, 366 self.def_map.edition,
355 ); 367 );
356 368
357 let res = self.def_map.resolve_name_in_extern_prelude( 369 let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name);
358 &import
359 .path
360 .as_ident()
361 .expect("extern crate should have been desugared to one-element path"),
362 );
363 370
364 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { 371 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
365 mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); 372 mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
@@ -420,7 +427,11 @@ impl DefCollector<'_> {
420 .as_ident() 427 .as_ident()
421 .expect("extern crate should have been desugared to one-element path"), 428 .expect("extern crate should have been desugared to one-element path"),
422 ); 429 );
423 PartialResolvedImport::Resolved(res) 430 if res.is_none() {
431 PartialResolvedImport::Unresolved
432 } else {
433 PartialResolvedImport::Resolved(res)
434 }
424 } else { 435 } else {
425 let res = self.def_map.resolve_path_fp_with_macro( 436 let res = self.def_map.resolve_path_fp_with_macro(
426 self.db, 437 self.db,
@@ -774,7 +785,51 @@ impl DefCollector<'_> {
774 .collect(item_tree.top_level_items()); 785 .collect(item_tree.top_level_items());
775 } 786 }
776 787
777 fn finish(self) -> CrateDefMap { 788 fn finish(mut self) -> CrateDefMap {
789 // Emit diagnostics for all remaining unresolved imports.
790
791 // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't
792 // resolve. We first emit diagnostics for unresolved extern crates and collect the missing
793 // crate names. Then we emit diagnostics for unresolved imports, but only if the import
794 // doesn't start with an unresolved crate's name. Due to renaming and reexports, this is a
795 // heuristic, but it works in practice.
796 let mut diagnosed_extern_crates = FxHashSet::default();
797 for directive in &self.unresolved_imports {
798 if let ImportSource::ExternCrate(krate) = directive.import.source {
799 let item_tree = self.db.item_tree(krate.file_id);
800 let extern_crate = &item_tree[krate.value];
801
802 diagnosed_extern_crates.insert(extern_crate.name.clone());
803
804 self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate(
805 directive.module_id,
806 InFile::new(krate.file_id, extern_crate.ast_id),
807 ));
808 }
809 }
810
811 for directive in &self.unresolved_imports {
812 if let ImportSource::Import(import) = &directive.import.source {
813 let item_tree = self.db.item_tree(import.file_id);
814 let import_data = &item_tree[import.value];
815
816 match (import_data.path.segments.first(), &import_data.path.kind) {
817 (Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => {
818 if diagnosed_extern_crates.contains(krate) {
819 continue;
820 }
821 }
822 _ => {}
823 }
824
825 self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
826 directive.module_id,
827 InFile::new(import.file_id, import_data.ast_id),
828 import_data.index,
829 ));
830 }
831 }
832
778 self.def_map 833 self.def_map
779 } 834 }
780} 835}
@@ -819,179 +874,184 @@ impl ModCollector<'_, '_> {
819 874
820 for &item in items { 875 for &item in items {
821 let attrs = self.item_tree.attrs(item.into()); 876 let attrs = self.item_tree.attrs(item.into());
822 if self.is_cfg_enabled(attrs) { 877 if !self.is_cfg_enabled(attrs) {
823 let module = 878 continue;
824 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; 879 }
825 let container = ContainerId::ModuleId(module); 880 let module =
826 881 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
827 let mut def = None; 882 let container = ContainerId::ModuleId(module);
828 match item { 883
829 ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs), 884 let mut def = None;
830 ModItem::Import(import_id) => { 885 match item {
831 self.def_collector.unresolved_imports.push(ImportDirective { 886 ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs),
832 module_id: self.module_id, 887 ModItem::Import(import_id) => {
833 import: Import::from_use(&self.item_tree, import_id), 888 self.def_collector.unresolved_imports.push(ImportDirective {
834 status: PartialResolvedImport::Unresolved, 889 module_id: self.module_id,
835 }) 890 import: Import::from_use(
836 } 891 &self.item_tree,
837 ModItem::ExternCrate(import_id) => { 892 InFile::new(self.file_id, import_id),
838 self.def_collector.unresolved_imports.push(ImportDirective { 893 ),
839 module_id: self.module_id, 894 status: PartialResolvedImport::Unresolved,
840 import: Import::from_extern_crate(&self.item_tree, import_id), 895 })
841 status: PartialResolvedImport::Unresolved, 896 }
842 }) 897 ModItem::ExternCrate(import_id) => {
843 } 898 self.def_collector.unresolved_imports.push(ImportDirective {
844 ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]), 899 module_id: self.module_id,
845 ModItem::Impl(imp) => { 900 import: Import::from_extern_crate(
846 let module = ModuleId { 901 &self.item_tree,
847 krate: self.def_collector.def_map.krate, 902 InFile::new(self.file_id, import_id),
848 local_id: self.module_id, 903 ),
849 }; 904 status: PartialResolvedImport::Unresolved,
850 let container = ContainerId::ModuleId(module); 905 })
851 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } 906 }
852 .intern(self.def_collector.db); 907 ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]),
853 self.def_collector.def_map.modules[self.module_id] 908 ModItem::Impl(imp) => {
854 .scope 909 let module = ModuleId {
855 .define_impl(impl_id) 910 krate: self.def_collector.def_map.krate,
856 } 911 local_id: self.module_id,
857 ModItem::Function(id) => { 912 };
858 let func = &self.item_tree[id]; 913 let container = ContainerId::ModuleId(module);
859 def = Some(DefData { 914 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) }
860 id: FunctionLoc { 915 .intern(self.def_collector.db);
861 container: container.into(), 916 self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id)
862 id: ItemTreeId::new(self.file_id, id), 917 }
863 } 918 ModItem::Function(id) => {
864 .intern(self.def_collector.db) 919 let func = &self.item_tree[id];
865 .into(), 920 def = Some(DefData {
866 name: &func.name, 921 id: FunctionLoc {
867 visibility: &self.item_tree[func.visibility], 922 container: container.into(),
868 has_constructor: false, 923 id: ItemTreeId::new(self.file_id, id),
869 }); 924 }
870 } 925 .intern(self.def_collector.db)
871 ModItem::Struct(id) => { 926 .into(),
872 let it = &self.item_tree[id]; 927 name: &func.name,
873 928 visibility: &self.item_tree[func.visibility],
874 // FIXME: check attrs to see if this is an attribute macro invocation; 929 has_constructor: false,
875 // in which case we don't add the invocation, just a single attribute 930 });
876 // macro invocation 931 }
877 self.collect_derives(attrs, it.ast_id.upcast()); 932 ModItem::Struct(id) => {
878 933 let it = &self.item_tree[id];
879 def = Some(DefData {
880 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
881 .intern(self.def_collector.db)
882 .into(),
883 name: &it.name,
884 visibility: &self.item_tree[it.visibility],
885 has_constructor: it.kind != StructDefKind::Record,
886 });
887 }
888 ModItem::Union(id) => {
889 let it = &self.item_tree[id];
890 934
891 // FIXME: check attrs to see if this is an attribute macro invocation; 935 // FIXME: check attrs to see if this is an attribute macro invocation;
892 // in which case we don't add the invocation, just a single attribute 936 // in which case we don't add the invocation, just a single attribute
893 // macro invocation 937 // macro invocation
894 self.collect_derives(attrs, it.ast_id.upcast()); 938 self.collect_derives(attrs, it.ast_id.upcast());
895 939
896 def = Some(DefData { 940 def = Some(DefData {
897 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } 941 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
898 .intern(self.def_collector.db) 942 .intern(self.def_collector.db)
899 .into(), 943 .into(),
900 name: &it.name, 944 name: &it.name,
901 visibility: &self.item_tree[it.visibility], 945 visibility: &self.item_tree[it.visibility],
902 has_constructor: false, 946 has_constructor: it.kind != StructDefKind::Record,
903 }); 947 });
904 } 948 }
905 ModItem::Enum(id) => { 949 ModItem::Union(id) => {
906 let it = &self.item_tree[id]; 950 let it = &self.item_tree[id];
907 951
908 // FIXME: check attrs to see if this is an attribute macro invocation; 952 // FIXME: check attrs to see if this is an attribute macro invocation;
909 // in which case we don't add the invocation, just a single attribute 953 // in which case we don't add the invocation, just a single attribute
910 // macro invocation 954 // macro invocation
911 self.collect_derives(attrs, it.ast_id.upcast()); 955 self.collect_derives(attrs, it.ast_id.upcast());
912 956
913 def = Some(DefData { 957 def = Some(DefData {
914 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } 958 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) }
915 .intern(self.def_collector.db) 959 .intern(self.def_collector.db)
916 .into(), 960 .into(),
917 name: &it.name, 961 name: &it.name,
918 visibility: &self.item_tree[it.visibility], 962 visibility: &self.item_tree[it.visibility],
919 has_constructor: false, 963 has_constructor: false,
920 }); 964 });
921 } 965 }
922 ModItem::Const(id) => { 966 ModItem::Enum(id) => {
923 let it = &self.item_tree[id]; 967 let it = &self.item_tree[id];
924
925 if let Some(name) = &it.name {
926 def = Some(DefData {
927 id: ConstLoc {
928 container: container.into(),
929 id: ItemTreeId::new(self.file_id, id),
930 }
931 .intern(self.def_collector.db)
932 .into(),
933 name,
934 visibility: &self.item_tree[it.visibility],
935 has_constructor: false,
936 });
937 }
938 }
939 ModItem::Static(id) => {
940 let it = &self.item_tree[id];
941 968
942 def = Some(DefData { 969 // FIXME: check attrs to see if this is an attribute macro invocation;
943 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } 970 // in which case we don't add the invocation, just a single attribute
944 .intern(self.def_collector.db) 971 // macro invocation
945 .into(), 972 self.collect_derives(attrs, it.ast_id.upcast());
946 name: &it.name,
947 visibility: &self.item_tree[it.visibility],
948 has_constructor: false,
949 });
950 }
951 ModItem::Trait(id) => {
952 let it = &self.item_tree[id];
953 973
954 def = Some(DefData { 974 def = Some(DefData {
955 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } 975 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) }
956 .intern(self.def_collector.db) 976 .intern(self.def_collector.db)
957 .into(), 977 .into(),
958 name: &it.name, 978 name: &it.name,
959 visibility: &self.item_tree[it.visibility], 979 visibility: &self.item_tree[it.visibility],
960 has_constructor: false, 980 has_constructor: false,
961 }); 981 });
962 } 982 }
963 ModItem::TypeAlias(id) => { 983 ModItem::Const(id) => {
964 let it = &self.item_tree[id]; 984 let it = &self.item_tree[id];
965 985
986 if let Some(name) = &it.name {
966 def = Some(DefData { 987 def = Some(DefData {
967 id: TypeAliasLoc { 988 id: ConstLoc {
968 container: container.into(), 989 container: container.into(),
969 id: ItemTreeId::new(self.file_id, id), 990 id: ItemTreeId::new(self.file_id, id),
970 } 991 }
971 .intern(self.def_collector.db) 992 .intern(self.def_collector.db)
972 .into(), 993 .into(),
973 name: &it.name, 994 name,
974 visibility: &self.item_tree[it.visibility], 995 visibility: &self.item_tree[it.visibility],
975 has_constructor: false, 996 has_constructor: false,
976 }); 997 });
977 } 998 }
978 } 999 }
1000 ModItem::Static(id) => {
1001 let it = &self.item_tree[id];
979 1002
980 if let Some(DefData { id, name, visibility, has_constructor }) = def { 1003 def = Some(DefData {
981 self.def_collector.def_map.modules[self.module_id].scope.define_def(id); 1004 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) }
982 let vis = self 1005 .intern(self.def_collector.db)
983 .def_collector 1006 .into(),
984 .def_map 1007 name: &it.name,
985 .resolve_visibility(self.def_collector.db, self.module_id, visibility) 1008 visibility: &self.item_tree[it.visibility],
986 .unwrap_or(Visibility::Public); 1009 has_constructor: false,
987 self.def_collector.update( 1010 });
988 self.module_id, 1011 }
989 &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))], 1012 ModItem::Trait(id) => {
990 vis, 1013 let it = &self.item_tree[id];
991 ImportType::Named, 1014
992 ) 1015 def = Some(DefData {
1016 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) }
1017 .intern(self.def_collector.db)
1018 .into(),
1019 name: &it.name,
1020 visibility: &self.item_tree[it.visibility],
1021 has_constructor: false,
1022 });
1023 }
1024 ModItem::TypeAlias(id) => {
1025 let it = &self.item_tree[id];
1026
1027 def = Some(DefData {
1028 id: TypeAliasLoc {
1029 container: container.into(),
1030 id: ItemTreeId::new(self.file_id, id),
1031 }
1032 .intern(self.def_collector.db)
1033 .into(),
1034 name: &it.name,
1035 visibility: &self.item_tree[it.visibility],
1036 has_constructor: false,
1037 });
993 } 1038 }
994 } 1039 }
1040
1041 if let Some(DefData { id, name, visibility, has_constructor }) = def {
1042 self.def_collector.def_map.modules[self.module_id].scope.define_def(id);
1043 let vis = self
1044 .def_collector
1045 .def_map
1046 .resolve_visibility(self.def_collector.db, self.module_id, visibility)
1047 .unwrap_or(Visibility::Public);
1048 self.def_collector.update(
1049 self.module_id,
1050 &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))],
1051 vis,
1052 ImportType::Named,
1053 )
1054 }
995 } 1055 }
996 } 1056 }
997 1057
@@ -1051,13 +1111,11 @@ impl ModCollector<'_, '_> {
1051 self.import_all_legacy_macros(module_id); 1111 self.import_all_legacy_macros(module_id);
1052 } 1112 }
1053 } 1113 }
1054 Err(candidate) => self.def_collector.def_map.diagnostics.push( 1114 Err(candidate) => {
1055 DefDiagnostic::UnresolvedModule { 1115 self.def_collector.def_map.diagnostics.push(
1056 module: self.module_id, 1116 DefDiagnostic::unresolved_module(self.module_id, ast_id, candidate),
1057 declaration: ast_id, 1117 );
1058 candidate, 1118 }
1059 },
1060 ),
1061 }; 1119 };
1062 } 1120 }
1063 } 1121 }
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index 5ca30dac9..11d84f808 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -2,6 +2,7 @@ mod globs;
2mod incremental; 2mod incremental;
3mod macros; 3mod macros;
4mod mod_resolution; 4mod mod_resolution;
5mod diagnostics;
5mod primitives; 6mod primitives;
6 7
7use std::sync::Arc; 8use std::sync::Arc;
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
new file mode 100644
index 000000000..576b813d2
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -0,0 +1,131 @@
1use base_db::fixture::WithFixture;
2use base_db::FileId;
3use base_db::SourceDatabaseExt;
4use hir_expand::db::AstDatabase;
5use rustc_hash::FxHashMap;
6use syntax::TextRange;
7use syntax::TextSize;
8
9use crate::test_db::TestDB;
10
11fn check_diagnostics(ra_fixture: &str) {
12 let db: TestDB = TestDB::with_files(ra_fixture);
13 let annotations = db.extract_annotations();
14 assert!(!annotations.is_empty());
15
16 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
17 db.diagnostics(|d| {
18 let src = d.display_source();
19 let root = db.parse_or_expand(src.file_id).unwrap();
20 // FIXME: macros...
21 let file_id = src.file_id.original_file(&db);
22 let range = src.value.to_node(&root).text_range();
23 let message = d.message().to_owned();
24 actual.entry(file_id).or_default().push((range, message));
25 });
26
27 for (file_id, diags) in actual.iter_mut() {
28 diags.sort_by_key(|it| it.0.start());
29 let text = db.file_text(*file_id);
30 // For multiline spans, place them on line start
31 for (range, content) in diags {
32 if text[*range].contains('\n') {
33 *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
34 *content = format!("... {}", content);
35 }
36 }
37 }
38
39 assert_eq!(annotations, actual);
40}
41
42#[test]
43fn unresolved_import() {
44 check_diagnostics(
45 r"
46 use does_exist;
47 use does_not_exist;
48 //^^^^^^^^^^^^^^ unresolved import
49
50 mod does_exist {}
51 ",
52 );
53}
54
55#[test]
56fn unresolved_import_in_use_tree() {
57 // Only the relevant part of a nested `use` item should be highlighted.
58 check_diagnostics(
59 r"
60 use does_exist::{Exists, DoesntExist};
61 //^^^^^^^^^^^ unresolved import
62
63 use {does_not_exist::*, does_exist};
64 //^^^^^^^^^^^^^^^^^ unresolved import
65
66 use does_not_exist::{
67 a,
68 //^ unresolved import
69 b,
70 //^ unresolved import
71 c,
72 //^ unresolved import
73 };
74
75 mod does_exist {
76 pub struct Exists;
77 }
78 ",
79 );
80}
81
82#[test]
83fn unresolved_extern_crate() {
84 check_diagnostics(
85 r"
86 //- /main.rs crate:main deps:core
87 extern crate core;
88 extern crate doesnotexist;
89 //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
90 //- /lib.rs crate:core
91 ",
92 );
93}
94
95#[test]
96fn dedup_unresolved_import_from_unresolved_crate() {
97 check_diagnostics(
98 r"
99 //- /main.rs crate:main
100 mod a {
101 extern crate doesnotexist;
102 //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
103
104 // Should not error, since we already errored for the missing crate.
105 use doesnotexist::{self, bla, *};
106
107 use crate::doesnotexist;
108 //^^^^^^^^^^^^^^^^^^^ unresolved import
109 }
110
111 mod m {
112 use super::doesnotexist;
113 //^^^^^^^^^^^^^^^^^^^ unresolved import
114 }
115 ",
116 );
117}
118
119#[test]
120fn unresolved_module() {
121 check_diagnostics(
122 r"
123 //- /lib.rs
124 mod foo;
125 mod bar;
126 //^^^^^^^^ unresolved module
127 mod baz {}
128 //- /foo.rs
129 ",
130 );
131}
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs
index 1f619787e..f93337a6e 100644
--- a/crates/hir_def/src/nameres/tests/mod_resolution.rs
+++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs
@@ -672,42 +672,6 @@ pub struct Baz;
672} 672}
673 673
674#[test] 674#[test]
675fn unresolved_module_diagnostics() {
676 let db = TestDB::with_files(
677 r"
678 //- /lib.rs
679 mod foo;
680 mod bar;
681 mod baz {}
682 //- /foo.rs
683 ",
684 );
685 let krate = db.test_crate();
686
687 let crate_def_map = db.crate_def_map(krate);
688
689 expect![[r#"
690 [
691 UnresolvedModule {
692 module: Idx::<ModuleData>(0),
693 declaration: InFile {
694 file_id: HirFileId(
695 FileId(
696 FileId(
697 0,
698 ),
699 ),
700 ),
701 value: FileAstId::<syntax::ast::generated::nodes::Module>(1),
702 },
703 candidate: "bar.rs",
704 },
705 ]
706 "#]]
707 .assert_debug_eq(&crate_def_map.diagnostics);
708}
709
710#[test]
711fn module_resolution_decl_inside_module_in_non_crate_root_2() { 675fn module_resolution_decl_inside_module_in_non_crate_root_2() {
712 check( 676 check(
713 r#" 677 r#"
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index 99395667d..734310458 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -56,10 +56,6 @@ impl ModPath {
56 ModPath { kind, segments } 56 ModPath { kind, segments }
57 } 57 }
58 58
59 pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> ModPath {
60 name_ref.as_name().into()
61 }
62
63 /// Converts an `tt::Ident` into a single-identifier `Path`. 59 /// Converts an `tt::Ident` into a single-identifier `Path`.
64 pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath { 60 pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath {
65 ident.as_name().into() 61 ident.as_name().into()
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs
index 42a762936..fb1d3c974 100644
--- a/crates/hir_def/src/test_db.rs
+++ b/crates/hir_def/src/test_db.rs
@@ -5,9 +5,15 @@ use std::{
5 sync::{Arc, Mutex}, 5 sync::{Arc, Mutex},
6}; 6};
7 7
8use base_db::SourceDatabase;
8use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; 9use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
9use hir_expand::db::AstDatabase; 10use hir_expand::db::AstDatabase;
11use hir_expand::diagnostics::Diagnostic;
12use hir_expand::diagnostics::DiagnosticSinkBuilder;
13use rustc_hash::FxHashMap;
10use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
15use syntax::TextRange;
16use test_utils::extract_annotations;
11 17
12use crate::db::DefDatabase; 18use crate::db::DefDatabase;
13 19
@@ -98,4 +104,40 @@ impl TestDB {
98 }) 104 })
99 .collect() 105 .collect()
100 } 106 }
107
108 pub fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
109 let mut files = Vec::new();
110 let crate_graph = self.crate_graph();
111 for krate in crate_graph.iter() {
112 let crate_def_map = self.crate_def_map(krate);
113 for (module_id, _) in crate_def_map.modules.iter() {
114 let file_id = crate_def_map[module_id].origin.file_id();
115 files.extend(file_id)
116 }
117 }
118 assert!(!files.is_empty());
119 files
120 .into_iter()
121 .filter_map(|file_id| {
122 let text = self.file_text(file_id);
123 let annotations = extract_annotations(&text);
124 if annotations.is_empty() {
125 return None;
126 }
127 Some((file_id, annotations))
128 })
129 .collect()
130 }
131
132 pub fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
133 let crate_graph = self.crate_graph();
134 for krate in crate_graph.iter() {
135 let crate_def_map = self.crate_def_map(krate);
136
137 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
138 for (module_id, _) in crate_def_map.modules.iter() {
139 crate_def_map.add_diagnostics(self, module_id, &mut sink);
140 }
141 }
142 }
101} 143}
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 710694a34..b591130ca 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -143,7 +143,7 @@ pub(crate) fn macro_def(
143 Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default()))) 143 Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default())))
144 } 144 }
145 MacroDefKind::BuiltInEager(_) => None, 145 MacroDefKind::BuiltInEager(_) => None,
146 MacroDefKind::CustomDerive(expander) => { 146 MacroDefKind::ProcMacro(expander) => {
147 Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default()))) 147 Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default())))
148 } 148 }
149 } 149 }
@@ -249,7 +249,7 @@ pub(crate) fn expand_proc_macro(
249 }; 249 };
250 250
251 let expander = match loc.def.kind { 251 let expander = match loc.def.kind {
252 MacroDefKind::CustomDerive(expander) => expander, 252 MacroDefKind::ProcMacro(expander) => expander,
253 _ => unreachable!(), 253 _ => unreachable!(),
254 }; 254 };
255 255
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index 10c45646f..2f37d7189 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -129,7 +129,7 @@ fn eager_macro_recur(
129 MacroDefKind::Declarative 129 MacroDefKind::Declarative
130 | MacroDefKind::BuiltIn(_) 130 | MacroDefKind::BuiltIn(_)
131 | MacroDefKind::BuiltInDerive(_) 131 | MacroDefKind::BuiltInDerive(_)
132 | MacroDefKind::CustomDerive(_) => { 132 | MacroDefKind::ProcMacro(_) => {
133 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?; 133 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?;
134 // replace macro inside 134 // replace macro inside
135 eager_macro_recur(db, expanded, krate, macro_resolver)? 135 eager_macro_recur(db, expanded, krate, macro_resolver)?
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index 845e9cbc1..d383b968d 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -33,7 +33,7 @@ impl Hygiene {
33 MacroDefKind::BuiltIn(_) => (None, false), 33 MacroDefKind::BuiltIn(_) => (None, false),
34 MacroDefKind::BuiltInDerive(_) => (None, false), 34 MacroDefKind::BuiltInDerive(_) => (None, false),
35 MacroDefKind::BuiltInEager(_) => (None, false), 35 MacroDefKind::BuiltInEager(_) => (None, false),
36 MacroDefKind::CustomDerive(_) => (None, false), 36 MacroDefKind::ProcMacro(_) => (None, false),
37 } 37 }
38 } 38 }
39 MacroCallId::EagerMacro(_id) => (None, false), 39 MacroCallId::EagerMacro(_id) => (None, false),
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 2be15e841..17f1178ed 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -246,7 +246,7 @@ pub enum MacroDefKind {
246 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander 246 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
247 BuiltInDerive(BuiltinDeriveExpander), 247 BuiltInDerive(BuiltinDeriveExpander),
248 BuiltInEager(EagerExpander), 248 BuiltInEager(EagerExpander),
249 CustomDerive(ProcMacroExpander), 249 ProcMacro(ProcMacroExpander),
250} 250}
251 251
252#[derive(Debug, Clone, PartialEq, Eq, Hash)] 252#[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs
index 161f59c1e..671b13328 100644
--- a/crates/ide/src/completion/completion_context.rs
+++ b/crates/ide/src/completion/completion_context.rs
@@ -1,7 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use base_db::SourceDatabase; 3use base_db::SourceDatabase;
4use hir::{Semantics, SemanticsScope, Type}; 4use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type};
5use ide_db::RootDatabase; 5use ide_db::RootDatabase;
6use syntax::{ 6use syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::{find_covering_element, find_node_at_offset},
@@ -91,6 +91,7 @@ pub(crate) struct CompletionContext<'a> {
91 pub(super) impl_as_prev_sibling: bool, 91 pub(super) impl_as_prev_sibling: bool,
92 pub(super) is_match_arm: bool, 92 pub(super) is_match_arm: bool,
93 pub(super) has_item_list_or_source_file_parent: bool, 93 pub(super) has_item_list_or_source_file_parent: bool,
94 pub(super) locals: Vec<(String, Local)>,
94} 95}
95 96
96impl<'a> CompletionContext<'a> { 97impl<'a> CompletionContext<'a> {
@@ -119,6 +120,12 @@ impl<'a> CompletionContext<'a> {
119 original_file.syntax().token_at_offset(position.offset).left_biased()?; 120 original_file.syntax().token_at_offset(position.offset).left_biased()?;
120 let token = sema.descend_into_macros(original_token.clone()); 121 let token = sema.descend_into_macros(original_token.clone());
121 let scope = sema.scope_at_offset(&token.parent(), position.offset); 122 let scope = sema.scope_at_offset(&token.parent(), position.offset);
123 let mut locals = vec![];
124 scope.process_all_names(&mut |name, scope| {
125 if let ScopeDef::Local(local) = scope {
126 locals.push((name.to_string(), local));
127 }
128 });
122 let mut ctx = CompletionContext { 129 let mut ctx = CompletionContext {
123 sema, 130 sema,
124 scope, 131 scope,
@@ -167,6 +174,7 @@ impl<'a> CompletionContext<'a> {
167 if_is_prev: false, 174 if_is_prev: false,
168 is_match_arm: false, 175 is_match_arm: false,
169 has_item_list_or_source_file_parent: false, 176 has_item_list_or_source_file_parent: false,
177 locals,
170 }; 178 };
171 179
172 let mut original_file = original_file.syntax().clone(); 180 let mut original_file = original_file.syntax().clone();
diff --git a/crates/ide/src/completion/presentation.rs b/crates/ide/src/completion/presentation.rs
index 24c507f9b..987cbfa7a 100644
--- a/crates/ide/src/completion/presentation.rs
+++ b/crates/ide/src/completion/presentation.rs
@@ -191,6 +191,17 @@ impl Completions {
191 func: hir::Function, 191 func: hir::Function,
192 local_name: Option<String>, 192 local_name: Option<String>,
193 ) { 193 ) {
194 fn add_arg(arg: &str, ty: &Type, ctx: &CompletionContext) -> String {
195 if let Some(derefed_ty) = ty.remove_ref() {
196 for (name, local) in ctx.locals.iter() {
197 if name == arg && local.ty(ctx.db) == derefed_ty {
198 return (if ty.is_mutable_reference() { "&mut " } else { "&" }).to_string()
199 + &arg.to_string();
200 }
201 }
202 }
203 arg.to_string()
204 };
194 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string()); 205 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
195 let ast_node = func.source(ctx.db).value; 206 let ast_node = func.source(ctx.db).value;
196 207
@@ -205,12 +216,20 @@ impl Completions {
205 .set_deprecated(is_deprecated(func, ctx.db)) 216 .set_deprecated(is_deprecated(func, ctx.db))
206 .detail(function_declaration(&ast_node)); 217 .detail(function_declaration(&ast_node));
207 218
219 let params_ty = func.params(ctx.db);
208 let params = ast_node 220 let params = ast_node
209 .param_list() 221 .param_list()
210 .into_iter() 222 .into_iter()
211 .flat_map(|it| it.params()) 223 .flat_map(|it| it.params())
212 .flat_map(|it| it.pat()) 224 .zip(params_ty)
213 .map(|pat| pat.to_string().trim_start_matches('_').into()) 225 .flat_map(|(it, param_ty)| {
226 if let Some(pat) = it.pat() {
227 let name = pat.to_string();
228 let arg = name.trim_start_matches("mut ").trim_start_matches('_');
229 return Some(add_arg(arg, param_ty.ty(), ctx));
230 }
231 None
232 })
214 .collect(); 233 .collect();
215 234
216 builder = builder.add_call_parens(ctx, name, Params::Named(params)); 235 builder = builder.add_call_parens(ctx, name, Params::Named(params));
@@ -864,6 +883,106 @@ fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
864 } 883 }
865 884
866 #[test] 885 #[test]
886 fn insert_ref_when_matching_local_in_scope() {
887 check_edit(
888 "ref_arg",
889 r#"
890struct Foo {}
891fn ref_arg(x: &Foo) {}
892fn main() {
893 let x = Foo {};
894 ref_ar<|>
895}
896"#,
897 r#"
898struct Foo {}
899fn ref_arg(x: &Foo) {}
900fn main() {
901 let x = Foo {};
902 ref_arg(${1:&x})$0
903}
904"#,
905 );
906 }
907
908 #[test]
909 fn insert_mut_ref_when_matching_local_in_scope() {
910 check_edit(
911 "ref_arg",
912 r#"
913struct Foo {}
914fn ref_arg(x: &mut Foo) {}
915fn main() {
916 let x = Foo {};
917 ref_ar<|>
918}
919"#,
920 r#"
921struct Foo {}
922fn ref_arg(x: &mut Foo) {}
923fn main() {
924 let x = Foo {};
925 ref_arg(${1:&mut x})$0
926}
927"#,
928 );
929 }
930
931 #[test]
932 fn insert_ref_when_matching_local_in_scope_for_method() {
933 check_edit(
934 "apply_foo",
935 r#"
936struct Foo {}
937struct Bar {}
938impl Bar {
939 fn apply_foo(&self, x: &Foo) {}
940}
941
942fn main() {
943 let x = Foo {};
944 let y = Bar {};
945 y.<|>
946}
947"#,
948 r#"
949struct Foo {}
950struct Bar {}
951impl Bar {
952 fn apply_foo(&self, x: &Foo) {}
953}
954
955fn main() {
956 let x = Foo {};
957 let y = Bar {};
958 y.apply_foo(${1:&x})$0
959}
960"#,
961 );
962 }
963
964 #[test]
965 fn trim_mut_keyword_in_func_completion() {
966 check_edit(
967 "take_mutably",
968 r#"
969fn take_mutably(mut x: &i32) {}
970
971fn main() {
972 take_m<|>
973}
974"#,
975 r#"
976fn take_mutably(mut x: &i32) {}
977
978fn main() {
979 take_mutably(${1:x})$0
980}
981"#,
982 );
983 }
984
985 #[test]
867 fn inserts_parens_for_tuple_enums() { 986 fn inserts_parens_for_tuple_enums() {
868 mark::check!(inserts_parens_for_tuple_enums); 987 mark::check!(inserts_parens_for_tuple_enums);
869 check_edit( 988 check_edit(
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index b2b972b02..dc815a483 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -622,13 +622,65 @@ pub struct Foo { pub a: i32, pub b: i32 }
622 r#" 622 r#"
623use a; 623use a;
624use a::{c, d::e}; 624use a::{c, d::e};
625
626mod a {
627 mod c {}
628 mod d {
629 mod e {}
630 }
631}
625"#, 632"#,
626 ); 633 );
627 check_fix(r#"use {<|>b};"#, r#"use b;"#); 634 check_fix(
628 check_fix(r#"use {b<|>};"#, r#"use b;"#); 635 r"
629 check_fix(r#"use a::{c<|>};"#, r#"use a::c;"#); 636 mod b {}
630 check_fix(r#"use a::{self<|>};"#, r#"use a;"#); 637 use {<|>b};
631 check_fix(r#"use a::{c, d::{e<|>}};"#, r#"use a::{c, d::e};"#); 638 ",
639 r"
640 mod b {}
641 use b;
642 ",
643 );
644 check_fix(
645 r"
646 mod b {}
647 use {b<|>};
648 ",
649 r"
650 mod b {}
651 use b;
652 ",
653 );
654 check_fix(
655 r"
656 mod a { mod c {} }
657 use a::{c<|>};
658 ",
659 r"
660 mod a { mod c {} }
661 use a::c;
662 ",
663 );
664 check_fix(
665 r"
666 mod a {}
667 use a::{self<|>};
668 ",
669 r"
670 mod a {}
671 use a;
672 ",
673 );
674 check_fix(
675 r"
676 mod a { mod c {} mod d { mod e {} } }
677 use a::{c, d::{e<|>}};
678 ",
679 r"
680 mod a { mod c {} mod d { mod e {} } }
681 use a::{c, d::e};
682 ",
683 );
632 } 684 }
633 685
634 #[test] 686 #[test]
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index 288c39e49..258f60e28 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -308,7 +308,13 @@ impl ProjectWorkspace {
308 .crates() 308 .crates()
309 .filter_map(|(crate_id, krate)| { 309 .filter_map(|(crate_id, krate)| {
310 let file_path = &krate.root_module; 310 let file_path = &krate.root_module;
311 let file_id = load(&file_path)?; 311 let file_id = match load(&file_path) {
312 Some(id) => id,
313 None => {
314 log::error!("failed to load crate root {}", file_path.display());
315 return None;
316 }
317 };
312 318
313 let env = krate.env.clone().into_iter().collect(); 319 let env = krate.env.clone().into_iter().collect();
314 let proc_macro = krate 320 let proc_macro = krate
diff --git a/crates/project_model/src/project_json.rs b/crates/project_model/src/project_json.rs
index 545f254aa..a6895ecdd 100644
--- a/crates/project_model/src/project_json.rs
+++ b/crates/project_model/src/project_json.rs
@@ -13,7 +13,7 @@ use crate::cfg_flag::CfgFlag;
13#[derive(Clone, Debug, Eq, PartialEq)] 13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct ProjectJson { 14pub struct ProjectJson {
15 pub(crate) sysroot_src: Option<AbsPathBuf>, 15 pub(crate) sysroot_src: Option<AbsPathBuf>,
16 project_root: Option<AbsPathBuf>, 16 project_root: AbsPathBuf,
17 crates: Vec<Crate>, 17 crates: Vec<Crate>,
18} 18}
19 19
@@ -34,10 +34,17 @@ pub struct Crate {
34} 34}
35 35
36impl ProjectJson { 36impl ProjectJson {
37 /// Create a new ProjectJson instance.
38 ///
39 /// # Arguments
40 ///
41 /// * `base` - The path to the workspace root (i.e. the folder containing `rust-project.json`)
42 /// * `data` - The parsed contents of `rust-project.json`, or project json that's passed via
43 /// configuration.
37 pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { 44 pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson {
38 ProjectJson { 45 ProjectJson {
39 sysroot_src: data.sysroot_src.map(|it| base.join(it)), 46 sysroot_src: data.sysroot_src.map(|it| base.join(it)),
40 project_root: base.parent().map(AbsPath::to_path_buf), 47 project_root: base.to_path_buf(),
41 crates: data 48 crates: data
42 .crates 49 .crates
43 .into_iter() 50 .into_iter()
@@ -85,17 +92,17 @@ impl ProjectJson {
85 .collect::<Vec<_>>(), 92 .collect::<Vec<_>>(),
86 } 93 }
87 } 94 }
95 /// Returns the number of crates in the project.
88 pub fn n_crates(&self) -> usize { 96 pub fn n_crates(&self) -> usize {
89 self.crates.len() 97 self.crates.len()
90 } 98 }
99 /// Returns an iterator over the crates in the project.
91 pub fn crates(&self) -> impl Iterator<Item = (CrateId, &Crate)> + '_ { 100 pub fn crates(&self) -> impl Iterator<Item = (CrateId, &Crate)> + '_ {
92 self.crates.iter().enumerate().map(|(idx, krate)| (CrateId(idx as u32), krate)) 101 self.crates.iter().enumerate().map(|(idx, krate)| (CrateId(idx as u32), krate))
93 } 102 }
94 pub fn path(&self) -> Option<&AbsPath> { 103 /// Returns the path to the project's root folder.
95 match &self.project_root { 104 pub fn path(&self) -> &AbsPath {
96 Some(p) => Some(p.as_path()), 105 &self.project_root
97 None => None,
98 }
99 } 106 }
100} 107}
101 108
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 8db0b0d72..631ffc4a7 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -21,7 +21,7 @@ env_logger = { version = "0.7.1", default-features = false }
21itertools = "0.9.0" 21itertools = "0.9.0"
22jod-thread = "0.1.0" 22jod-thread = "0.1.0"
23log = "0.4.8" 23log = "0.4.8"
24lsp-types = { version = "0.80.0", features = ["proposed"] } 24lsp-types = { version = "0.82.0", features = ["proposed"] }
25parking_lot = "0.11.0" 25parking_lot = "0.11.0"
26pico-args = "0.3.1" 26pico-args = "0.3.1"
27oorandom = "11.1.2" 27oorandom = "11.1.2"
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index ba4402ade..97b246a32 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -134,6 +134,10 @@ fn run_server() -> Result<()> {
134 134
135 let discovered = ProjectManifest::discover_all(&workspace_roots); 135 let discovered = ProjectManifest::discover_all(&workspace_roots);
136 log::info!("discovered projects: {:?}", discovered); 136 log::info!("discovered projects: {:?}", discovered);
137 if discovered.is_empty() {
138 log::error!("failed to find any projects in {:?}", workspace_roots);
139 }
140
137 config.linked_projects = discovered.into_iter().map(LinkedProject::from).collect(); 141 config.linked_projects = discovered.into_iter().map(LinkedProject::from).collect();
138 } 142 }
139 143
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index de4bc2813..c589afeaf 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -5,7 +5,7 @@ use lsp_types::{
5 CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions, 5 CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
6 CodeActionProviderCapability, CodeLensOptions, CompletionOptions, 6 CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
7 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, HoverProviderCapability, 7 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, HoverProviderCapability,
8 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, 8 ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions,
9 SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend, 9 SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend,
10 SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, 10 SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
11 TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability, 11 TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
@@ -42,16 +42,16 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
42 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, 42 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
43 }), 43 }),
44 declaration_provider: None, 44 declaration_provider: None,
45 definition_provider: Some(true), 45 definition_provider: Some(OneOf::Left(true)),
46 type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)), 46 type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
47 implementation_provider: Some(ImplementationProviderCapability::Simple(true)), 47 implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
48 references_provider: Some(true), 48 references_provider: Some(OneOf::Left(true)),
49 document_highlight_provider: Some(true), 49 document_highlight_provider: Some(OneOf::Left(true)),
50 document_symbol_provider: Some(true), 50 document_symbol_provider: Some(OneOf::Left(true)),
51 workspace_symbol_provider: Some(true), 51 workspace_symbol_provider: Some(true),
52 code_action_provider: Some(code_action_provider), 52 code_action_provider: Some(code_action_provider),
53 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), 53 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
54 document_formatting_provider: Some(true), 54 document_formatting_provider: Some(OneOf::Left(true)),
55 document_range_formatting_provider: None, 55 document_range_formatting_provider: None,
56 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { 56 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
57 first_trigger_character: "=".to_string(), 57 first_trigger_character: "=".to_string(),
@@ -60,7 +60,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
60 selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), 60 selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
61 semantic_highlighting: None, 61 semantic_highlighting: None,
62 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), 62 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
63 rename_provider: Some(RenameProviderCapability::Options(RenameOptions { 63 rename_provider: Some(OneOf::Right(RenameOptions {
64 prepare_provider: Some(true), 64 prepare_provider: Some(true),
65 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, 65 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
66 })), 66 })),
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 1a74286f5..69d05aed5 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -288,7 +288,10 @@ impl Config {
288 let path = self.root_path.join(it); 288 let path = self.root_path.join(it);
289 match ProjectManifest::from_manifest_file(path) { 289 match ProjectManifest::from_manifest_file(path) {
290 Ok(it) => it.into(), 290 Ok(it) => it.into(),
291 Err(_) => continue, 291 Err(e) => {
292 log::error!("failed to load linked project: {}", e);
293 continue;
294 }
292 } 295 }
293 } 296 }
294 ManifestOrProjectJson::ProjectJson(it) => { 297 ManifestOrProjectJson::ProjectJson(it) => {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 64cb4d96c..c0943a54d 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -748,10 +748,15 @@ pub(crate) fn handle_formatting(
748 } 748 }
749 } 749 }
750 750
751 Ok(Some(vec![lsp_types::TextEdit { 751 if *file == captured_stdout {
752 range: Range::new(Position::new(0, 0), end_position), 752 // The document is already formatted correctly -- no edits needed.
753 new_text: captured_stdout, 753 Ok(None)
754 }])) 754 } else {
755 Ok(Some(vec![lsp_types::TextEdit {
756 range: Range::new(Position::new(0, 0), end_position),
757 new_text: captured_stdout,
758 }]))
759 }
755} 760}
756 761
757fn handle_fixes( 762fn handle_fixes(
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index b819618cb..b070087a4 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -201,11 +201,14 @@ impl GlobalState {
201 let mut crate_graph = CrateGraph::default(); 201 let mut crate_graph = CrateGraph::default();
202 let vfs = &mut self.vfs.write().0; 202 let vfs = &mut self.vfs.write().0;
203 let loader = &mut self.loader; 203 let loader = &mut self.loader;
204 let mem_docs = &self.mem_docs;
204 let mut load = |path: &AbsPath| { 205 let mut load = |path: &AbsPath| {
205 let contents = loader.handle.load_sync(path); 206 let vfs_path = vfs::VfsPath::from(path.to_path_buf());
206 let path = vfs::VfsPath::from(path.to_path_buf()); 207 if !mem_docs.contains_key(&vfs_path) {
207 vfs.set_file_contents(path.clone(), contents); 208 let contents = loader.handle.load_sync(path);
208 vfs.file_id(&path) 209 vfs.set_file_contents(vfs_path.clone(), contents);
210 }
211 vfs.file_id(&vfs_path)
209 }; 212 };
210 for ws in workspaces.iter() { 213 for ws in workspaces.iter() {
211 crate_graph.extend(ws.to_crate_graph( 214 crate_graph.extend(ws.to_crate_graph(
@@ -249,7 +252,7 @@ impl GlobalState {
249 // Enable flychecks for json projects if a custom flycheck command was supplied 252 // Enable flychecks for json projects if a custom flycheck command was supplied
250 // in the workspace configuration. 253 // in the workspace configuration.
251 match config { 254 match config {
252 FlycheckConfig::CustomCommand { .. } => project.path(), 255 FlycheckConfig::CustomCommand { .. } => Some(project.path()),
253 _ => None, 256 _ => None,
254 } 257 }
255 } 258 }
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index dcbf837d6..59e780b7d 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -285,12 +285,18 @@ pub(crate) fn signature_help(
285 }) 285 })
286 }; 286 };
287 287
288 let signature = 288 let active_parameter = call_info.active_parameter.map(|it| it as i64);
289 lsp_types::SignatureInformation { label, documentation, parameters: Some(parameters) }; 289
290 let signature = lsp_types::SignatureInformation {
291 label,
292 documentation,
293 parameters: Some(parameters),
294 active_parameter,
295 };
290 lsp_types::SignatureHelp { 296 lsp_types::SignatureHelp {
291 signatures: vec![signature], 297 signatures: vec![signature],
292 active_signature: None, 298 active_signature: None,
293 active_parameter: call_info.active_parameter.map(|it| it as i64), 299 active_parameter,
294 } 300 }
295} 301}
296 302
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs
index 0880d0425..06726f957 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/main.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs
@@ -260,6 +260,42 @@ pub use std::collections::HashMap;
260} 260}
261 261
262#[test] 262#[test]
263fn test_format_document_unchanged() {
264 if skip_slow_tests() {
265 return;
266 }
267
268 let server = project(
269 r#"
270//- /Cargo.toml
271[package]
272name = "foo"
273version = "0.0.0"
274
275//- /src/lib.rs
276fn main() {}
277"#,
278 )
279 .wait_until_workspace_is_loaded();
280
281 server.request::<Formatting>(
282 DocumentFormattingParams {
283 text_document: server.doc_id("src/lib.rs"),
284 options: FormattingOptions {
285 tab_size: 4,
286 insert_spaces: false,
287 insert_final_newline: None,
288 trim_final_newlines: None,
289 trim_trailing_whitespace: None,
290 properties: HashMap::new(),
291 },
292 work_done_progress_params: WorkDoneProgressParams::default(),
293 },
294 json!(null),
295 );
296}
297
298#[test]
263fn test_missing_module_code_action() { 299fn test_missing_module_code_action() {
264 if skip_slow_tests() { 300 if skip_slow_tests() {
265 return; 301 return;
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 45cf31f13..dda0a0319 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -93,6 +93,22 @@ where
93 } 93 }
94} 94}
95 95
96impl ast::Impl {
97 #[must_use]
98 pub fn with_assoc_item_list(&self, items: ast::AssocItemList) -> ast::Impl {
99 let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
100 if let Some(old_items) = self.assoc_item_list() {
101 let to_replace: SyntaxElement = old_items.syntax().clone().into();
102 to_insert.push(items.syntax().clone().into());
103 self.replace_children(single_node(to_replace), to_insert)
104 } else {
105 to_insert.push(make::tokens::single_space().into());
106 to_insert.push(items.syntax().clone().into());
107 self.insert_children(InsertPosition::Last, to_insert)
108 }
109 }
110}
111
96impl ast::AssocItemList { 112impl ast::AssocItemList {
97 #[must_use] 113 #[must_use]
98 pub fn append_items( 114 pub fn append_items(
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 6868feed9..4a0ffcbb0 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -21,6 +21,10 @@ pub fn ty(text: &str) -> ast::Type {
21 ast_from_text(&format!("impl {} for D {{}};", text)) 21 ast_from_text(&format!("impl {} for D {{}};", text))
22} 22}
23 23
24pub fn assoc_item_list() -> ast::AssocItemList {
25 ast_from_text("impl C for D {};")
26}
27
24pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { 28pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
25 ast_from_text(&format!("use {};", name_ref)) 29 ast_from_text(&format!("use {};", name_ref))
26} 30}