aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/ast_transform.rs28
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs75
-rw-r--r--crates/assists/src/handlers/auto_import.rs33
-rw-r--r--crates/assists/src/handlers/invert_if.rs13
-rw-r--r--crates/assists/src/handlers/merge_imports.rs14
-rw-r--r--crates/assists/src/handlers/replace_qualified_name_with_use.rs20
-rw-r--r--crates/assists/src/utils/insert_use.rs346
-rw-r--r--crates/flycheck/src/lib.rs24
-rw-r--r--crates/hir/src/code_model.rs50
-rw-r--r--crates/hir/src/semantics.rs19
-rw-r--r--crates/hir_def/src/diagnostics.rs49
-rw-r--r--crates/hir_def/src/find_path.rs170
-rw-r--r--crates/hir_def/src/item_scope.rs22
-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.rs508
-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/macros.rs73
-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/hir_expand/src/name.rs10
-rw-r--r--crates/hir_expand/src/proc_macro.rs39
-rw-r--r--crates/hir_ty/Cargo.toml6
-rw-r--r--crates/hir_ty/src/infer.rs2
-rw-r--r--crates/hir_ty/src/traits/chalk.rs10
-rw-r--r--crates/ide/src/completion/complete_postfix.rs17
-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/ide/src/syntax_highlighting/test_data/highlighting.html6
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs6
-rw-r--r--crates/mbe/src/syntax_bridge.rs5
-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/global_state.rs4
-rw-r--r--crates/rust-analyzer/src/handlers.rs13
-rw-r--r--crates/rust-analyzer/src/main_loop.rs17
-rw-r--r--crates/rust-analyzer/src/reload.rs33
-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.rs17
-rw-r--r--crates/syntax/src/ast/make.rs8
54 files changed, 1756 insertions, 516 deletions
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs
index bbcd2d488..835da3bb2 100644
--- a/crates/assists/src/ast_transform.rs
+++ b/crates/assists/src/ast_transform.rs
@@ -18,6 +18,34 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
18 .rewrite_ast(&node) 18 .rewrite_ast(&node)
19} 19}
20 20
21/// `AstTransform` helps with applying bulk transformations to syntax nodes.
22///
23/// This is mostly useful for IDE code generation. If you paste some existing
24/// code into a new context (for example, to add method overrides to an `impl`
25/// block), you generally want to appropriately qualify the names, and sometimes
26/// you might want to substitute generic parameters as well:
27///
28/// ```
29/// mod x {
30/// pub struct A;
31/// pub trait T<U> { fn foo(&self, _: U) -> A; }
32/// }
33///
34/// mod y {
35/// use x::T;
36///
37/// impl T<()> for () {
38/// // If we invoke **Add Missing Members** here, we want to copy-paste `foo`.
39/// // But we want a slightly-modified version of it:
40/// fn foo(&self, _: ()) -> x::A {}
41/// }
42/// }
43/// ```
44///
45/// So, a single `AstTransform` describes such function from `SyntaxNode` to
46/// `SyntaxNode`. Note that the API here is a bit too high-order and high-brow.
47/// We'd want to somehow express this concept simpler, but so far nobody got to
48/// simplifying this!
21pub trait AstTransform<'a> { 49pub trait AstTransform<'a> {
22 fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode>; 50 fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode>;
23 51
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs
index 83a2ada9a..1ac5fefd6 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,
@@ -393,6 +415,41 @@ impl foo::Foo for S {
393 } 415 }
394 416
395 #[test] 417 #[test]
418 fn test_qualify_path_2() {
419 check_assist(
420 add_missing_impl_members,
421 r#"
422mod foo {
423 pub mod bar {
424 pub struct Bar;
425 pub trait Foo { fn foo(&self, bar: Bar); }
426 }
427}
428
429use foo::bar;
430
431struct S;
432impl bar::Foo for S { <|> }"#,
433 r#"
434mod foo {
435 pub mod bar {
436 pub struct Bar;
437 pub trait Foo { fn foo(&self, bar: Bar); }
438 }
439}
440
441use foo::bar;
442
443struct S;
444impl bar::Foo for S {
445 fn foo(&self, bar: bar::Bar) {
446 ${0:todo!()}
447 }
448}"#,
449 );
450 }
451
452 #[test]
396 fn test_qualify_path_generic() { 453 fn test_qualify_path_generic() {
397 check_assist( 454 check_assist(
398 add_missing_impl_members, 455 add_missing_impl_members,
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index 5a1b5a4ac..fa524ffd9 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -192,10 +192,10 @@ impl AutoImportAssets {
192 }) 192 })
193 .filter_map(|candidate| match candidate { 193 .filter_map(|candidate| match candidate {
194 Either::Left(module_def) => { 194 Either::Left(module_def) => {
195 self.module_with_name_to_import.find_use_path(db, module_def) 195 self.module_with_name_to_import.find_use_path_prefixed(db, module_def)
196 } 196 }
197 Either::Right(macro_def) => { 197 Either::Right(macro_def) => {
198 self.module_with_name_to_import.find_use_path(db, macro_def) 198 self.module_with_name_to_import.find_use_path_prefixed(db, macro_def)
199 } 199 }
200 }) 200 })
201 .filter(|use_path| !use_path.segments.is_empty()) 201 .filter(|use_path| !use_path.segments.is_empty())
@@ -287,6 +287,35 @@ mod tests {
287 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 287 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
288 288
289 #[test] 289 #[test]
290 fn applicable_when_found_an_import_partial() {
291 check_assist(
292 auto_import,
293 r"
294 mod std {
295 pub mod fmt {
296 pub struct Formatter;
297 }
298 }
299
300 use std::fmt;
301
302 <|>Formatter
303 ",
304 r"
305 mod std {
306 pub mod fmt {
307 pub struct Formatter;
308 }
309 }
310
311 use std::fmt::{self, Formatter};
312
313 Formatter
314 ",
315 );
316 }
317
318 #[test]
290 fn applicable_when_found_an_import() { 319 fn applicable_when_found_an_import() {
291 check_assist( 320 check_assist(
292 auto_import, 321 auto_import,
diff --git a/crates/assists/src/handlers/invert_if.rs b/crates/assists/src/handlers/invert_if.rs
index 294256297..461fcf862 100644
--- a/crates/assists/src/handlers/invert_if.rs
+++ b/crates/assists/src/handlers/invert_if.rs
@@ -49,13 +49,14 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
49 ast::ElseBranch::IfExpr(_) => return None, 49 ast::ElseBranch::IfExpr(_) => return None,
50 }; 50 };
51 51
52 let cond_range = cond.syntax().text_range();
53 let flip_cond = invert_boolean_expression(cond);
54 let else_node = else_block.syntax();
55 let else_range = else_node.text_range();
56 let then_range = then_node.text_range();
57 acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| { 52 acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| {
58 edit.replace(cond_range, flip_cond.syntax().text()); 53 let flip_cond = invert_boolean_expression(cond.clone());
54 edit.replace_ast(cond, flip_cond);
55
56 let else_node = else_block.syntax();
57 let else_range = else_node.text_range();
58 let then_range = then_node.text_range();
59
59 edit.replace(else_range, then_node.text()); 60 edit.replace(else_range, then_node.text());
60 edit.replace(then_range, else_node.text()); 61 edit.replace(then_range, else_node.text());
61 }) 62 })
diff --git a/crates/assists/src/handlers/merge_imports.rs b/crates/assists/src/handlers/merge_imports.rs
index 0bd679260..fe33cee53 100644
--- a/crates/assists/src/handlers/merge_imports.rs
+++ b/crates/assists/src/handlers/merge_imports.rs
@@ -95,7 +95,7 @@ use std::fmt::Debug;
95use std::fmt<|>::Display; 95use std::fmt<|>::Display;
96", 96",
97 r" 97 r"
98use std::fmt::{Display, Debug}; 98use std::fmt::{Debug, Display};
99", 99",
100 ); 100 );
101 } 101 }
@@ -122,7 +122,7 @@ use std::fmt::{self, Display};
122use std::{fmt, <|>fmt::Display}; 122use std::{fmt, <|>fmt::Display};
123", 123",
124 r" 124 r"
125use std::{fmt::{Display, self}}; 125use std::{fmt::{self, Display}};
126", 126",
127 ); 127 );
128 } 128 }
@@ -210,13 +210,17 @@ use std::{fmt<|>::Debug, fmt::Display};
210use std::{fmt::{Debug, Display}}; 210use std::{fmt::{Debug, Display}};
211", 211",
212 ); 212 );
213 }
214
215 #[test]
216 fn test_merge_nested2() {
213 check_assist( 217 check_assist(
214 merge_imports, 218 merge_imports,
215 r" 219 r"
216use std::{fmt::Debug, fmt<|>::Display}; 220use std::{fmt::Debug, fmt<|>::Display};
217", 221",
218 r" 222 r"
219use std::{fmt::{Display, Debug}}; 223use std::{fmt::{Debug, Display}};
220", 224",
221 ); 225 );
222 } 226 }
@@ -310,9 +314,7 @@ use foo::<|>{
310}; 314};
311", 315",
312 r" 316 r"
313use foo::{ 317use foo::{FooBar, bar::baz};
314 FooBar,
315bar::baz};
316", 318",
317 ) 319 )
318 } 320 }
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
index 27b49455c..74afc123b 100644
--- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
@@ -298,7 +298,7 @@ impl std::fmt<|> for Foo {
298} 298}
299 ", 299 ",
300 r" 300 r"
301use std::fmt::{Debug, self}; 301use std::fmt::{self, Debug};
302 302
303impl fmt for Foo { 303impl fmt for Foo {
304} 304}
@@ -316,9 +316,8 @@ use std::fmt::{Debug, nested::{Display}};
316impl std::fmt::nested<|> for Foo { 316impl std::fmt::nested<|> for Foo {
317} 317}
318", 318",
319 // FIXME(veykril): should be nested::{self, Display} here
320 r" 319 r"
321use std::fmt::{Debug, nested::{Display}, nested}; 320use std::fmt::{Debug, nested::{self, Display}};
322 321
323impl nested for Foo { 322impl nested for Foo {
324} 323}
@@ -336,9 +335,8 @@ use std::fmt::{Debug, nested::{self, Display}};
336impl std::fmt::nested<|> for Foo { 335impl std::fmt::nested<|> for Foo {
337} 336}
338", 337",
339 // FIXME(veykril): nested is duplicated now
340 r" 338 r"
341use std::fmt::{Debug, nested::{self, Display}, nested}; 339use std::fmt::{Debug, nested::{self, Display}};
342 340
343impl nested for Foo { 341impl nested for Foo {
344} 342}
@@ -357,7 +355,7 @@ impl std::fmt::nested::Debug<|> for Foo {
357} 355}
358", 356",
359 r" 357 r"
360use std::fmt::{Debug, nested::{Display}, nested::Debug}; 358use std::fmt::{Debug, nested::{Debug, Display}};
361 359
362impl Debug for Foo { 360impl Debug for Foo {
363} 361}
@@ -395,7 +393,7 @@ impl std::fmt::Display<|> for Foo {
395} 393}
396", 394",
397 r" 395 r"
398use std::fmt::{nested::Debug, Display}; 396use std::fmt::{Display, nested::Debug};
399 397
400impl Display for Foo { 398impl Display for Foo {
401} 399}
@@ -415,12 +413,8 @@ use crate::{
415 413
416fn foo() { crate::ty::lower<|>::trait_env() } 414fn foo() { crate::ty::lower<|>::trait_env() }
417", 415",
418 // FIXME(veykril): formatting broke here
419 r" 416 r"
420use crate::{ 417use crate::{AssocItem, ty::{Substs, Ty, lower}};
421 ty::{Substs, Ty},
422 AssocItem,
423ty::lower};
424 418
425fn foo() { lower::trait_env() } 419fn foo() { lower::trait_env() }
426", 420",
@@ -619,7 +613,7 @@ fn main() {
619} 613}
620 ", 614 ",
621 r" 615 r"
622use std::fmt::{Display, self}; 616use std::fmt::{self, Display};
623 617
624fn main() { 618fn main() {
625 fmt; 619 fmt;
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index 6d110aaaf..5719b06af 100644
--- a/crates/assists/src/utils/insert_use.rs
+++ b/crates/assists/src/utils/insert_use.rs
@@ -1,7 +1,9 @@
1//! Handle syntactic aspects of inserting a new `use`. 1//! Handle syntactic aspects of inserting a new `use`.
2use std::iter::{self, successors}; 2use std::{
3 cmp::Ordering,
4 iter::{self, successors},
5};
3 6
4use algo::skip_trivia_token;
5use ast::{ 7use ast::{
6 edit::{AstNodeEdit, IndentLevel}, 8 edit::{AstNodeEdit, IndentLevel},
7 PathSegmentKind, VisibilityOwner, 9 PathSegmentKind, VisibilityOwner,
@@ -9,9 +11,8 @@ use ast::{
9use syntax::{ 11use syntax::{
10 algo, 12 algo,
11 ast::{self, make, AstNode}, 13 ast::{self, make, AstNode},
12 Direction, InsertPosition, SyntaxElement, SyntaxNode, T, 14 InsertPosition, SyntaxElement, SyntaxNode,
13}; 15};
14use test_utils::mark;
15 16
16#[derive(Debug)] 17#[derive(Debug)]
17pub enum ImportScope { 18pub enum ImportScope {
@@ -119,7 +120,6 @@ pub(crate) fn insert_use(
119 } 120 }
120 121
121 if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { 122 if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize {
122 // FIXME: this alone doesnt properly re-align all cases
123 buf.push(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()); 123 buf.push(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into());
124 } 124 }
125 buf.push(use_item.syntax().clone().into()); 125 buf.push(use_item.syntax().clone().into());
@@ -149,66 +149,123 @@ fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -
149} 149}
150 150
151pub(crate) fn try_merge_imports( 151pub(crate) fn try_merge_imports(
152 old: &ast::Use, 152 lhs: &ast::Use,
153 new: &ast::Use, 153 rhs: &ast::Use,
154 merge_behaviour: MergeBehaviour, 154 merge_behaviour: MergeBehaviour,
155) -> Option<ast::Use> { 155) -> Option<ast::Use> {
156 // don't merge imports with different visibilities 156 // don't merge imports with different visibilities
157 if !eq_visibility(old.visibility(), new.visibility()) { 157 if !eq_visibility(lhs.visibility(), rhs.visibility()) {
158 return None; 158 return None;
159 } 159 }
160 let old_tree = old.use_tree()?; 160 let lhs_tree = lhs.use_tree()?;
161 let new_tree = new.use_tree()?; 161 let rhs_tree = rhs.use_tree()?;
162 let merged = try_merge_trees(&old_tree, &new_tree, merge_behaviour)?; 162 let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behaviour)?;
163 Some(old.with_use_tree(merged)) 163 Some(lhs.with_use_tree(merged))
164}
165
166/// Simple function that checks if a UseTreeList is deeper than one level
167fn use_tree_list_is_nested(tl: &ast::UseTreeList) -> bool {
168 tl.use_trees().any(|use_tree| {
169 use_tree.use_tree_list().is_some() || use_tree.path().and_then(|p| p.qualifier()).is_some()
170 })
171} 164}
172 165
173// FIXME: currently this merely prepends the new tree into old, ideally it would insert the items in a sorted fashion
174pub(crate) fn try_merge_trees( 166pub(crate) fn try_merge_trees(
175 old: &ast::UseTree, 167 lhs: &ast::UseTree,
176 new: &ast::UseTree, 168 rhs: &ast::UseTree,
177 merge_behaviour: MergeBehaviour, 169 merge: MergeBehaviour,
178) -> Option<ast::UseTree> { 170) -> Option<ast::UseTree> {
179 let lhs_path = old.path()?; 171 let lhs_path = lhs.path()?;
180 let rhs_path = new.path()?; 172 let rhs_path = rhs.path()?;
181 173
182 let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?; 174 let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?;
183 let lhs = old.split_prefix(&lhs_prefix); 175 let lhs = lhs.split_prefix(&lhs_prefix);
184 let rhs = new.split_prefix(&rhs_prefix); 176 let rhs = rhs.split_prefix(&rhs_prefix);
185 let lhs_tl = lhs.use_tree_list()?; 177 recursive_merge(&lhs, &rhs, merge).map(|(merged, _)| merged)
186 let rhs_tl = rhs.use_tree_list()?; 178}
187
188 // if we are only allowed to merge the last level check if the split off paths are only one level deep
189 if merge_behaviour == MergeBehaviour::Last
190 && (use_tree_list_is_nested(&lhs_tl) || use_tree_list_is_nested(&rhs_tl))
191 {
192 mark::hit!(test_last_merge_too_long);
193 return None;
194 }
195 179
196 let should_insert_comma = lhs_tl 180/// Recursively "zips" together lhs and rhs.
197 .r_curly_token() 181fn recursive_merge(
198 .and_then(|it| skip_trivia_token(it.prev_token()?, Direction::Prev)) 182 lhs: &ast::UseTree,
199 .map(|it| it.kind()) 183 rhs: &ast::UseTree,
200 != Some(T![,]); 184 merge: MergeBehaviour,
201 let mut to_insert: Vec<SyntaxElement> = Vec::new(); 185) -> Option<(ast::UseTree, bool)> {
202 if should_insert_comma { 186 let mut use_trees = lhs
203 to_insert.push(make::token(T![,]).into()); 187 .use_tree_list()
204 to_insert.push(make::tokens::single_space().into()); 188 .into_iter()
205 } 189 .flat_map(|list| list.use_trees())
206 to_insert.extend( 190 // check if any of the use trees are nested, if they are and the behaviour is `last` we are not allowed to merge this
207 rhs_tl.syntax().children_with_tokens().filter(|it| !matches!(it.kind(), T!['{'] | T!['}'])), 191 // so early exit the iterator by using Option's Intoiterator impl
208 ); 192 .map(|tree| match merge == MergeBehaviour::Last && tree.use_tree_list().is_some() {
209 let pos = InsertPosition::Before(lhs_tl.r_curly_token()?.into()); 193 true => None,
210 let use_tree_list = lhs_tl.insert_children(pos, to_insert); 194 false => Some(tree),
211 Some(lhs.with_use_tree_list(use_tree_list)) 195 })
196 .collect::<Option<Vec<_>>>()?;
197 use_trees.sort_unstable_by(|a, b| path_cmp_opt(a.path(), b.path()));
198 for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) {
199 let rhs_path = rhs_t.path();
200 match use_trees.binary_search_by(|p| path_cmp_opt(p.path(), rhs_path.clone())) {
201 Ok(idx) => {
202 let lhs_t = &mut use_trees[idx];
203 let lhs_path = lhs_t.path()?;
204 let rhs_path = rhs_path?;
205 let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?;
206 if lhs_prefix == lhs_path && rhs_prefix == rhs_path {
207 let tree_is_self = |tree: ast::UseTree| {
208 tree.path().as_ref().map(path_is_self).unwrap_or(false)
209 };
210 // check if only one of the two trees has a tree list, and whether that then contains `self` or not.
211 // If this is the case we can skip this iteration since the path without the list is already included in the other one via `self`
212 let tree_contains_self = |tree: &ast::UseTree| {
213 tree.use_tree_list()
214 .map(|tree_list| tree_list.use_trees().any(tree_is_self))
215 .unwrap_or(false)
216 };
217 match (tree_contains_self(&lhs_t), tree_contains_self(&rhs_t)) {
218 (true, false) => continue,
219 (false, true) => {
220 *lhs_t = rhs_t;
221 continue;
222 }
223 _ => (),
224 }
225
226 // glob imports arent part of the use-tree lists so we need to special handle them here as well
227 // this special handling is only required for when we merge a module import into a glob import of said module
228 // see the `merge_self_glob` or `merge_mod_into_glob` tests
229 if lhs_t.star_token().is_some() || rhs_t.star_token().is_some() {
230 *lhs_t = make::use_tree(
231 make::path_unqualified(make::path_segment_self()),
232 None,
233 None,
234 false,
235 );
236 use_trees.insert(idx, make::glob_use_tree());
237 continue;
238 }
239 }
240 let lhs = lhs_t.split_prefix(&lhs_prefix);
241 let rhs = rhs_t.split_prefix(&rhs_prefix);
242 let this_has_children = use_trees.len() > 0;
243 match recursive_merge(&lhs, &rhs, merge) {
244 Some((_, has_multiple_children))
245 if merge == MergeBehaviour::Last
246 && this_has_children
247 && has_multiple_children =>
248 {
249 return None
250 }
251 Some((use_tree, _)) => use_trees[idx] = use_tree,
252 None => use_trees.insert(idx, rhs_t),
253 }
254 }
255 Err(_)
256 if merge == MergeBehaviour::Last
257 && use_trees.len() > 0
258 && rhs_t.use_tree_list().is_some() =>
259 {
260 return None
261 }
262 Err(idx) => {
263 use_trees.insert(idx, rhs_t);
264 }
265 }
266 }
267 let has_multiple_children = use_trees.len() > 1;
268 Some((lhs.with_use_tree_list(make::use_tree_list(use_trees)), has_multiple_children))
212} 269}
213 270
214/// Traverses both paths until they differ, returning the common prefix of both. 271/// Traverses both paths until they differ, returning the common prefix of both.
@@ -219,7 +276,7 @@ fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast::Pa
219 loop { 276 loop {
220 match (lhs_curr.segment(), rhs_curr.segment()) { 277 match (lhs_curr.segment(), rhs_curr.segment()) {
221 (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (), 278 (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
222 _ => break, 279 _ => break res,
223 } 280 }
224 res = Some((lhs_curr.clone(), rhs_curr.clone())); 281 res = Some((lhs_curr.clone(), rhs_curr.clone()));
225 282
@@ -228,11 +285,62 @@ fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast::Pa
228 lhs_curr = lhs; 285 lhs_curr = lhs;
229 rhs_curr = rhs; 286 rhs_curr = rhs;
230 } 287 }
231 _ => break, 288 _ => break res,
289 }
290 }
291}
292
293fn path_is_self(path: &ast::Path) -> bool {
294 path.segment().and_then(|seg| seg.self_token()).is_some() && path.qualifier().is_none()
295}
296
297#[inline]
298fn first_segment(path: &ast::Path) -> Option<ast::PathSegment> {
299 first_path(path).segment()
300}
301
302fn first_path(path: &ast::Path) -> ast::Path {
303 successors(Some(path.clone()), ast::Path::qualifier).last().unwrap()
304}
305
306fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Clone {
307 // cant make use of SyntaxNode::siblings, because the returned Iterator is not clone
308 successors(first_segment(path), |p| p.parent_path().parent_path().and_then(|p| p.segment()))
309}
310
311/// Orders paths in the following way:
312/// the sole self token comes first, after that come uppercase identifiers, then lowercase identifiers
313// FIXME: rustfmt sort lowercase idents before uppercase, in general we want to have the same ordering rustfmt has
314// which is `self` and `super` first, then identifier imports with lowercase ones first, then glob imports and at last list imports.
315// Example foo::{self, foo, baz, Baz, Qux, *, {Bar}}
316fn path_cmp(a: &ast::Path, b: &ast::Path) -> Ordering {
317 match (path_is_self(a), path_is_self(b)) {
318 (true, true) => Ordering::Equal,
319 (true, false) => Ordering::Less,
320 (false, true) => Ordering::Greater,
321 (false, false) => {
322 let a = segment_iter(a);
323 let b = segment_iter(b);
324 // cmp_by would be useful for us here but that is currently unstable
325 // cmp doesnt work due the lifetimes on text's return type
326 a.zip(b)
327 .flat_map(|(seg, seg2)| seg.name_ref().zip(seg2.name_ref()))
328 .find_map(|(a, b)| match a.text().cmp(b.text()) {
329 ord @ Ordering::Greater | ord @ Ordering::Less => Some(ord),
330 Ordering::Equal => None,
331 })
332 .unwrap_or(Ordering::Equal)
232 } 333 }
233 } 334 }
335}
234 336
235 res 337fn path_cmp_opt(a: Option<ast::Path>, b: Option<ast::Path>) -> Ordering {
338 match (a, b) {
339 (None, None) => Ordering::Equal,
340 (None, Some(_)) => Ordering::Less,
341 (Some(_), None) => Ordering::Greater,
342 (Some(a), Some(b)) => path_cmp(&a, &b),
343 }
236} 344}
237 345
238/// What type of merges are allowed. 346/// What type of merges are allowed.
@@ -279,19 +387,6 @@ impl ImportGroup {
279 } 387 }
280} 388}
281 389
282fn first_segment(path: &ast::Path) -> Option<ast::PathSegment> {
283 first_path(path).segment()
284}
285
286fn first_path(path: &ast::Path) -> ast::Path {
287 successors(Some(path.clone()), ast::Path::qualifier).last().unwrap()
288}
289
290fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Clone {
291 // cant make use of SyntaxNode::siblings, because the returned Iterator is not clone
292 successors(first_segment(path), |p| p.parent_path().parent_path().and_then(|p| p.segment()))
293}
294
295#[derive(PartialEq, Eq)] 390#[derive(PartialEq, Eq)]
296enum AddBlankLine { 391enum AddBlankLine {
297 Before, 392 Before,
@@ -594,7 +689,7 @@ use std::io;",
594 check_full( 689 check_full(
595 "std::foo::bar::Baz", 690 "std::foo::bar::Baz",
596 r"use std::foo::bar::Qux;", 691 r"use std::foo::bar::Qux;",
597 r"use std::foo::bar::{Qux, Baz};", 692 r"use std::foo::bar::{Baz, Qux};",
598 ) 693 )
599 } 694 }
600 695
@@ -603,7 +698,7 @@ use std::io;",
603 check_last( 698 check_last(
604 "std::foo::bar::Baz", 699 "std::foo::bar::Baz",
605 r"use std::foo::bar::Qux;", 700 r"use std::foo::bar::Qux;",
606 r"use std::foo::bar::{Qux, Baz};", 701 r"use std::foo::bar::{Baz, Qux};",
607 ) 702 )
608 } 703 }
609 704
@@ -612,7 +707,7 @@ use std::io;",
612 check_full( 707 check_full(
613 "std::foo::bar::Baz", 708 "std::foo::bar::Baz",
614 r"use std::foo::bar::{Qux, Quux};", 709 r"use std::foo::bar::{Qux, Quux};",
615 r"use std::foo::bar::{Qux, Quux, Baz};", 710 r"use std::foo::bar::{Baz, Quux, Qux};",
616 ) 711 )
617 } 712 }
618 713
@@ -621,7 +716,7 @@ use std::io;",
621 check_last( 716 check_last(
622 "std::foo::bar::Baz", 717 "std::foo::bar::Baz",
623 r"use std::foo::bar::{Qux, Quux};", 718 r"use std::foo::bar::{Qux, Quux};",
624 r"use std::foo::bar::{Qux, Quux, Baz};", 719 r"use std::foo::bar::{Baz, Quux, Qux};",
625 ) 720 )
626 } 721 }
627 722
@@ -630,7 +725,7 @@ use std::io;",
630 check_full( 725 check_full(
631 "std::foo::bar::Baz", 726 "std::foo::bar::Baz",
632 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", 727 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
633 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}, Baz};", 728 r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};",
634 ) 729 )
635 } 730 }
636 731
@@ -645,6 +740,15 @@ use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
645 } 740 }
646 741
647 #[test] 742 #[test]
743 fn merge_groups_full_nested_deep() {
744 check_full(
745 "std::foo::bar::quux::Baz",
746 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
747 r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};",
748 )
749 }
750
751 #[test]
648 fn merge_groups_skip_pub() { 752 fn merge_groups_skip_pub() {
649 check_full( 753 check_full(
650 "std::io", 754 "std::io",
@@ -670,34 +774,53 @@ use std::io;",
670 check_last( 774 check_last(
671 "std::fmt::Result", 775 "std::fmt::Result",
672 r"use std::{fmt, io};", 776 r"use std::{fmt, io};",
673 r"use std::{self, fmt::Result}; 777 r"use std::fmt::{self, Result};
674use std::io;", 778use std::io;",
675 ) 779 )
676 } 780 }
677 781
678 #[test] 782 #[test]
783 fn merge_into_module_import() {
784 check_full(
785 "std::fmt::Result",
786 r"use std::{fmt, io};",
787 r"use std::{fmt::{self, Result}, io};",
788 )
789 }
790
791 #[test]
679 fn merge_groups_self() { 792 fn merge_groups_self() {
680 check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};") 793 check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};")
681 } 794 }
682 795
683 #[test] 796 #[test]
684 fn merge_self_glob() { 797 fn merge_mod_into_glob() {
685 check_full( 798 check_full(
686 "token::TokenKind", 799 "token::TokenKind",
687 r"use token::TokenKind::*;", 800 r"use token::TokenKind::*;",
688 r"use token::TokenKind::{self::*, self};", 801 r"use token::TokenKind::{*, self};",
802 )
803 // FIXME: have it emit `use token::TokenKind::{self, *}`?
804 }
805
806 #[test]
807 fn merge_self_glob() {
808 check_full("self", r"use self::*;", r"use self::{*, self};")
809 // FIXME: have it emit `use {self, *}`?
810 }
811
812 #[test]
813 fn merge_glob_nested() {
814 check_full(
815 "foo::bar::quux::Fez",
816 r"use foo::bar::{Baz, quux::*};",
817 r"use foo::bar::{Baz, quux::{self::*, Fez}};",
689 ) 818 )
690 } 819 }
691 820
692 #[test] 821 #[test]
693 fn merge_last_too_long() { 822 fn merge_last_too_long() {
694 mark::check!(test_last_merge_too_long); 823 check_last("foo::bar", r"use foo::bar::baz::Qux;", r"use foo::bar::{self, baz::Qux};");
695 check_last(
696 "foo::bar",
697 r"use foo::bar::baz::Qux;",
698 r"use foo::bar;
699use foo::bar::baz::Qux;",
700 );
701 } 824 }
702 825
703 #[test] 826 #[test]
@@ -710,6 +833,42 @@ use foo::bar::baz::Qux;",
710 ); 833 );
711 } 834 }
712 835
836 #[test]
837 fn merge_last_fail() {
838 check_merge_only_fail(
839 r"use foo::bar::{baz::{Qux, Fez}};",
840 r"use foo::bar::{baaz::{Quux, Feez}};",
841 MergeBehaviour::Last,
842 );
843 }
844
845 #[test]
846 fn merge_last_fail1() {
847 check_merge_only_fail(
848 r"use foo::bar::{baz::{Qux, Fez}};",
849 r"use foo::bar::baaz::{Quux, Feez};",
850 MergeBehaviour::Last,
851 );
852 }
853
854 #[test]
855 fn merge_last_fail2() {
856 check_merge_only_fail(
857 r"use foo::bar::baz::{Qux, Fez};",
858 r"use foo::bar::{baaz::{Quux, Feez}};",
859 MergeBehaviour::Last,
860 );
861 }
862
863 #[test]
864 fn merge_last_fail3() {
865 check_merge_only_fail(
866 r"use foo::bar::baz::{Qux, Fez};",
867 r"use foo::bar::baaz::{Quux, Feez};",
868 MergeBehaviour::Last,
869 );
870 }
871
713 fn check( 872 fn check(
714 path: &str, 873 path: &str,
715 ra_fixture_before: &str, 874 ra_fixture_before: &str,
@@ -742,4 +901,23 @@ use foo::bar::baz::Qux;",
742 fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 901 fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
743 check(path, ra_fixture_before, ra_fixture_after, None) 902 check(path, ra_fixture_before, ra_fixture_after, None)
744 } 903 }
904
905 fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) {
906 let use0 = ast::SourceFile::parse(ra_fixture0)
907 .tree()
908 .syntax()
909 .descendants()
910 .find_map(ast::Use::cast)
911 .unwrap();
912
913 let use1 = ast::SourceFile::parse(ra_fixture1)
914 .tree()
915 .syntax()
916 .descendants()
917 .find_map(ast::Use::cast)
918 .unwrap();
919
920 let result = try_merge_imports(&use0, &use1, mb);
921 assert_eq!(result.map(|u| u.to_string()), None);
922 }
745} 923}
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index 16078d104..d982c5f29 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -59,11 +59,12 @@ pub struct FlycheckHandle {
59 59
60impl FlycheckHandle { 60impl FlycheckHandle {
61 pub fn spawn( 61 pub fn spawn(
62 id: usize,
62 sender: Box<dyn Fn(Message) + Send>, 63 sender: Box<dyn Fn(Message) + Send>,
63 config: FlycheckConfig, 64 config: FlycheckConfig,
64 workspace_root: PathBuf, 65 workspace_root: PathBuf,
65 ) -> FlycheckHandle { 66 ) -> FlycheckHandle {
66 let actor = FlycheckActor::new(sender, config, workspace_root); 67 let actor = FlycheckActor::new(id, sender, config, workspace_root);
67 let (sender, receiver) = unbounded::<Restart>(); 68 let (sender, receiver) = unbounded::<Restart>();
68 let thread = jod_thread::spawn(move || actor.run(receiver)); 69 let thread = jod_thread::spawn(move || actor.run(receiver));
69 FlycheckHandle { sender, thread } 70 FlycheckHandle { sender, thread }
@@ -81,7 +82,11 @@ pub enum Message {
81 AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, 82 AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic },
82 83
83 /// Request check progress notification to client 84 /// Request check progress notification to client
84 Progress(Progress), 85 Progress {
86 /// Flycheck instance ID
87 id: usize,
88 progress: Progress,
89 },
85} 90}
86 91
87#[derive(Debug)] 92#[derive(Debug)]
@@ -95,6 +100,7 @@ pub enum Progress {
95struct Restart; 100struct Restart;
96 101
97struct FlycheckActor { 102struct FlycheckActor {
103 id: usize,
98 sender: Box<dyn Fn(Message) + Send>, 104 sender: Box<dyn Fn(Message) + Send>,
99 config: FlycheckConfig, 105 config: FlycheckConfig,
100 workspace_root: PathBuf, 106 workspace_root: PathBuf,
@@ -113,11 +119,15 @@ enum Event {
113 119
114impl FlycheckActor { 120impl FlycheckActor {
115 fn new( 121 fn new(
122 id: usize,
116 sender: Box<dyn Fn(Message) + Send>, 123 sender: Box<dyn Fn(Message) + Send>,
117 config: FlycheckConfig, 124 config: FlycheckConfig,
118 workspace_root: PathBuf, 125 workspace_root: PathBuf,
119 ) -> FlycheckActor { 126 ) -> FlycheckActor {
120 FlycheckActor { sender, config, workspace_root, cargo_handle: None } 127 FlycheckActor { id, sender, config, workspace_root, cargo_handle: None }
128 }
129 fn progress(&self, progress: Progress) {
130 self.send(Message::Progress { id: self.id, progress });
121 } 131 }
122 fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> { 132 fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
123 let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver); 133 let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
@@ -139,7 +149,7 @@ impl FlycheckActor {
139 command.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); 149 command.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
140 if let Ok(child) = command.spawn().map(JodChild) { 150 if let Ok(child) = command.spawn().map(JodChild) {
141 self.cargo_handle = Some(CargoHandle::spawn(child)); 151 self.cargo_handle = Some(CargoHandle::spawn(child));
142 self.send(Message::Progress(Progress::DidStart)); 152 self.progress(Progress::DidStart);
143 } 153 }
144 } 154 }
145 Event::CheckEvent(None) => { 155 Event::CheckEvent(None) => {
@@ -153,11 +163,11 @@ impl FlycheckActor {
153 self.check_command() 163 self.check_command()
154 ) 164 )
155 } 165 }
156 self.send(Message::Progress(Progress::DidFinish(res))); 166 self.progress(Progress::DidFinish(res));
157 } 167 }
158 Event::CheckEvent(Some(message)) => match message { 168 Event::CheckEvent(Some(message)) => match message {
159 cargo_metadata::Message::CompilerArtifact(msg) => { 169 cargo_metadata::Message::CompilerArtifact(msg) => {
160 self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name))); 170 self.progress(Progress::DidCheckCrate(msg.target.name));
161 } 171 }
162 172
163 cargo_metadata::Message::CompilerMessage(msg) => { 173 cargo_metadata::Message::CompilerMessage(msg) => {
@@ -179,7 +189,7 @@ impl FlycheckActor {
179 } 189 }
180 fn cancel_check_process(&mut self) { 190 fn cancel_check_process(&mut self) {
181 if self.cargo_handle.take().is_some() { 191 if self.cargo_handle.take().is_some() {
182 self.send(Message::Progress(Progress::DidCancel)); 192 self.progress(Progress::DidCancel);
183 } 193 }
184 } 194 }
185 fn check_command(&self) -> Command { 195 fn check_command(&self) -> Command {
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 7a9747fc7..567fd91af 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -383,6 +383,16 @@ impl Module {
383 pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> { 383 pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> {
384 hir_def::find_path::find_path(db, item.into(), self.into()) 384 hir_def::find_path::find_path(db, item.into(), self.into())
385 } 385 }
386
387 /// Finds a path that can be used to refer to the given item from within
388 /// this module, if possible. This is used for returning import paths for use-statements.
389 pub fn find_use_path_prefixed(
390 self,
391 db: &dyn DefDatabase,
392 item: impl Into<ItemInNs>,
393 ) -> Option<ModPath> {
394 hir_def::find_path::find_path_prefixed(db, item.into(), self.into())
395 }
386} 396}
387 397
388#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 398#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -709,11 +719,23 @@ impl Function {
709 } 719 }
710 720
711 pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> { 721 pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> {
722 let resolver = self.id.resolver(db.upcast());
723 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
724 let environment = TraitEnvironment::lower(db, &resolver);
712 db.function_data(self.id) 725 db.function_data(self.id)
713 .params 726 .params
714 .iter() 727 .iter()
715 .skip(if self.self_param(db).is_some() { 1 } else { 0 }) 728 .skip(if self.self_param(db).is_some() { 1 } else { 0 })
716 .map(|_| Param { _ty: () }) 729 .map(|type_ref| {
730 let ty = Type {
731 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate,
732 ty: InEnvironment {
733 value: Ty::from_hir_ext(&ctx, type_ref).0,
734 environment: environment.clone(),
735 },
736 };
737 Param { ty }
738 })
717 .collect() 739 .collect()
718 } 740 }
719 741
@@ -742,15 +764,21 @@ impl From<Mutability> for Access {
742 } 764 }
743} 765}
744 766
767pub struct Param {
768 ty: Type,
769}
770
771impl Param {
772 pub fn ty(&self) -> &Type {
773 &self.ty
774 }
775}
776
745#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 777#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
746pub struct SelfParam { 778pub struct SelfParam {
747 func: FunctionId, 779 func: FunctionId,
748} 780}
749 781
750pub struct Param {
751 _ty: (),
752}
753
754impl SelfParam { 782impl SelfParam {
755 pub fn access(self, db: &dyn HirDatabase) -> Access { 783 pub fn access(self, db: &dyn HirDatabase) -> Access {
756 let func_data = db.function_data(self.func); 784 let func_data = db.function_data(self.func);
@@ -908,12 +936,12 @@ impl MacroDef {
908 936
909 /// Indicate it is a proc-macro 937 /// Indicate it is a proc-macro
910 pub fn is_proc_macro(&self) -> bool { 938 pub fn is_proc_macro(&self) -> bool {
911 matches!(self.id.kind, MacroDefKind::CustomDerive(_)) 939 matches!(self.id.kind, MacroDefKind::ProcMacro(_))
912 } 940 }
913 941
914 /// Indicate it is a derive macro 942 /// Indicate it is a derive macro
915 pub fn is_derive_macro(&self) -> bool { 943 pub fn is_derive_macro(&self) -> bool {
916 matches!(self.id.kind, MacroDefKind::CustomDerive(_) | MacroDefKind::BuiltInDerive(_)) 944 matches!(self.id.kind, MacroDefKind::ProcMacro(_) | MacroDefKind::BuiltInDerive(_))
917 } 945 }
918} 946}
919 947
@@ -1276,6 +1304,14 @@ impl Type {
1276 ) 1304 )
1277 } 1305 }
1278 1306
1307 pub fn remove_ref(&self) -> Option<Type> {
1308 if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(_), .. }) = self.ty.value {
1309 self.ty.value.substs().map(|substs| self.derived(substs[0].clone()))
1310 } else {
1311 None
1312 }
1313 }
1314
1279 pub fn is_unknown(&self) -> bool { 1315 pub fn is_unknown(&self) -> bool {
1280 matches!(self.ty.value, Ty::Unknown) 1316 matches!(self.ty.value, Ty::Unknown)
1281 } 1317 }
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..001b3c5db 100644
--- a/crates/hir_def/src/diagnostics.rs
+++ b/crates/hir_def/src/diagnostics.rs
@@ -28,3 +28,52 @@ 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 fn is_experimental(&self) -> bool {
73 // This currently results in false positives in the following cases:
74 // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
75 // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly)
76 // - proc macros and/or proc macro generated code
77 true
78 }
79}
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index ac2c54ac5..baf374144 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -4,6 +4,7 @@ use hir_expand::name::{known, AsName, Name};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use test_utils::mark; 5use test_utils::mark;
6 6
7use crate::nameres::CrateDefMap;
7use crate::{ 8use crate::{
8 db::DefDatabase, 9 db::DefDatabase,
9 item_scope::ItemInNs, 10 item_scope::ItemInNs,
@@ -18,7 +19,12 @@ use crate::{
18/// *from where* you're referring to the item, hence the `from` parameter. 19/// *from where* you're referring to the item, hence the `from` parameter.
19pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 20pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
20 let _p = profile::span("find_path"); 21 let _p = profile::span("find_path");
21 find_path_inner(db, item, from, MAX_PATH_LEN) 22 find_path_inner(db, item, from, MAX_PATH_LEN, Prefixed::Not)
23}
24
25pub fn find_path_prefixed(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
26 let _p = profile::span("find_path_absolute");
27 find_path_inner(db, item, from, MAX_PATH_LEN, Prefixed::Plain)
22} 28}
23 29
24const MAX_PATH_LEN: usize = 15; 30const MAX_PATH_LEN: usize = 15;
@@ -36,11 +42,67 @@ impl ModPath {
36 } 42 }
37} 43}
38 44
45fn check_crate_self_super(
46 def_map: &CrateDefMap,
47 item: ItemInNs,
48 from: ModuleId,
49) -> Option<ModPath> {
50 // - if the item is the crate root, return `crate`
51 if item
52 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
53 krate: from.krate,
54 local_id: def_map.root,
55 }))
56 {
57 Some(ModPath::from_segments(PathKind::Crate, Vec::new()))
58 } else if item == ItemInNs::Types(from.into()) {
59 // - if the item is the module we're in, use `self`
60 Some(ModPath::from_segments(PathKind::Super(0), Vec::new()))
61 } else {
62 if let Some(parent_id) = def_map.modules[from.local_id].parent {
63 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
64 if item
65 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
66 krate: from.krate,
67 local_id: parent_id,
68 }))
69 {
70 return Some(ModPath::from_segments(PathKind::Super(1), Vec::new()));
71 }
72 }
73 None
74 }
75}
76
77#[derive(Copy, Clone, PartialEq, Eq)]
78pub enum Prefixed {
79 Not,
80 BySelf,
81 Plain,
82}
83
84impl Prefixed {
85 #[inline]
86 fn prefix(self) -> Option<PathKind> {
87 match self {
88 Prefixed::Not => None,
89 Prefixed::BySelf => Some(PathKind::Super(0)),
90 Prefixed::Plain => Some(PathKind::Plain),
91 }
92 }
93
94 #[inline]
95 fn prefixed(self) -> bool {
96 self != Prefixed::Not
97 }
98}
99
39fn find_path_inner( 100fn find_path_inner(
40 db: &dyn DefDatabase, 101 db: &dyn DefDatabase,
41 item: ItemInNs, 102 item: ItemInNs,
42 from: ModuleId, 103 from: ModuleId,
43 max_len: usize, 104 max_len: usize,
105 prefixed: Prefixed,
44) -> Option<ModPath> { 106) -> Option<ModPath> {
45 if max_len == 0 { 107 if max_len == 0 {
46 return None; 108 return None;
@@ -51,41 +113,22 @@ fn find_path_inner(
51 // - if the item is already in scope, return the name under which it is 113 // - if the item is already in scope, return the name under which it is
52 let def_map = db.crate_def_map(from.krate); 114 let def_map = db.crate_def_map(from.krate);
53 let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; 115 let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope;
54 if let Some((name, _)) = from_scope.name_of(item) { 116 let scope_name =
55 return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()])); 117 if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None };
118 if !prefixed.prefixed() && scope_name.is_some() {
119 return scope_name
120 .map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name]));
56 } 121 }
57 122
58 // - if the item is the crate root, return `crate` 123 if let modpath @ Some(_) = check_crate_self_super(&def_map, item, from) {
59 if item 124 return modpath;
60 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
61 krate: from.krate,
62 local_id: def_map.root,
63 }))
64 {
65 return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
66 }
67
68 // - if the item is the module we're in, use `self`
69 if item == ItemInNs::Types(from.into()) {
70 return Some(ModPath::from_segments(PathKind::Super(0), Vec::new()));
71 }
72
73 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
74 if let Some(parent_id) = def_map.modules[from.local_id].parent {
75 if item
76 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
77 krate: from.krate,
78 local_id: parent_id,
79 }))
80 {
81 return Some(ModPath::from_segments(PathKind::Super(1), Vec::new()));
82 }
83 } 125 }
84 126
85 // - if the item is the crate root of a dependency crate, return the name from the extern prelude 127 // - if the item is the crate root of a dependency crate, return the name from the extern prelude
86 for (name, def_id) in &def_map.extern_prelude { 128 for (name, def_id) in &def_map.extern_prelude {
87 if item == ItemInNs::Types(*def_id) { 129 if item == ItemInNs::Types(*def_id) {
88 return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()])); 130 let name = scope_name.unwrap_or_else(|| name.clone());
131 return Some(ModPath::from_segments(PathKind::Plain, vec![name]));
89 } 132 }
90 } 133 }
91 134
@@ -138,6 +181,7 @@ fn find_path_inner(
138 ItemInNs::Types(ModuleDefId::ModuleId(module_id)), 181 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
139 from, 182 from,
140 best_path_len - 1, 183 best_path_len - 1,
184 prefixed,
141 ) { 185 ) {
142 path.segments.push(name); 186 path.segments.push(name);
143 187
@@ -165,6 +209,7 @@ fn find_path_inner(
165 ItemInNs::Types(ModuleDefId::ModuleId(info.container)), 209 ItemInNs::Types(ModuleDefId::ModuleId(info.container)),
166 from, 210 from,
167 best_path_len - 1, 211 best_path_len - 1,
212 prefixed,
168 )?; 213 )?;
169 path.segments.push(info.path.segments.last().unwrap().clone()); 214 path.segments.push(info.path.segments.last().unwrap().clone());
170 Some(path) 215 Some(path)
@@ -181,7 +226,13 @@ fn find_path_inner(
181 } 226 }
182 } 227 }
183 228
184 best_path 229 if let Some(prefix) = prefixed.prefix() {
230 best_path.or_else(|| {
231 scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name]))
232 })
233 } else {
234 best_path
235 }
185} 236}
186 237
187fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { 238fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
@@ -304,7 +355,7 @@ mod tests {
304 /// `code` needs to contain a cursor marker; checks that `find_path` for the 355 /// `code` needs to contain a cursor marker; checks that `find_path` for the
305 /// item the `path` refers to returns that same path when called from the 356 /// item the `path` refers to returns that same path when called from the
306 /// module the cursor is in. 357 /// module the cursor is in.
307 fn check_found_path(ra_fixture: &str, path: &str) { 358 fn check_found_path_(ra_fixture: &str, path: &str, absolute: bool) {
308 let (db, pos) = TestDB::with_position(ra_fixture); 359 let (db, pos) = TestDB::with_position(ra_fixture);
309 let module = db.module_for_file(pos.file_id); 360 let module = db.module_for_file(pos.file_id);
310 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); 361 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path));
@@ -324,9 +375,20 @@ mod tests {
324 .take_types() 375 .take_types()
325 .unwrap(); 376 .unwrap();
326 377
327 let found_path = find_path(&db, ItemInNs::Types(resolved), module); 378 let found_path = if absolute { find_path_prefixed } else { find_path }(
379 &db,
380 ItemInNs::Types(resolved),
381 module,
382 );
383 assert_eq!(found_path, Some(mod_path), "absolute {}", absolute);
384 }
385
386 fn check_found_path(ra_fixture: &str, path: &str) {
387 check_found_path_(ra_fixture, path, false);
388 }
328 389
329 assert_eq!(found_path, Some(mod_path)); 390 fn check_found_path_abs(ra_fixture: &str, path: &str) {
391 check_found_path_(ra_fixture, path, true);
330 } 392 }
331 393
332 #[test] 394 #[test]
@@ -337,6 +399,7 @@ mod tests {
337 <|> 399 <|>
338 "#; 400 "#;
339 check_found_path(code, "S"); 401 check_found_path(code, "S");
402 check_found_path_abs(code, "S");
340 } 403 }
341 404
342 #[test] 405 #[test]
@@ -347,6 +410,7 @@ mod tests {
347 <|> 410 <|>
348 "#; 411 "#;
349 check_found_path(code, "E::A"); 412 check_found_path(code, "E::A");
413 check_found_path_abs(code, "E::A");
350 } 414 }
351 415
352 #[test] 416 #[test]
@@ -359,6 +423,7 @@ mod tests {
359 <|> 423 <|>
360 "#; 424 "#;
361 check_found_path(code, "foo::S"); 425 check_found_path(code, "foo::S");
426 check_found_path_abs(code, "foo::S");
362 } 427 }
363 428
364 #[test] 429 #[test]
@@ -373,6 +438,7 @@ mod tests {
373 <|> 438 <|>
374 "#; 439 "#;
375 check_found_path(code, "super::S"); 440 check_found_path(code, "super::S");
441 check_found_path_abs(code, "super::S");
376 } 442 }
377 443
378 #[test] 444 #[test]
@@ -384,6 +450,7 @@ mod tests {
384 <|> 450 <|>
385 "#; 451 "#;
386 check_found_path(code, "self"); 452 check_found_path(code, "self");
453 check_found_path_abs(code, "self");
387 } 454 }
388 455
389 #[test] 456 #[test]
@@ -395,6 +462,7 @@ mod tests {
395 <|> 462 <|>
396 "#; 463 "#;
397 check_found_path(code, "crate"); 464 check_found_path(code, "crate");
465 check_found_path_abs(code, "crate");
398 } 466 }
399 467
400 #[test] 468 #[test]
@@ -407,6 +475,7 @@ mod tests {
407 <|> 475 <|>
408 "#; 476 "#;
409 check_found_path(code, "crate::S"); 477 check_found_path(code, "crate::S");
478 check_found_path_abs(code, "crate::S");
410 } 479 }
411 480
412 #[test] 481 #[test]
@@ -418,6 +487,7 @@ mod tests {
418 pub struct S; 487 pub struct S;
419 "#; 488 "#;
420 check_found_path(code, "std::S"); 489 check_found_path(code, "std::S");
490 check_found_path_abs(code, "std::S");
421 } 491 }
422 492
423 #[test] 493 #[test]
@@ -430,14 +500,14 @@ mod tests {
430 pub struct S; 500 pub struct S;
431 "#; 501 "#;
432 check_found_path(code, "std_renamed::S"); 502 check_found_path(code, "std_renamed::S");
503 check_found_path_abs(code, "std_renamed::S");
433 } 504 }
434 505
435 #[test] 506 #[test]
436 fn partially_imported() { 507 fn partially_imported() {
437 // Tests that short paths are used even for external items, when parts of the path are 508 // Tests that short paths are used even for external items, when parts of the path are
438 // already in scope. 509 // already in scope.
439 check_found_path( 510 let code = r#"
440 r#"
441 //- /main.rs crate:main deps:syntax 511 //- /main.rs crate:main deps:syntax
442 512
443 use syntax::ast; 513 use syntax::ast;
@@ -449,12 +519,11 @@ mod tests {
449 A, B, C, 519 A, B, C,
450 } 520 }
451 } 521 }
452 "#, 522 "#;
453 "ast::ModuleItem", 523 check_found_path(code, "ast::ModuleItem");
454 ); 524 check_found_path_abs(code, "syntax::ast::ModuleItem");
455 525
456 check_found_path( 526 let code = r#"
457 r#"
458 //- /main.rs crate:main deps:syntax 527 //- /main.rs crate:main deps:syntax
459 528
460 <|> 529 <|>
@@ -465,9 +534,9 @@ mod tests {
465 A, B, C, 534 A, B, C,
466 } 535 }
467 } 536 }
468 "#, 537 "#;
469 "syntax::ast::ModuleItem", 538 check_found_path(code, "syntax::ast::ModuleItem");
470 ); 539 check_found_path_abs(code, "syntax::ast::ModuleItem");
471 } 540 }
472 541
473 #[test] 542 #[test]
@@ -481,6 +550,7 @@ mod tests {
481 <|> 550 <|>
482 "#; 551 "#;
483 check_found_path(code, "bar::S"); 552 check_found_path(code, "bar::S");
553 check_found_path_abs(code, "bar::S");
484 } 554 }
485 555
486 #[test] 556 #[test]
@@ -494,6 +564,7 @@ mod tests {
494 <|> 564 <|>
495 "#; 565 "#;
496 check_found_path(code, "bar::U"); 566 check_found_path(code, "bar::U");
567 check_found_path_abs(code, "bar::U");
497 } 568 }
498 569
499 #[test] 570 #[test]
@@ -507,6 +578,7 @@ mod tests {
507 pub struct S; 578 pub struct S;
508 "#; 579 "#;
509 check_found_path(code, "std::S"); 580 check_found_path(code, "std::S");
581 check_found_path_abs(code, "std::S");
510 } 582 }
511 583
512 #[test] 584 #[test]
@@ -520,6 +592,7 @@ mod tests {
520 pub use prelude::*; 592 pub use prelude::*;
521 "#; 593 "#;
522 check_found_path(code, "S"); 594 check_found_path(code, "S");
595 check_found_path_abs(code, "S");
523 } 596 }
524 597
525 #[test] 598 #[test]
@@ -537,6 +610,8 @@ mod tests {
537 "#; 610 "#;
538 check_found_path(code, "None"); 611 check_found_path(code, "None");
539 check_found_path(code, "Some"); 612 check_found_path(code, "Some");
613 check_found_path_abs(code, "None");
614 check_found_path_abs(code, "Some");
540 } 615 }
541 616
542 #[test] 617 #[test]
@@ -553,6 +628,7 @@ mod tests {
553 pub use crate::foo::bar::S; 628 pub use crate::foo::bar::S;
554 "#; 629 "#;
555 check_found_path(code, "baz::S"); 630 check_found_path(code, "baz::S");
631 check_found_path_abs(code, "baz::S");
556 } 632 }
557 633
558 #[test] 634 #[test]
@@ -567,6 +643,7 @@ mod tests {
567 "#; 643 "#;
568 // crate::S would be shorter, but using private imports seems wrong 644 // crate::S would be shorter, but using private imports seems wrong
569 check_found_path(code, "crate::bar::S"); 645 check_found_path(code, "crate::bar::S");
646 check_found_path_abs(code, "crate::bar::S");
570 } 647 }
571 648
572 #[test] 649 #[test]
@@ -585,6 +662,7 @@ mod tests {
585 pub use super::foo; 662 pub use super::foo;
586 "#; 663 "#;
587 check_found_path(code, "crate::foo::S"); 664 check_found_path(code, "crate::foo::S");
665 check_found_path_abs(code, "crate::foo::S");
588 } 666 }
589 667
590 #[test] 668 #[test]
@@ -605,6 +683,7 @@ mod tests {
605 } 683 }
606 "#; 684 "#;
607 check_found_path(code, "std::sync::Arc"); 685 check_found_path(code, "std::sync::Arc");
686 check_found_path_abs(code, "std::sync::Arc");
608 } 687 }
609 688
610 #[test] 689 #[test]
@@ -629,6 +708,7 @@ mod tests {
629 } 708 }
630 "#; 709 "#;
631 check_found_path(code, "core::fmt::Error"); 710 check_found_path(code, "core::fmt::Error");
711 check_found_path_abs(code, "core::fmt::Error");
632 } 712 }
633 713
634 #[test] 714 #[test]
@@ -652,6 +732,7 @@ mod tests {
652 } 732 }
653 "#; 733 "#;
654 check_found_path(code, "alloc::sync::Arc"); 734 check_found_path(code, "alloc::sync::Arc");
735 check_found_path_abs(code, "alloc::sync::Arc");
655 } 736 }
656 737
657 #[test] 738 #[test]
@@ -669,6 +750,7 @@ mod tests {
669 pub struct Arc; 750 pub struct Arc;
670 "#; 751 "#;
671 check_found_path(code, "megaalloc::Arc"); 752 check_found_path(code, "megaalloc::Arc");
753 check_found_path_abs(code, "megaalloc::Arc");
672 } 754 }
673 755
674 #[test] 756 #[test]
@@ -683,5 +765,7 @@ mod tests {
683 "#; 765 "#;
684 check_found_path(code, "u8"); 766 check_found_path(code, "u8");
685 check_found_path(code, "u16"); 767 check_found_path(code, "u16");
768 check_found_path_abs(code, "u8");
769 check_found_path_abs(code, "u16");
686 } 770 }
687} 771}
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index f1e9dfd5b..12c24e1ca 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -5,10 +5,12 @@ use std::collections::hash_map::Entry;
5 5
6use base_db::CrateId; 6use base_db::CrateId;
7use hir_expand::name::Name; 7use hir_expand::name::Name;
8use hir_expand::MacroDefKind;
8use once_cell::sync::Lazy; 9use once_cell::sync::Lazy;
9use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
10use test_utils::mark; 11use test_utils::mark;
11 12
13use crate::ModuleId;
12use crate::{ 14use crate::{
13 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, 15 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId,
14 LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, 16 LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId,
@@ -265,6 +267,26 @@ impl ItemScope {
265 pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { 267 pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> {
266 self.legacy_macros.clone() 268 self.legacy_macros.clone()
267 } 269 }
270
271 /// Marks everything that is not a procedural macro as private to `this_module`.
272 pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) {
273 self.types
274 .values_mut()
275 .chain(self.values.values_mut())
276 .map(|(_, v)| v)
277 .chain(self.unnamed_trait_imports.values_mut())
278 .for_each(|vis| *vis = Visibility::Module(this_module));
279
280 for (mac, vis) in self.macros.values_mut() {
281 if let MacroDefKind::ProcMacro(_) = mac.kind {
282 // FIXME: Technically this is insufficient since reexports of proc macros are also
283 // forbidden. Practically nobody does that.
284 continue;
285 }
286
287 *vis = Visibility::Module(this_module);
288 }
289 }
268} 290}
269 291
270impl PerNs { 292impl PerNs {
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..100e25ffc 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,
@@ -13,17 +16,16 @@ use hir_expand::{
13 proc_macro::ProcMacroExpander, 16 proc_macro::ProcMacroExpander,
14 HirFileId, MacroCallId, MacroDefId, MacroDefKind, 17 HirFileId, MacroCallId, MacroDefId, MacroDefKind,
15}; 18};
16use rustc_hash::FxHashMap; 19use rustc_hash::{FxHashMap, FxHashSet};
17use syntax::ast; 20use syntax::ast;
18use test_utils::mark; 21use test_utils::mark;
22use tt::{Leaf, TokenTree};
19 23
20use crate::{ 24use 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,
@@ -85,6 +87,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr
85 mod_dirs: FxHashMap::default(), 87 mod_dirs: FxHashMap::default(),
86 cfg_options, 88 cfg_options,
87 proc_macros, 89 proc_macros,
90 exports_proc_macros: false,
88 from_glob_import: Default::default(), 91 from_glob_import: Default::default(),
89 }; 92 };
90 collector.collect(); 93 collector.collect();
@@ -112,6 +115,12 @@ impl PartialResolvedImport {
112} 115}
113 116
114#[derive(Clone, Debug, Eq, PartialEq)] 117#[derive(Clone, Debug, Eq, PartialEq)]
118enum ImportSource {
119 Import(ItemTreeId<item_tree::Import>),
120 ExternCrate(ItemTreeId<item_tree::ExternCrate>),
121}
122
123#[derive(Clone, Debug, Eq, PartialEq)]
115struct Import { 124struct Import {
116 pub path: ModPath, 125 pub path: ModPath,
117 pub alias: Option<ImportAlias>, 126 pub alias: Option<ImportAlias>,
@@ -120,11 +129,12 @@ struct Import {
120 pub is_prelude: bool, 129 pub is_prelude: bool,
121 pub is_extern_crate: bool, 130 pub is_extern_crate: bool,
122 pub is_macro_use: bool, 131 pub is_macro_use: bool,
132 source: ImportSource,
123} 133}
124 134
125impl Import { 135impl Import {
126 fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self { 136 fn from_use(tree: &ItemTree, id: ItemTreeId<item_tree::Import>) -> Self {
127 let it = &tree[id]; 137 let it = &tree[id.value];
128 let visibility = &tree[it.visibility]; 138 let visibility = &tree[it.visibility];
129 Self { 139 Self {
130 path: it.path.clone(), 140 path: it.path.clone(),
@@ -134,20 +144,22 @@ impl Import {
134 is_prelude: it.is_prelude, 144 is_prelude: it.is_prelude,
135 is_extern_crate: false, 145 is_extern_crate: false,
136 is_macro_use: false, 146 is_macro_use: false,
147 source: ImportSource::Import(id),
137 } 148 }
138 } 149 }
139 150
140 fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self { 151 fn from_extern_crate(tree: &ItemTree, id: ItemTreeId<item_tree::ExternCrate>) -> Self {
141 let it = &tree[id]; 152 let it = &tree[id.value];
142 let visibility = &tree[it.visibility]; 153 let visibility = &tree[it.visibility];
143 Self { 154 Self {
144 path: it.path.clone(), 155 path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())),
145 alias: it.alias.clone(), 156 alias: it.alias.clone(),
146 visibility: visibility.clone(), 157 visibility: visibility.clone(),
147 is_glob: false, 158 is_glob: false,
148 is_prelude: false, 159 is_prelude: false,
149 is_extern_crate: true, 160 is_extern_crate: true,
150 is_macro_use: it.is_macro_use, 161 is_macro_use: it.is_macro_use,
162 source: ImportSource::ExternCrate(id),
151 } 163 }
152 } 164 }
153} 165}
@@ -191,7 +203,12 @@ struct DefCollector<'a> {
191 unexpanded_attribute_macros: Vec<DeriveDirective>, 203 unexpanded_attribute_macros: Vec<DeriveDirective>,
192 mod_dirs: FxHashMap<LocalModuleId, ModDir>, 204 mod_dirs: FxHashMap<LocalModuleId, ModDir>,
193 cfg_options: &'a CfgOptions, 205 cfg_options: &'a CfgOptions,
206 /// List of procedural macros defined by this crate. This is read from the dynamic library
207 /// built by the build system, and is the list of proc. macros we can actually expand. It is
208 /// empty when proc. macro support is disabled (in which case we still do name resolution for
209 /// them).
194 proc_macros: Vec<(Name, ProcMacroExpander)>, 210 proc_macros: Vec<(Name, ProcMacroExpander)>,
211 exports_proc_macros: bool,
195 from_glob_import: PerNsGlobImports, 212 from_glob_import: PerNsGlobImports,
196} 213}
197 214
@@ -245,28 +262,61 @@ impl DefCollector<'_> {
245 262
246 let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); 263 let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
247 // show unresolved imports in completion, etc 264 // show unresolved imports in completion, etc
248 for directive in unresolved_imports { 265 for directive in &unresolved_imports {
249 self.record_resolved_import(&directive) 266 self.record_resolved_import(directive)
267 }
268 self.unresolved_imports = unresolved_imports;
269
270 // FIXME: This condition should instead check if this is a `proc-macro` type crate.
271 if self.exports_proc_macros {
272 // A crate exporting procedural macros is not allowed to export anything else.
273 //
274 // Additionally, while the proc macro entry points must be `pub`, they are not publicly
275 // exported in type/value namespace. This function reduces the visibility of all items
276 // in the crate root that aren't proc macros.
277 let root = self.def_map.root;
278 let root = &mut self.def_map.modules[root];
279 root.scope.censor_non_proc_macros(ModuleId {
280 krate: self.def_map.krate,
281 local_id: self.def_map.root,
282 });
250 } 283 }
251
252 // Record proc-macros
253 self.collect_proc_macro();
254 } 284 }
255 285
256 fn collect_proc_macro(&mut self) { 286 /// Adds a definition of procedural macro `name` to the root module.
257 let proc_macros = std::mem::take(&mut self.proc_macros); 287 ///
258 for (name, expander) in proc_macros { 288 /// # Notes on procedural macro resolution
259 let krate = self.def_map.krate; 289 ///
260 290 /// Procedural macro functionality is provided by the build system: It has to build the proc
261 let macro_id = MacroDefId { 291 /// macro and pass the resulting dynamic library to rust-analyzer.
292 ///
293 /// When procedural macro support is enabled, the list of proc macros exported by a crate is
294 /// known before we resolve names in the crate. This list is stored in `self.proc_macros` and is
295 /// derived from the dynamic library.
296 ///
297 /// However, we *also* would like to be able to at least *resolve* macros on our own, without
298 /// help by the build system. So, when the macro isn't found in `self.proc_macros`, we instead
299 /// use a dummy expander that always errors. This comes with the drawback of macros potentially
300 /// going out of sync with what the build system sees (since we resolve using VFS state, but
301 /// Cargo builds only on-disk files). We could and probably should add diagnostics for that.
302 fn resolve_proc_macro(&mut self, name: &Name) {
303 self.exports_proc_macros = true;
304 let macro_def = match self.proc_macros.iter().find(|(n, _)| n == name) {
305 Some((_, expander)) => MacroDefId {
262 ast_id: None, 306 ast_id: None,
263 krate: Some(krate), 307 krate: Some(self.def_map.krate),
264 kind: MacroDefKind::CustomDerive(expander), 308 kind: MacroDefKind::ProcMacro(*expander),
265 local_inner: false, 309 local_inner: false,
266 }; 310 },
311 None => MacroDefId {
312 ast_id: None,
313 krate: Some(self.def_map.krate),
314 kind: MacroDefKind::ProcMacro(ProcMacroExpander::dummy(self.def_map.krate)),
315 local_inner: false,
316 },
317 };
267 318
268 self.define_proc_macro(name.clone(), macro_id); 319 self.define_proc_macro(name.clone(), macro_def);
269 }
270 } 320 }
271 321
272 /// Define a macro with `macro_rules`. 322 /// Define a macro with `macro_rules`.
@@ -346,20 +396,15 @@ impl DefCollector<'_> {
346 fn import_macros_from_extern_crate( 396 fn import_macros_from_extern_crate(
347 &mut self, 397 &mut self,
348 current_module_id: LocalModuleId, 398 current_module_id: LocalModuleId,
349 import: &item_tree::ExternCrate, 399 extern_crate: &item_tree::ExternCrate,
350 ) { 400 ) {
351 log::debug!( 401 log::debug!(
352 "importing macros from extern crate: {:?} ({:?})", 402 "importing macros from extern crate: {:?} ({:?})",
353 import, 403 extern_crate,
354 self.def_map.edition, 404 self.def_map.edition,
355 ); 405 );
356 406
357 let res = self.def_map.resolve_name_in_extern_prelude( 407 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 408
364 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { 409 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
365 mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); 410 mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
@@ -420,7 +465,11 @@ impl DefCollector<'_> {
420 .as_ident() 465 .as_ident()
421 .expect("extern crate should have been desugared to one-element path"), 466 .expect("extern crate should have been desugared to one-element path"),
422 ); 467 );
423 PartialResolvedImport::Resolved(res) 468 if res.is_none() {
469 PartialResolvedImport::Unresolved
470 } else {
471 PartialResolvedImport::Resolved(res)
472 }
424 } else { 473 } else {
425 let res = self.def_map.resolve_path_fp_with_macro( 474 let res = self.def_map.resolve_path_fp_with_macro(
426 self.db, 475 self.db,
@@ -774,7 +823,51 @@ impl DefCollector<'_> {
774 .collect(item_tree.top_level_items()); 823 .collect(item_tree.top_level_items());
775 } 824 }
776 825
777 fn finish(self) -> CrateDefMap { 826 fn finish(mut self) -> CrateDefMap {
827 // Emit diagnostics for all remaining unresolved imports.
828
829 // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't
830 // resolve. We first emit diagnostics for unresolved extern crates and collect the missing
831 // crate names. Then we emit diagnostics for unresolved imports, but only if the import
832 // doesn't start with an unresolved crate's name. Due to renaming and reexports, this is a
833 // heuristic, but it works in practice.
834 let mut diagnosed_extern_crates = FxHashSet::default();
835 for directive in &self.unresolved_imports {
836 if let ImportSource::ExternCrate(krate) = directive.import.source {
837 let item_tree = self.db.item_tree(krate.file_id);
838 let extern_crate = &item_tree[krate.value];
839
840 diagnosed_extern_crates.insert(extern_crate.name.clone());
841
842 self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate(
843 directive.module_id,
844 InFile::new(krate.file_id, extern_crate.ast_id),
845 ));
846 }
847 }
848
849 for directive in &self.unresolved_imports {
850 if let ImportSource::Import(import) = &directive.import.source {
851 let item_tree = self.db.item_tree(import.file_id);
852 let import_data = &item_tree[import.value];
853
854 match (import_data.path.segments.first(), &import_data.path.kind) {
855 (Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => {
856 if diagnosed_extern_crates.contains(krate) {
857 continue;
858 }
859 }
860 _ => {}
861 }
862
863 self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
864 directive.module_id,
865 InFile::new(import.file_id, import_data.ast_id),
866 import_data.index,
867 ));
868 }
869 }
870
778 self.def_map 871 self.def_map
779 } 872 }
780} 873}
@@ -819,178 +912,186 @@ impl ModCollector<'_, '_> {
819 912
820 for &item in items { 913 for &item in items {
821 let attrs = self.item_tree.attrs(item.into()); 914 let attrs = self.item_tree.attrs(item.into());
822 if self.is_cfg_enabled(attrs) { 915 if !self.is_cfg_enabled(attrs) {
823 let module = 916 continue;
824 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; 917 }
825 let container = ContainerId::ModuleId(module); 918 let module =
826 919 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
827 let mut def = None; 920 let container = ContainerId::ModuleId(module);
828 match item { 921
829 ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs), 922 let mut def = None;
830 ModItem::Import(import_id) => { 923 match item {
831 self.def_collector.unresolved_imports.push(ImportDirective { 924 ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs),
832 module_id: self.module_id, 925 ModItem::Import(import_id) => {
833 import: Import::from_use(&self.item_tree, import_id), 926 self.def_collector.unresolved_imports.push(ImportDirective {
834 status: PartialResolvedImport::Unresolved, 927 module_id: self.module_id,
835 }) 928 import: Import::from_use(
836 } 929 &self.item_tree,
837 ModItem::ExternCrate(import_id) => { 930 InFile::new(self.file_id, import_id),
838 self.def_collector.unresolved_imports.push(ImportDirective { 931 ),
839 module_id: self.module_id, 932 status: PartialResolvedImport::Unresolved,
840 import: Import::from_extern_crate(&self.item_tree, import_id), 933 })
841 status: PartialResolvedImport::Unresolved, 934 }
842 }) 935 ModItem::ExternCrate(import_id) => {
843 } 936 self.def_collector.unresolved_imports.push(ImportDirective {
844 ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]), 937 module_id: self.module_id,
845 ModItem::Impl(imp) => { 938 import: Import::from_extern_crate(
846 let module = ModuleId { 939 &self.item_tree,
847 krate: self.def_collector.def_map.krate, 940 InFile::new(self.file_id, import_id),
848 local_id: self.module_id, 941 ),
849 }; 942 status: PartialResolvedImport::Unresolved,
850 let container = ContainerId::ModuleId(module); 943 })
851 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } 944 }
852 .intern(self.def_collector.db); 945 ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]),
853 self.def_collector.def_map.modules[self.module_id] 946 ModItem::Impl(imp) => {
854 .scope 947 let module = ModuleId {
855 .define_impl(impl_id) 948 krate: self.def_collector.def_map.krate,
856 } 949 local_id: self.module_id,
857 ModItem::Function(id) => { 950 };
858 let func = &self.item_tree[id]; 951 let container = ContainerId::ModuleId(module);
859 def = Some(DefData { 952 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) }
860 id: FunctionLoc { 953 .intern(self.def_collector.db);
861 container: container.into(), 954 self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id)
862 id: ItemTreeId::new(self.file_id, id), 955 }
863 } 956 ModItem::Function(id) => {
864 .intern(self.def_collector.db) 957 let func = &self.item_tree[id];
865 .into(),
866 name: &func.name,
867 visibility: &self.item_tree[func.visibility],
868 has_constructor: false,
869 });
870 }
871 ModItem::Struct(id) => {
872 let it = &self.item_tree[id];
873 958
874 // FIXME: check attrs to see if this is an attribute macro invocation; 959 self.collect_proc_macro_def(&func.name, attrs);
875 // in which case we don't add the invocation, just a single attribute
876 // macro invocation
877 self.collect_derives(attrs, it.ast_id.upcast());
878 960
879 def = Some(DefData { 961 def = Some(DefData {
880 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) } 962 id: FunctionLoc {
881 .intern(self.def_collector.db) 963 container: container.into(),
882 .into(), 964 id: ItemTreeId::new(self.file_id, id),
883 name: &it.name, 965 }
884 visibility: &self.item_tree[it.visibility], 966 .intern(self.def_collector.db)
885 has_constructor: it.kind != StructDefKind::Record, 967 .into(),
886 }); 968 name: &func.name,
887 } 969 visibility: &self.item_tree[func.visibility],
888 ModItem::Union(id) => { 970 has_constructor: false,
889 let it = &self.item_tree[id]; 971 });
972 }
973 ModItem::Struct(id) => {
974 let it = &self.item_tree[id];
890 975
891 // FIXME: check attrs to see if this is an attribute macro invocation; 976 // 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 977 // in which case we don't add the invocation, just a single attribute
893 // macro invocation 978 // macro invocation
894 self.collect_derives(attrs, it.ast_id.upcast()); 979 self.collect_derives(attrs, it.ast_id.upcast());
895 980
896 def = Some(DefData { 981 def = Some(DefData {
897 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } 982 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
898 .intern(self.def_collector.db) 983 .intern(self.def_collector.db)
899 .into(), 984 .into(),
900 name: &it.name, 985 name: &it.name,
901 visibility: &self.item_tree[it.visibility], 986 visibility: &self.item_tree[it.visibility],
902 has_constructor: false, 987 has_constructor: it.kind != StructDefKind::Record,
903 }); 988 });
904 } 989 }
905 ModItem::Enum(id) => { 990 ModItem::Union(id) => {
906 let it = &self.item_tree[id]; 991 let it = &self.item_tree[id];
907 992
908 // FIXME: check attrs to see if this is an attribute macro invocation; 993 // 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 994 // in which case we don't add the invocation, just a single attribute
910 // macro invocation 995 // macro invocation
911 self.collect_derives(attrs, it.ast_id.upcast()); 996 self.collect_derives(attrs, it.ast_id.upcast());
912 997
913 def = Some(DefData { 998 def = Some(DefData {
914 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } 999 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) }
915 .intern(self.def_collector.db) 1000 .intern(self.def_collector.db)
916 .into(), 1001 .into(),
917 name: &it.name, 1002 name: &it.name,
918 visibility: &self.item_tree[it.visibility], 1003 visibility: &self.item_tree[it.visibility],
919 has_constructor: false, 1004 has_constructor: false,
920 }); 1005 });
921 } 1006 }
922 ModItem::Const(id) => { 1007 ModItem::Enum(id) => {
923 let it = &self.item_tree[id]; 1008 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 1009
942 def = Some(DefData { 1010 // FIXME: check attrs to see if this is an attribute macro invocation;
943 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } 1011 // in which case we don't add the invocation, just a single attribute
944 .intern(self.def_collector.db) 1012 // macro invocation
945 .into(), 1013 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 1014
954 def = Some(DefData { 1015 def = Some(DefData {
955 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } 1016 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) }
956 .intern(self.def_collector.db) 1017 .intern(self.def_collector.db)
957 .into(), 1018 .into(),
958 name: &it.name, 1019 name: &it.name,
959 visibility: &self.item_tree[it.visibility], 1020 visibility: &self.item_tree[it.visibility],
960 has_constructor: false, 1021 has_constructor: false,
961 }); 1022 });
962 } 1023 }
963 ModItem::TypeAlias(id) => { 1024 ModItem::Const(id) => {
964 let it = &self.item_tree[id]; 1025 let it = &self.item_tree[id];
965 1026
1027 if let Some(name) = &it.name {
966 def = Some(DefData { 1028 def = Some(DefData {
967 id: TypeAliasLoc { 1029 id: ConstLoc {
968 container: container.into(), 1030 container: container.into(),
969 id: ItemTreeId::new(self.file_id, id), 1031 id: ItemTreeId::new(self.file_id, id),
970 } 1032 }
971 .intern(self.def_collector.db) 1033 .intern(self.def_collector.db)
972 .into(), 1034 .into(),
973 name: &it.name, 1035 name,
974 visibility: &self.item_tree[it.visibility], 1036 visibility: &self.item_tree[it.visibility],
975 has_constructor: false, 1037 has_constructor: false,
976 }); 1038 });
977 } 1039 }
978 } 1040 }
1041 ModItem::Static(id) => {
1042 let it = &self.item_tree[id];
979 1043
980 if let Some(DefData { id, name, visibility, has_constructor }) = def { 1044 def = Some(DefData {
981 self.def_collector.def_map.modules[self.module_id].scope.define_def(id); 1045 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) }
982 let vis = self 1046 .intern(self.def_collector.db)
983 .def_collector 1047 .into(),
984 .def_map 1048 name: &it.name,
985 .resolve_visibility(self.def_collector.db, self.module_id, visibility) 1049 visibility: &self.item_tree[it.visibility],
986 .unwrap_or(Visibility::Public); 1050 has_constructor: false,
987 self.def_collector.update( 1051 });
988 self.module_id, 1052 }
989 &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))], 1053 ModItem::Trait(id) => {
990 vis, 1054 let it = &self.item_tree[id];
991 ImportType::Named, 1055
992 ) 1056 def = Some(DefData {
1057 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) }
1058 .intern(self.def_collector.db)
1059 .into(),
1060 name: &it.name,
1061 visibility: &self.item_tree[it.visibility],
1062 has_constructor: false,
1063 });
993 } 1064 }
1065 ModItem::TypeAlias(id) => {
1066 let it = &self.item_tree[id];
1067
1068 def = Some(DefData {
1069 id: TypeAliasLoc {
1070 container: container.into(),
1071 id: ItemTreeId::new(self.file_id, id),
1072 }
1073 .intern(self.def_collector.db)
1074 .into(),
1075 name: &it.name,
1076 visibility: &self.item_tree[it.visibility],
1077 has_constructor: false,
1078 });
1079 }
1080 }
1081
1082 if let Some(DefData { id, name, visibility, has_constructor }) = def {
1083 self.def_collector.def_map.modules[self.module_id].scope.define_def(id);
1084 let vis = self
1085 .def_collector
1086 .def_map
1087 .resolve_visibility(self.def_collector.db, self.module_id, visibility)
1088 .unwrap_or(Visibility::Public);
1089 self.def_collector.update(
1090 self.module_id,
1091 &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))],
1092 vis,
1093 ImportType::Named,
1094 )
994 } 1095 }
995 } 1096 }
996 } 1097 }
@@ -1051,13 +1152,11 @@ impl ModCollector<'_, '_> {
1051 self.import_all_legacy_macros(module_id); 1152 self.import_all_legacy_macros(module_id);
1052 } 1153 }
1053 } 1154 }
1054 Err(candidate) => self.def_collector.def_map.diagnostics.push( 1155 Err(candidate) => {
1055 DefDiagnostic::UnresolvedModule { 1156 self.def_collector.def_map.diagnostics.push(
1056 module: self.module_id, 1157 DefDiagnostic::unresolved_module(self.module_id, ast_id, candidate),
1057 declaration: ast_id, 1158 );
1058 candidate, 1159 }
1059 },
1060 ),
1061 }; 1160 };
1062 } 1161 }
1063 } 1162 }
@@ -1119,6 +1218,30 @@ impl ModCollector<'_, '_> {
1119 } 1218 }
1120 } 1219 }
1121 1220
1221 /// If `attrs` registers a procedural macro, collects its definition.
1222 fn collect_proc_macro_def(&mut self, func_name: &Name, attrs: &Attrs) {
1223 // FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere
1224 // FIXME: distinguish the type of macro
1225 let macro_name = if attrs.by_key("proc_macro").exists()
1226 || attrs.by_key("proc_macro_attribute").exists()
1227 {
1228 func_name.clone()
1229 } else {
1230 let derive = attrs.by_key("proc_macro_derive");
1231 if let Some(arg) = derive.tt_values().next() {
1232 if let [TokenTree::Leaf(Leaf::Ident(trait_name))] = &*arg.token_trees {
1233 trait_name.as_name()
1234 } else {
1235 return;
1236 }
1237 } else {
1238 return;
1239 }
1240 };
1241
1242 self.def_collector.resolve_proc_macro(&macro_name);
1243 }
1244
1122 fn collect_macro(&mut self, mac: &MacroCall) { 1245 fn collect_macro(&mut self, mac: &MacroCall) {
1123 let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); 1246 let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
1124 1247
@@ -1225,6 +1348,7 @@ mod tests {
1225 mod_dirs: FxHashMap::default(), 1348 mod_dirs: FxHashMap::default(),
1226 cfg_options: &CfgOptions::default(), 1349 cfg_options: &CfgOptions::default(),
1227 proc_macros: Default::default(), 1350 proc_macros: Default::default(),
1351 exports_proc_macros: false,
1228 from_glob_import: Default::default(), 1352 from_glob_import: Default::default(),
1229 }; 1353 };
1230 collector.collect(); 1354 collector.collect();
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/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index e0fb8bdef..0851c3b7d 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -667,3 +667,76 @@ b! { static = #[] (); }
667 "#]], 667 "#]],
668 ); 668 );
669} 669}
670
671#[test]
672fn resolves_proc_macros() {
673 check(
674 r"
675 struct TokenStream;
676
677 #[proc_macro]
678 pub fn function_like_macro(args: TokenStream) -> TokenStream {
679 args
680 }
681
682 #[proc_macro_attribute]
683 pub fn attribute_macro(_args: TokenStream, item: TokenStream) -> TokenStream {
684 item
685 }
686
687 #[proc_macro_derive(DummyTrait)]
688 pub fn derive_macro(_item: TokenStream) -> TokenStream {
689 TokenStream
690 }
691 ",
692 expect![[r#"
693 crate
694 DummyTrait: m
695 TokenStream: t v
696 attribute_macro: v m
697 derive_macro: v
698 function_like_macro: v m
699 "#]],
700 );
701}
702
703#[test]
704fn proc_macro_censoring() {
705 // Make sure that only proc macros are publicly exported from proc-macro crates.
706
707 check(
708 r"
709 //- /main.rs crate:main deps:macros
710 pub use macros::*;
711
712 //- /macros.rs crate:macros
713 pub struct TokenStream;
714
715 #[proc_macro]
716 pub fn function_like_macro(args: TokenStream) -> TokenStream {
717 args
718 }
719
720 #[proc_macro_attribute]
721 pub fn attribute_macro(_args: TokenStream, item: TokenStream) -> TokenStream {
722 item
723 }
724
725 #[proc_macro_derive(DummyTrait)]
726 pub fn derive_macro(_item: TokenStream) -> TokenStream {
727 TokenStream
728 }
729
730 #[macro_export]
731 macro_rules! mbe {
732 () => {};
733 }
734 ",
735 expect![[r#"
736 crate
737 DummyTrait: m
738 attribute_macro: m
739 function_like_macro: m
740 "#]],
741 );
742}
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 40ec38f56..209b18e78 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/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 49841c7a1..a5750d829 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -43,8 +43,8 @@ impl Name {
43 } 43 }
44 44
45 /// Shortcut to create inline plain text name 45 /// Shortcut to create inline plain text name
46 const fn new_inline_ascii(text: &[u8]) -> Name { 46 const fn new_inline(text: &str) -> Name {
47 Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text)) 47 Name::new_text(SmolStr::new_inline(text))
48 } 48 }
49 49
50 /// Resolve a name from the text of token. 50 /// Resolve a name from the text of token.
@@ -127,7 +127,7 @@ pub mod known {
127 $( 127 $(
128 #[allow(bad_style)] 128 #[allow(bad_style)]
129 pub const $ident: super::Name = 129 pub const $ident: super::Name =
130 super::Name::new_inline_ascii(stringify!($ident).as_bytes()); 130 super::Name::new_inline(stringify!($ident));
131 )* 131 )*
132 }; 132 };
133 } 133 }
@@ -210,8 +210,8 @@ pub mod known {
210 ); 210 );
211 211
212 // self/Self cannot be used as an identifier 212 // self/Self cannot be used as an identifier
213 pub const SELF_PARAM: super::Name = super::Name::new_inline_ascii(b"self"); 213 pub const SELF_PARAM: super::Name = super::Name::new_inline("self");
214 pub const SELF_TYPE: super::Name = super::Name::new_inline_ascii(b"Self"); 214 pub const SELF_TYPE: super::Name = super::Name::new_inline("Self");
215 215
216 #[macro_export] 216 #[macro_export]
217 macro_rules! name { 217 macro_rules! name {
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index 80255ea32..7505cb061 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -7,7 +7,7 @@ use tt::buffer::{Cursor, TokenBuffer};
7#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] 7#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
8pub struct ProcMacroExpander { 8pub struct ProcMacroExpander {
9 krate: CrateId, 9 krate: CrateId,
10 proc_macro_id: ProcMacroId, 10 proc_macro_id: Option<ProcMacroId>,
11} 11}
12 12
13macro_rules! err { 13macro_rules! err {
@@ -20,8 +20,14 @@ macro_rules! err {
20} 20}
21 21
22impl ProcMacroExpander { 22impl ProcMacroExpander {
23 pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander { 23 pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> Self {
24 ProcMacroExpander { krate, proc_macro_id } 24 Self { krate, proc_macro_id: Some(proc_macro_id) }
25 }
26
27 pub fn dummy(krate: CrateId) -> Self {
28 // FIXME: Should store the name for better errors
29 // FIXME: I think this is the second layer of "dummy" expansion, we should reduce that
30 Self { krate, proc_macro_id: None }
25 } 31 }
26 32
27 pub fn expand( 33 pub fn expand(
@@ -30,17 +36,22 @@ impl ProcMacroExpander {
30 _id: LazyMacroId, 36 _id: LazyMacroId,
31 tt: &tt::Subtree, 37 tt: &tt::Subtree,
32 ) -> Result<tt::Subtree, mbe::ExpandError> { 38 ) -> Result<tt::Subtree, mbe::ExpandError> {
33 let krate_graph = db.crate_graph(); 39 match self.proc_macro_id {
34 let proc_macro = krate_graph[self.krate] 40 Some(id) => {
35 .proc_macro 41 let krate_graph = db.crate_graph();
36 .get(self.proc_macro_id.0 as usize) 42 let proc_macro = krate_graph[self.krate]
37 .clone() 43 .proc_macro
38 .ok_or_else(|| err!("No derive macro found."))?; 44 .get(id.0 as usize)
39 45 .clone()
40 let tt = remove_derive_attrs(tt) 46 .ok_or_else(|| err!("No derive macro found."))?;
41 .ok_or_else(|| err!("Fail to remove derive for custom derive"))?; 47
42 48 let tt = remove_derive_attrs(tt)
43 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) 49 .ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
50
51 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
52 }
53 None => Err(err!("Unresolved proc macro")),
54 }
44 } 55 }
45} 56}
46 57
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index bc86df2b1..ed1c911c2 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -17,9 +17,9 @@ ena = "0.14.0"
17log = "0.4.8" 17log = "0.4.8"
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19scoped-tls = "1" 19scoped-tls = "1"
20chalk-solve = { version = "0.27.0" } 20chalk-solve = { version = "0.30.0" }
21chalk-ir = { version = "0.27.0" } 21chalk-ir = { version = "0.30.0" }
22chalk-recursive = { version = "0.27.0" } 22chalk-recursive = { version = "0.30.0" }
23 23
24stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
25hir_def = { path = "../hir_def", version = "0.0.0" } 25hir_def = { path = "../hir_def", version = "0.0.0" }
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 2b53b8297..9a7785c76 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -555,7 +555,7 @@ impl<'a> InferenceContext<'a> {
555 555
556 fn resolve_lang_item(&self, name: &str) -> Option<LangItemTarget> { 556 fn resolve_lang_item(&self, name: &str) -> Option<LangItemTarget> {
557 let krate = self.resolver.krate()?; 557 let krate = self.resolver.krate()?;
558 let name = SmolStr::new_inline_from_ascii(name.len(), name.as_bytes()); 558 let name = SmolStr::new_inline(name);
559 self.db.lang_item(krate, name) 559 self.db.lang_item(krate, name)
560 } 560 }
561 561
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index 27f0ed628..009b17a7f 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -129,8 +129,12 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
129 debug!("impls_for_trait returned {} impls", result.len()); 129 debug!("impls_for_trait returned {} impls", result.len());
130 result 130 result
131 } 131 }
132 fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: AdtId) -> bool { 132 fn impl_provided_for(
133 debug!("impl_provided_for {:?}, {:?}", auto_trait_id, struct_id); 133 &self,
134 auto_trait_id: TraitId,
135 application_ty: &chalk_ir::ApplicationTy<Interner>,
136 ) -> bool {
137 debug!("impl_provided_for {:?}, {:?}", auto_trait_id, application_ty);
134 false // FIXME 138 false // FIXME
135 } 139 }
136 fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> { 140 fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> {
@@ -422,6 +426,7 @@ fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> {
422 "fn_mut" => WellKnownTrait::FnMut, 426 "fn_mut" => WellKnownTrait::FnMut,
423 "fn" => WellKnownTrait::Fn, 427 "fn" => WellKnownTrait::Fn,
424 "unsize" => WellKnownTrait::Unsize, 428 "unsize" => WellKnownTrait::Unsize,
429 "coerce_unsized" => WellKnownTrait::CoerceUnsized,
425 _ => return None, 430 _ => return None,
426 }) 431 })
427} 432}
@@ -437,6 +442,7 @@ fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str {
437 WellKnownTrait::Fn => "fn", 442 WellKnownTrait::Fn => "fn",
438 WellKnownTrait::Unsize => "unsize", 443 WellKnownTrait::Unsize => "unsize",
439 WellKnownTrait::Unpin => "unpin", 444 WellKnownTrait::Unpin => "unpin",
445 WellKnownTrait::CoerceUnsized => "coerce_unsized",
440 } 446 }
441} 447}
442 448
diff --git a/crates/ide/src/completion/complete_postfix.rs b/crates/ide/src/completion/complete_postfix.rs
index 84c4e129d..26a5af5b9 100644
--- a/crates/ide/src/completion/complete_postfix.rs
+++ b/crates/ide/src/completion/complete_postfix.rs
@@ -175,6 +175,9 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
175 ) 175 )
176 .add_to(acc); 176 .add_to(acc);
177 177
178 postfix_snippet(ctx, cap, &dot_receiver, "ok", "Ok(expr)", &format!("Ok({})", receiver_text))
179 .add_to(acc);
180
178 postfix_snippet( 181 postfix_snippet(
179 ctx, 182 ctx,
180 cap, 183 cap,
@@ -189,6 +192,16 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
189 ctx, 192 ctx,
190 cap, 193 cap,
191 &dot_receiver, 194 &dot_receiver,
195 "dbgr",
196 "dbg!(&expr)",
197 &format!("dbg!(&{})", receiver_text),
198 )
199 .add_to(acc);
200
201 postfix_snippet(
202 ctx,
203 cap,
204 &dot_receiver,
192 "call", 205 "call",
193 "function(expr)", 206 "function(expr)",
194 &format!("${{1}}({})", receiver_text), 207 &format!("${{1}}({})", receiver_text),
@@ -263,9 +276,11 @@ fn main() {
263 sn box Box::new(expr) 276 sn box Box::new(expr)
264 sn call function(expr) 277 sn call function(expr)
265 sn dbg dbg!(expr) 278 sn dbg dbg!(expr)
279 sn dbgr dbg!(&expr)
266 sn if if expr {} 280 sn if if expr {}
267 sn match match expr {} 281 sn match match expr {}
268 sn not !expr 282 sn not !expr
283 sn ok Ok(expr)
269 sn ref &expr 284 sn ref &expr
270 sn refm &mut expr 285 sn refm &mut expr
271 sn while while expr {} 286 sn while while expr {}
@@ -286,7 +301,9 @@ fn main() {
286 sn box Box::new(expr) 301 sn box Box::new(expr)
287 sn call function(expr) 302 sn call function(expr)
288 sn dbg dbg!(expr) 303 sn dbg dbg!(expr)
304 sn dbgr dbg!(&expr)
289 sn match match expr {} 305 sn match match expr {}
306 sn ok Ok(expr)
290 sn ref &expr 307 sn ref &expr
291 sn refm &mut expr 308 sn refm &mut expr
292 "#]], 309 "#]],
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/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index cde42024c..1d8a3c404 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -44,7 +44,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
44 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> 44 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span>
45<span class="punctuation">}</span> 45<span class="punctuation">}</span>
46 46
47<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Clone</span><span class="punctuation">,</span><span class="attribute"> Debug</span><span class="punctuation">)</span><span class="attribute">]</span> 47
48<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span> 48<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span>
49 <span class="keyword">pub</span> <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> 49 <span class="keyword">pub</span> <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span>
50 <span class="keyword">pub</span> <span class="field declaration">y</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> 50 <span class="keyword">pub</span> <span class="field declaration">y</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span>
@@ -74,7 +74,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
74 <span class="punctuation">}</span> 74 <span class="punctuation">}</span>
75<span class="punctuation">}</span> 75<span class="punctuation">}</span>
76 76
77<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Copy</span><span class="punctuation">,</span><span class="attribute"> Clone</span><span class="punctuation">)</span><span class="attribute">]</span> 77<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Copy</span><span class="punctuation">)</span><span class="attribute">]</span>
78<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span> 78<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span>
79 <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> 79 <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span>
80<span class="punctuation">}</span> 80<span class="punctuation">}</span>
@@ -144,7 +144,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
144 <span class="variable">y</span><span class="punctuation">;</span> 144 <span class="variable">y</span><span class="punctuation">;</span>
145 145
146 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> 146 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span>
147 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="unresolved_reference">clone</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 147 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span>
148 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 148 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
149 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 149 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
150 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span> 150 <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 57d4e1252..211e62ea1 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -18,7 +18,7 @@ pub mod marker {
18 pub trait Copy {} 18 pub trait Copy {}
19} 19}
20 20
21#[derive(Clone, Debug)] 21
22struct Foo { 22struct Foo {
23 pub x: i32, 23 pub x: i32,
24 pub y: i32, 24 pub y: i32,
@@ -48,7 +48,7 @@ impl Foo {
48 } 48 }
49} 49}
50 50
51#[derive(Copy, Clone)] 51#[derive(Copy)]
52struct FooCopy { 52struct FooCopy {
53 x: u32, 53 x: u32,
54} 54}
@@ -118,7 +118,7 @@ fn main() {
118 y; 118 y;
119 119
120 let mut foo = Foo { x, y: x }; 120 let mut foo = Foo { x, y: x };
121 let foo2 = foo.clone(); 121 let foo2 = Foo { x, y: x };
122 foo.quop(); 122 foo.quop();
123 foo.qux(); 123 foo.qux();
124 foo.baz(foo2); 124 foo.baz(foo2);
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index a8ad917fb..d987b2500 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -636,7 +636,10 @@ impl<'a> TreeSink for TtTreeSink<'a> {
636 let (text, id) = match leaf { 636 let (text, id) = match leaf {
637 tt::Leaf::Ident(ident) => (ident.text.clone(), ident.id), 637 tt::Leaf::Ident(ident) => (ident.text.clone(), ident.id),
638 tt::Leaf::Punct(punct) => { 638 tt::Leaf::Punct(punct) => {
639 (SmolStr::new_inline_from_ascii(1, &[punct.char as u8]), punct.id) 639 assert!(punct.char.is_ascii());
640 let char = &(punct.char as u8);
641 let text = std::str::from_utf8(std::slice::from_ref(char)).unwrap();
642 (SmolStr::new_inline(text), punct.id)
640 } 643 }
641 tt::Leaf::Literal(lit) => (lit.text.clone(), lit.id), 644 tt::Leaf::Literal(lit) => (lit.text.clone(), lit.id),
642 }; 645 };
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/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 212f98a30..96313aaec 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -63,7 +63,7 @@ pub(crate) struct GlobalState {
63 req_queue: ReqQueue, 63 req_queue: ReqQueue,
64 pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, 64 pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
65 pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, 65 pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
66 pub(crate) flycheck: Option<FlycheckHandle>, 66 pub(crate) flycheck: Vec<FlycheckHandle>,
67 pub(crate) flycheck_sender: Sender<flycheck::Message>, 67 pub(crate) flycheck_sender: Sender<flycheck::Message>,
68 pub(crate) flycheck_receiver: Receiver<flycheck::Message>, 68 pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
69 pub(crate) config: Config, 69 pub(crate) config: Config,
@@ -115,7 +115,7 @@ impl GlobalState {
115 req_queue: ReqQueue::default(), 115 req_queue: ReqQueue::default(),
116 task_pool, 116 task_pool,
117 loader, 117 loader,
118 flycheck: None, 118 flycheck: Vec::new(),
119 flycheck_sender, 119 flycheck_sender,
120 flycheck_receiver, 120 flycheck_receiver,
121 config, 121 config,
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/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 8d3132581..06ab9d508 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -266,8 +266,8 @@ impl GlobalState {
266 } 266 }
267 } 267 }
268 268
269 flycheck::Message::Progress(status) => { 269 flycheck::Message::Progress { id, progress } => {
270 let (state, message) = match status { 270 let (state, message) = match progress {
271 flycheck::Progress::DidStart => { 271 flycheck::Progress::DidStart => {
272 self.diagnostics.clear_check(); 272 self.diagnostics.clear_check();
273 (Progress::Begin, None) 273 (Progress::Begin, None)
@@ -284,14 +284,21 @@ impl GlobalState {
284 } 284 }
285 }; 285 };
286 286
287 self.report_progress("cargo check", state, message, None); 287 // When we're running multiple flychecks, we have to include a disambiguator in
288 // the title, or the editor complains. Note that this is a user-facing string.
289 let title = if self.flycheck.len() == 1 {
290 "cargo check".to_string()
291 } else {
292 format!("cargo check (#{})", id + 1)
293 };
294 self.report_progress(&title, state, message, None);
288 } 295 }
289 }, 296 },
290 } 297 }
291 298
292 let state_changed = self.process_changes(); 299 let state_changed = self.process_changes();
293 if prev_status == Status::Loading && self.status == Status::Ready { 300 if prev_status == Status::Loading && self.status == Status::Ready {
294 if let Some(flycheck) = &self.flycheck { 301 for flycheck in &self.flycheck {
295 flycheck.update(); 302 flycheck.update();
296 } 303 }
297 } 304 }
@@ -490,7 +497,7 @@ impl GlobalState {
490 Ok(()) 497 Ok(())
491 })? 498 })?
492 .on::<lsp_types::notification::DidSaveTextDocument>(|this, params| { 499 .on::<lsp_types::notification::DidSaveTextDocument>(|this, params| {
493 if let Some(flycheck) = &this.flycheck { 500 for flycheck in &this.flycheck {
494 flycheck.update(); 501 flycheck.update();
495 } 502 }
496 if let Ok(abs_path) = from_proto::abs_path(&params.text_document.uri) { 503 if let Ok(abs_path) = from_proto::abs_path(&params.text_document.uri) {
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index b819618cb..de0dbcad4 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(
@@ -232,29 +235,37 @@ impl GlobalState {
232 let config = match self.config.flycheck.clone() { 235 let config = match self.config.flycheck.clone() {
233 Some(it) => it, 236 Some(it) => it,
234 None => { 237 None => {
235 self.flycheck = None; 238 self.flycheck = Vec::new();
236 return; 239 return;
237 } 240 }
238 }; 241 };
239 242
240 let sender = self.flycheck_sender.clone(); 243 let sender = self.flycheck_sender.clone();
241 let sender = Box::new(move |msg| sender.send(msg).unwrap());
242 self.flycheck = self 244 self.flycheck = self
243 .workspaces 245 .workspaces
244 .iter() 246 .iter()
245 // FIXME: Figure out the multi-workspace situation 247 .enumerate()
246 .find_map(|w| match w { 248 .filter_map(|(id, w)| match w {
247 ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some(cargo.workspace_root()), 249 ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some((id, cargo.workspace_root())),
248 ProjectWorkspace::Json { project, .. } => { 250 ProjectWorkspace::Json { project, .. } => {
249 // Enable flychecks for json projects if a custom flycheck command was supplied 251 // Enable flychecks for json projects if a custom flycheck command was supplied
250 // in the workspace configuration. 252 // in the workspace configuration.
251 match config { 253 match config {
252 FlycheckConfig::CustomCommand { .. } => project.path(), 254 FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
253 _ => None, 255 _ => None,
254 } 256 }
255 } 257 }
256 }) 258 })
257 .map(move |root| FlycheckHandle::spawn(sender, config, root.to_path_buf().into())) 259 .map(|(id, root)| {
260 let sender = sender.clone();
261 FlycheckHandle::spawn(
262 id,
263 Box::new(move |msg| sender.send(msg).unwrap()),
264 config.clone(),
265 root.to_path_buf().into(),
266 )
267 })
268 .collect();
258 } 269 }
259} 270}
260 271
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 8b1c65dd6..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(
@@ -347,6 +363,7 @@ impl ast::UseTree {
347 self.clone() 363 self.clone()
348 } 364 }
349 365
366 /// Splits off the given prefix, making it the path component of the use tree, appending the rest of the path to all UseTreeList items.
350 #[must_use] 367 #[must_use]
351 pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree { 368 pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree {
352 let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() { 369 let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() {
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 580eb3690..3a184094c 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}
@@ -61,6 +65,10 @@ pub fn path_from_segments(
61 }) 65 })
62} 66}
63 67
68pub fn glob_use_tree() -> ast::UseTree {
69 ast_from_text("use *;")
70}
71
64pub fn use_tree( 72pub fn use_tree(
65 path: ast::Path, 73 path: ast::Path,
66 use_tree_list: Option<ast::UseTreeList>, 74 use_tree_list: Option<ast::UseTreeList>,