aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock20
-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
-rw-r--r--docs/dev/syntax.md2
-rw-r--r--docs/user/manual.adoc5
-rw-r--r--editors/code/package.json9
-rw-r--r--editors/code/src/commands.ts6
-rw-r--r--editors/code/src/lsp_ext.ts6
-rw-r--r--editors/code/src/main.ts98
-rw-r--r--editors/code/src/net.ts18
-rw-r--r--editors/code/src/persistent_state.ts11
43 files changed, 1109 insertions, 334 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 72ec68624..7fecee1b5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -140,9 +140,9 @@ dependencies = [
140 140
141[[package]] 141[[package]]
142name = "cc" 142name = "cc"
143version = "1.0.59" 143version = "1.0.60"
144source = "registry+https://github.com/rust-lang/crates.io-index" 144source = "registry+https://github.com/rust-lang/crates.io-index"
145checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" 145checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c"
146 146
147[[package]] 147[[package]]
148name = "cfg" 148name = "cfg"
@@ -311,9 +311,9 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1"
311 311
312[[package]] 312[[package]]
313name = "either" 313name = "either"
314version = "1.6.0" 314version = "1.6.1"
315source = "registry+https://github.com/rust-lang/crates.io-index" 315source = "registry+https://github.com/rust-lang/crates.io-index"
316checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" 316checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
317 317
318[[package]] 318[[package]]
319name = "ena" 319name = "ena"
@@ -775,9 +775,9 @@ dependencies = [
775 775
776[[package]] 776[[package]]
777name = "lsp-types" 777name = "lsp-types"
778version = "0.80.0" 778version = "0.82.0"
779source = "registry+https://github.com/rust-lang/crates.io-index" 779source = "registry+https://github.com/rust-lang/crates.io-index"
780checksum = "f4265e2715bdacbb4dad029fce525e420cd66dc0af24ff9cb996a8ab48ac92ef" 780checksum = "db895abb8527cf59e3de893ab2acf52cf904faeb65e60ea6f373e11fe86464e8"
781dependencies = [ 781dependencies = [
782 "base64", 782 "base64",
783 "bitflags", 783 "bitflags",
@@ -1187,9 +1187,9 @@ dependencies = [
1187 1187
1188[[package]] 1188[[package]]
1189name = "rayon-core" 1189name = "rayon-core"
1190version = "1.8.0" 1190version = "1.8.1"
1191source = "registry+https://github.com/rust-lang/crates.io-index" 1191source = "registry+https://github.com/rust-lang/crates.io-index"
1192checksum = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0" 1192checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf"
1193dependencies = [ 1193dependencies = [
1194 "crossbeam-channel", 1194 "crossbeam-channel",
1195 "crossbeam-deque", 1195 "crossbeam-deque",
@@ -1493,9 +1493,9 @@ version = "0.0.0"
1493 1493
1494[[package]] 1494[[package]]
1495name = "syn" 1495name = "syn"
1496version = "1.0.40" 1496version = "1.0.41"
1497source = "registry+https://github.com/rust-lang/crates.io-index" 1497source = "registry+https://github.com/rust-lang/crates.io-index"
1498checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350" 1498checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b"
1499dependencies = [ 1499dependencies = [
1500 "proc-macro2", 1500 "proc-macro2",
1501 "quote", 1501 "quote",
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}
diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md
index c08062ef4..2eb08b7ca 100644
--- a/docs/dev/syntax.md
+++ b/docs/dev/syntax.md
@@ -72,7 +72,7 @@ Points of note:
72* Trivia and non-trivia tokens are not distinguished on the type level. 72* Trivia and non-trivia tokens are not distinguished on the type level.
73* Each token carries its full text. 73* Each token carries its full text.
74* The original text can be recovered by concatenating the texts of all tokens in order. 74* The original text can be recovered by concatenating the texts of all tokens in order.
75* Accessing a child of particular type (for example, parameter list of a function) generally involves linerary traversing the children, looking for a specific `kind`. 75* Accessing a child of particular type (for example, parameter list of a function) generally involves linearly traversing the children, looking for a specific `kind`.
76* Modifying the tree is roughly `O(depth)`. 76* Modifying the tree is roughly `O(depth)`.
77 We don't make special efforts to guarantee that the depth is not linear, but, in practice, syntax trees are branchy and shallow. 77 We don't make special efforts to guarantee that the depth is not linear, but, in practice, syntax trees are branchy and shallow.
78* If mandatory (grammar wise) node is missing from the input, it's just missing from the tree. 78* If mandatory (grammar wise) node is missing from the input, it's just missing from the tree.
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index b15c9ee7f..d3e6b23ae 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -44,6 +44,11 @@ https://github.com/rust-analyzer/rust-analyzer/tree/master/editors/code[in tree]
44 44
45You can install the latest release of the plugin from 45You can install the latest release of the plugin from
46https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer[the marketplace]. 46https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer[the marketplace].
47
48Note that the plugin may cause conflicts with the
49https://marketplace.visualstudio.com/items?itemName=rust-lang.rust[official Rust plugin]. It is
50recommended to disable the Rust plugin when using the rust-analyzer extension.
51
47By default, the plugin will prompt you to download the matching version of the server as well: 52By default, the plugin will prompt you to download the matching version of the server as well:
48 53
49image::https://user-images.githubusercontent.com/9021944/75067008-17502500-54ba-11ea-835a-f92aac50e866.png[] 54image::https://user-images.githubusercontent.com/9021944/75067008-17502500-54ba-11ea-835a-f92aac50e866.png[]
diff --git a/editors/code/package.json b/editors/code/package.json
index c57fbdda2..132664926 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -159,6 +159,11 @@
159 "category": "Rust Analyzer" 159 "category": "Rust Analyzer"
160 }, 160 },
161 { 161 {
162 "command": "rust-analyzer.updateGithubToken",
163 "title": "Update Github API token",
164 "category": "Rust Analyzer"
165 },
166 {
162 "command": "rust-analyzer.onEnter", 167 "command": "rust-analyzer.onEnter",
163 "title": "Enhanced enter key", 168 "title": "Enhanced enter key",
164 "category": "Rust Analyzer" 169 "category": "Rust Analyzer"
@@ -985,6 +990,10 @@
985 "when": "inRustProject" 990 "when": "inRustProject"
986 }, 991 },
987 { 992 {
993 "command": "rust-analyzer.updateGithubToken",
994 "when": "inRustProject"
995 },
996 {
988 "command": "rust-analyzer.onEnter", 997 "command": "rust-analyzer.onEnter",
989 "when": "inRustProject" 998 "when": "inRustProject"
990 }, 999 },
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 4321de244..e9581a9b5 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -21,7 +21,7 @@ export function analyzerStatus(ctx: Ctx): Cmd {
21 provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> { 21 provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
22 if (!vscode.window.activeTextEditor) return ''; 22 if (!vscode.window.activeTextEditor) return '';
23 23
24 return ctx.client.sendRequest(ra.analyzerStatus, null); 24 return ctx.client.sendRequest(ra.analyzerStatus);
25 } 25 }
26 26
27 get onDidChange(): vscode.Event<vscode.Uri> { 27 get onDidChange(): vscode.Event<vscode.Uri> {
@@ -63,7 +63,7 @@ export function memoryUsage(ctx: Ctx): Cmd {
63 provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> { 63 provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
64 if (!vscode.window.activeTextEditor) return ''; 64 if (!vscode.window.activeTextEditor) return '';
65 65
66 return ctx.client.sendRequest(ra.memoryUsage, null).then((mem: any) => { 66 return ctx.client.sendRequest(ra.memoryUsage).then((mem: any) => {
67 return 'Per-query memory usage:\n' + mem + '\n(note: database has been cleared)'; 67 return 'Per-query memory usage:\n' + mem + '\n(note: database has been cleared)';
68 }); 68 });
69 } 69 }
@@ -372,7 +372,7 @@ export function expandMacro(ctx: Ctx): Cmd {
372} 372}
373 373
374export function reloadWorkspace(ctx: Ctx): Cmd { 374export function reloadWorkspace(ctx: Ctx): Cmd {
375 return async () => ctx.client.sendRequest(ra.reloadWorkspace, null); 375 return async () => ctx.client.sendRequest(ra.reloadWorkspace);
376} 376}
377 377
378export function showReferences(ctx: Ctx): Cmd { 378export function showReferences(ctx: Ctx): Cmd {
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index f280bba3d..d167041c4 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -4,8 +4,8 @@
4 4
5import * as lc from "vscode-languageclient"; 5import * as lc from "vscode-languageclient";
6 6
7export const analyzerStatus = new lc.RequestType<null, string, void>("rust-analyzer/analyzerStatus"); 7export const analyzerStatus = new lc.RequestType0<string, void>("rust-analyzer/analyzerStatus");
8export const memoryUsage = new lc.RequestType<null, string, void>("rust-analyzer/memoryUsage"); 8export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage");
9 9
10export type Status = "loading" | "ready" | "invalid" | "needsReload"; 10export type Status = "loading" | "ready" | "invalid" | "needsReload";
11export interface StatusParams { 11export interface StatusParams {
@@ -13,7 +13,7 @@ export interface StatusParams {
13} 13}
14export const status = new lc.NotificationType<StatusParams>("rust-analyzer/status"); 14export const status = new lc.NotificationType<StatusParams>("rust-analyzer/status");
15 15
16export const reloadWorkspace = new lc.RequestType<null, null, void>("rust-analyzer/reloadWorkspace"); 16export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace");
17 17
18export interface SyntaxTreeParams { 18export interface SyntaxTreeParams {
19 textDocument: lc.TextDocumentIdentifier; 19 textDocument: lc.TextDocumentIdentifier;
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index bd99d696a..2896d90ac 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -95,6 +95,10 @@ async function tryActivate(context: vscode.ExtensionContext) {
95 await activate(context).catch(log.error); 95 await activate(context).catch(log.error);
96 }); 96 });
97 97
98 ctx.registerCommand('updateGithubToken', ctx => async () => {
99 await queryForGithubToken(new PersistentState(ctx.globalState));
100 });
101
98 ctx.registerCommand('analyzerStatus', commands.analyzerStatus); 102 ctx.registerCommand('analyzerStatus', commands.analyzerStatus);
99 ctx.registerCommand('memoryUsage', commands.memoryUsage); 103 ctx.registerCommand('memoryUsage', commands.memoryUsage);
100 ctx.registerCommand('reloadWorkspace', commands.reloadWorkspace); 104 ctx.registerCommand('reloadWorkspace', commands.reloadWorkspace);
@@ -173,7 +177,9 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
173 if (!shouldCheckForNewNightly) return; 177 if (!shouldCheckForNewNightly) return;
174 } 178 }
175 179
176 const release = await fetchRelease("nightly").catch((e) => { 180 const release = await downloadWithRetryDialog(state, async () => {
181 return await fetchRelease("nightly", state.githubToken);
182 }).catch((e) => {
177 log.error(e); 183 log.error(e);
178 if (state.releaseId === undefined) { // Show error only for the initial download 184 if (state.releaseId === undefined) { // Show error only for the initial download
179 vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly ${e}`); 185 vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly ${e}`);
@@ -192,10 +198,14 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
192 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 198 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
193 199
194 const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix"); 200 const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix");
195 await download({ 201
196 url: artifact.browser_download_url, 202 await downloadWithRetryDialog(state, async () => {
197 dest, 203 await download({
198 progressTitle: "Downloading rust-analyzer extension", 204 url: artifact.browser_download_url,
205 dest,
206 progressTitle: "Downloading rust-analyzer extension",
207 overwrite: true,
208 });
199 }); 209 });
200 210
201 await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest)); 211 await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest));
@@ -308,21 +318,22 @@ async function getServer(config: Config, state: PersistentState): Promise<string
308 if (userResponse !== "Download now") return dest; 318 if (userResponse !== "Download now") return dest;
309 } 319 }
310 320
311 const release = await fetchRelease(config.package.releaseTag); 321 const releaseTag = config.package.releaseTag;
322 const release = await downloadWithRetryDialog(state, async () => {
323 return await fetchRelease(releaseTag, state.githubToken);
324 });
312 const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`); 325 const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`);
313 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 326 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
314 327
315 // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error. 328 await downloadWithRetryDialog(state, async () => {
316 await fs.unlink(dest).catch(err => { 329 await download({
317 if (err.code !== "ENOENT") throw err; 330 url: artifact.browser_download_url,
318 }); 331 dest,
319 332 progressTitle: "Downloading rust-analyzer server",
320 await download({ 333 gunzip: true,
321 url: artifact.browser_download_url, 334 mode: 0o755,
322 dest, 335 overwrite: true,
323 progressTitle: "Downloading rust-analyzer server", 336 });
324 gunzip: true,
325 mode: 0o755
326 }); 337 });
327 338
328 // Patching executable if that's NixOS. 339 // Patching executable if that's NixOS.
@@ -333,3 +344,56 @@ async function getServer(config: Config, state: PersistentState): Promise<string
333 await state.updateServerVersion(config.package.version); 344 await state.updateServerVersion(config.package.version);
334 return dest; 345 return dest;
335} 346}
347
348async function downloadWithRetryDialog<T>(state: PersistentState, downloadFunc: () => Promise<T>): Promise<T> {
349 while (true) {
350 try {
351 return await downloadFunc();
352 } catch (e) {
353 const selected = await vscode.window.showErrorMessage("Failed to download: " + e.message, {}, {
354 title: "Update Github Auth Token",
355 updateToken: true,
356 }, {
357 title: "Retry download",
358 retry: true,
359 }, {
360 title: "Dismiss",
361 });
362
363 if (selected?.updateToken) {
364 await queryForGithubToken(state);
365 continue;
366 } else if (selected?.retry) {
367 continue;
368 }
369 throw e;
370 };
371 }
372}
373
374async function queryForGithubToken(state: PersistentState): Promise<void> {
375 const githubTokenOptions: vscode.InputBoxOptions = {
376 value: state.githubToken,
377 password: true,
378 prompt: `
379 This dialog allows to store a Github authorization token.
380 The usage of an authorization token will increase the rate
381 limit on the use of Github APIs and can thereby prevent getting
382 throttled.
383 Auth tokens can be created at https://github.com/settings/tokens`,
384 };
385
386 const newToken = await vscode.window.showInputBox(githubTokenOptions);
387 if (newToken === undefined) {
388 // The user aborted the dialog => Do not update the stored token
389 return;
390 }
391
392 if (newToken === "") {
393 log.info("Clearing github token");
394 await state.updateGithubToken(undefined);
395 } else {
396 log.info("Storing new github token");
397 await state.updateGithubToken(newToken);
398 }
399}
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
index 5eba2728d..9ba17b7b5 100644
--- a/editors/code/src/net.ts
+++ b/editors/code/src/net.ts
@@ -18,7 +18,8 @@ const OWNER = "rust-analyzer";
18const REPO = "rust-analyzer"; 18const REPO = "rust-analyzer";
19 19
20export async function fetchRelease( 20export async function fetchRelease(
21 releaseTag: string 21 releaseTag: string,
22 githubToken: string | null | undefined,
22): Promise<GithubRelease> { 23): Promise<GithubRelease> {
23 24
24 const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`; 25 const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`;
@@ -27,7 +28,12 @@ export async function fetchRelease(
27 28
28 log.debug("Issuing request for released artifacts metadata to", requestUrl); 29 log.debug("Issuing request for released artifacts metadata to", requestUrl);
29 30
30 const response = await fetch(requestUrl, { headers: { Accept: "application/vnd.github.v3+json" } }); 31 const headers: Record<string, string> = { Accept: "application/vnd.github.v3+json" };
32 if (githubToken != null) {
33 headers.Authorization = "token " + githubToken;
34 }
35
36 const response = await fetch(requestUrl, { headers: headers });
31 37
32 if (!response.ok) { 38 if (!response.ok) {
33 log.error("Error fetching artifact release info", { 39 log.error("Error fetching artifact release info", {
@@ -70,6 +76,7 @@ interface DownloadOpts {
70 dest: string; 76 dest: string;
71 mode?: number; 77 mode?: number;
72 gunzip?: boolean; 78 gunzip?: boolean;
79 overwrite?: boolean;
73} 80}
74 81
75export async function download(opts: DownloadOpts) { 82export async function download(opts: DownloadOpts) {
@@ -79,6 +86,13 @@ export async function download(opts: DownloadOpts) {
79 const randomHex = crypto.randomBytes(5).toString("hex"); 86 const randomHex = crypto.randomBytes(5).toString("hex");
80 const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`); 87 const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`);
81 88
89 if (opts.overwrite) {
90 // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error.
91 await fs.promises.unlink(opts.dest).catch(err => {
92 if (err.code !== "ENOENT") throw err;
93 });
94 }
95
82 await vscode.window.withProgress( 96 await vscode.window.withProgress(
83 { 97 {
84 location: vscode.ProgressLocation.Notification, 98 location: vscode.ProgressLocation.Notification,
diff --git a/editors/code/src/persistent_state.ts b/editors/code/src/persistent_state.ts
index 5705eed81..afb652589 100644
--- a/editors/code/src/persistent_state.ts
+++ b/editors/code/src/persistent_state.ts
@@ -38,4 +38,15 @@ export class PersistentState {
38 async updateServerVersion(value: string | undefined) { 38 async updateServerVersion(value: string | undefined) {
39 await this.globalState.update("serverVersion", value); 39 await this.globalState.update("serverVersion", value);
40 } 40 }
41
42 /**
43 * Github authorization token.
44 * This is used for API requests against the Github API.
45 */
46 get githubToken(): string | undefined {
47 return this.globalState.get("githubToken");
48 }
49 async updateGithubToken(value: string | undefined) {
50 await this.globalState.update("githubToken", value);
51 }
41} 52}