aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-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.rs356
-rw-r--r--crates/hir/src/code_model.rs4
-rw-r--r--crates/hir_def/src/data.rs2
-rw-r--r--crates/hir_def/src/diagnostics.rs42
-rw-r--r--crates/hir_def/src/item_tree.rs9
-rw-r--r--crates/hir_def/src/item_tree/lower.rs13
-rw-r--r--crates/hir_def/src/item_tree/tests.rs8
-rw-r--r--crates/hir_def/src/nameres.rs93
-rw-r--r--crates/hir_def/src/nameres/collector.rs418
-rw-r--r--crates/hir_def/src/nameres/tests.rs1
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs131
-rw-r--r--crates/hir_def/src/nameres/tests/mod_resolution.rs36
-rw-r--r--crates/hir_def/src/path.rs4
-rw-r--r--crates/hir_def/src/test_db.rs42
-rw-r--r--crates/hir_expand/src/db.rs4
-rw-r--r--crates/hir_expand/src/eager.rs2
-rw-r--r--crates/hir_expand/src/hygiene.rs2
-rw-r--r--crates/hir_expand/src/lib.rs2
-rw-r--r--crates/hir_ty/Cargo.toml6
-rw-r--r--crates/hir_ty/src/display.rs9
-rw-r--r--crates/hir_ty/src/lib.rs11
-rw-r--r--crates/hir_ty/src/lower.rs10
-rw-r--r--crates/hir_ty/src/method_resolution.rs8
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs36
-rw-r--r--crates/hir_ty/src/traits/chalk.rs22
-rw-r--r--crates/hir_ty/src/traits/chalk/interner.rs1
-rw-r--r--crates/hir_ty/src/traits/chalk/mapping.rs69
-rw-r--r--crates/hir_ty/src/traits/chalk/tls.rs11
-rw-r--r--crates/ide/src/completion/complete_trait_impl.rs248
-rw-r--r--crates/ide/src/diagnostics.rs62
-rw-r--r--crates/project_model/src/lib.rs8
-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/config.rs5
-rw-r--r--crates/rust-analyzer/src/handlers.rs13
-rw-r--r--crates/rust-analyzer/src/reload.rs11
-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.rs1
-rw-r--r--crates/syntax/src/ast/make.rs4
42 files changed, 1354 insertions, 438 deletions
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 e48407fcc..8ac907707 100644
--- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
@@ -312,7 +312,7 @@ impl std::fmt<|> for Foo {
312} 312}
313 ", 313 ",
314 r" 314 r"
315use std::fmt::{Debug, self}; 315use std::fmt::{self, Debug};
316 316
317impl fmt for Foo { 317impl fmt for Foo {
318} 318}
@@ -330,9 +330,8 @@ use std::fmt::{Debug, nested::{Display}};
330impl std::fmt::nested<|> for Foo { 330impl std::fmt::nested<|> for Foo {
331} 331}
332", 332",
333 // FIXME(veykril): should be nested::{self, Display} here
334 r" 333 r"
335use std::fmt::{Debug, nested::{Display}, nested}; 334use std::fmt::{Debug, nested::{self, Display}};
336 335
337impl nested for Foo { 336impl nested for Foo {
338} 337}
@@ -350,9 +349,8 @@ use std::fmt::{Debug, nested::{self, Display}};
350impl std::fmt::nested<|> for Foo { 349impl std::fmt::nested<|> for Foo {
351} 350}
352", 351",
353 // FIXME(veykril): nested is duplicated now
354 r" 352 r"
355use std::fmt::{Debug, nested::{self, Display}, nested}; 353use std::fmt::{Debug, nested::{self, Display}};
356 354
357impl nested for Foo { 355impl nested for Foo {
358} 356}
@@ -371,7 +369,7 @@ impl std::fmt::nested::Debug<|> for Foo {
371} 369}
372", 370",
373 r" 371 r"
374use std::fmt::{Debug, nested::{Display}, nested::Debug}; 372use std::fmt::{Debug, nested::{Debug, Display}};
375 373
376impl Debug for Foo { 374impl Debug for Foo {
377} 375}
@@ -409,7 +407,7 @@ impl std::fmt::Display<|> for Foo {
409} 407}
410", 408",
411 r" 409 r"
412use std::fmt::{nested::Debug, Display}; 410use std::fmt::{Display, nested::Debug};
413 411
414impl Display for Foo { 412impl Display for Foo {
415} 413}
@@ -429,12 +427,8 @@ use crate::{
429 427
430fn foo() { crate::ty::lower<|>::trait_env() } 428fn foo() { crate::ty::lower<|>::trait_env() }
431", 429",
432 // FIXME(veykril): formatting broke here
433 r" 430 r"
434use crate::{ 431use crate::{AssocItem, ty::{Substs, Ty, lower}};
435 ty::{Substs, Ty},
436 AssocItem,
437ty::lower};
438 432
439fn foo() { lower::trait_env() } 433fn foo() { lower::trait_env() }
440", 434",
@@ -633,7 +627,7 @@ fn main() {
633} 627}
634 ", 628 ",
635 r" 629 r"
636use std::fmt::{Display, self}; 630use std::fmt::{self, Display};
637 631
638fn main() { 632fn main() {
639 fmt; 633 fmt;
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index 6d110aaaf..09f4a2224 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,
232 } 289 }
233 } 290 }
291}
234 292
235 res 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)
333 }
334 }
335}
336
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,63 @@ 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 #[ignore] // FIXME: Support this
814 fn merge_partial_path() {
815 check_full(
816 "ast::Foo",
817 r"use syntax::{ast, algo};",
818 r"use syntax::{ast::{self, Foo}, algo};",
819 )
820 }
821
822 #[test]
823 fn merge_glob_nested() {
824 check_full(
825 "foo::bar::quux::Fez",
826 r"use foo::bar::{Baz, quux::*};",
827 r"use foo::bar::{Baz, quux::{self::*, Fez}};",
689 ) 828 )
690 } 829 }
691 830
692 #[test] 831 #[test]
693 fn merge_last_too_long() { 832 fn merge_last_too_long() {
694 mark::check!(test_last_merge_too_long); 833 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 } 834 }
702 835
703 #[test] 836 #[test]
@@ -710,6 +843,42 @@ use foo::bar::baz::Qux;",
710 ); 843 );
711 } 844 }
712 845
846 #[test]
847 fn merge_last_fail() {
848 check_merge_only_fail(
849 r"use foo::bar::{baz::{Qux, Fez}};",
850 r"use foo::bar::{baaz::{Quux, Feez}};",
851 MergeBehaviour::Last,
852 );
853 }
854
855 #[test]
856 fn merge_last_fail1() {
857 check_merge_only_fail(
858 r"use foo::bar::{baz::{Qux, Fez}};",
859 r"use foo::bar::baaz::{Quux, Feez};",
860 MergeBehaviour::Last,
861 );
862 }
863
864 #[test]
865 fn merge_last_fail2() {
866 check_merge_only_fail(
867 r"use foo::bar::baz::{Qux, Fez};",
868 r"use foo::bar::{baaz::{Quux, Feez}};",
869 MergeBehaviour::Last,
870 );
871 }
872
873 #[test]
874 fn merge_last_fail3() {
875 check_merge_only_fail(
876 r"use foo::bar::baz::{Qux, Fez};",
877 r"use foo::bar::baaz::{Quux, Feez};",
878 MergeBehaviour::Last,
879 );
880 }
881
713 fn check( 882 fn check(
714 path: &str, 883 path: &str,
715 ra_fixture_before: &str, 884 ra_fixture_before: &str,
@@ -742,4 +911,23 @@ use foo::bar::baz::Qux;",
742 fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 911 fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
743 check(path, ra_fixture_before, ra_fixture_after, None) 912 check(path, ra_fixture_before, ra_fixture_after, None)
744 } 913 }
914
915 fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) {
916 let use0 = ast::SourceFile::parse(ra_fixture0)
917 .tree()
918 .syntax()
919 .descendants()
920 .find_map(ast::Use::cast)
921 .unwrap();
922
923 let use1 = ast::SourceFile::parse(ra_fixture1)
924 .tree()
925 .syntax()
926 .descendants()
927 .find_map(ast::Use::cast)
928 .unwrap();
929
930 let result = try_merge_imports(&use0, &use1, mb);
931 assert_eq!(result.map(|u| u.to_string()), None);
932 }
745} 933}
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 7a9747fc7..849c8f6d0 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -908,12 +908,12 @@ impl MacroDef {
908 908
909 /// Indicate it is a proc-macro 909 /// Indicate it is a proc-macro
910 pub fn is_proc_macro(&self) -> bool { 910 pub fn is_proc_macro(&self) -> bool {
911 matches!(self.id.kind, MacroDefKind::CustomDerive(_)) 911 matches!(self.id.kind, MacroDefKind::ProcMacro(_))
912 } 912 }
913 913
914 /// Indicate it is a derive macro 914 /// Indicate it is a derive macro
915 pub fn is_derive_macro(&self) -> bool { 915 pub fn is_derive_macro(&self) -> bool {
916 matches!(self.id.kind, MacroDefKind::CustomDerive(_) | MacroDefKind::BuiltInDerive(_)) 916 matches!(self.id.kind, MacroDefKind::ProcMacro(_) | MacroDefKind::BuiltInDerive(_))
917 } 917 }
918} 918}
919 919
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index 9a8eb4ede..6190906da 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -54,6 +54,7 @@ pub struct TypeAliasData {
54 pub name: Name, 54 pub name: Name,
55 pub type_ref: Option<TypeRef>, 55 pub type_ref: Option<TypeRef>,
56 pub visibility: RawVisibility, 56 pub visibility: RawVisibility,
57 pub is_extern: bool,
57 /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl). 58 /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
58 pub bounds: Vec<TypeBound>, 59 pub bounds: Vec<TypeBound>,
59} 60}
@@ -71,6 +72,7 @@ impl TypeAliasData {
71 name: typ.name.clone(), 72 name: typ.name.clone(),
72 type_ref: typ.type_ref.clone(), 73 type_ref: typ.type_ref.clone(),
73 visibility: item_tree[typ.visibility].clone(), 74 visibility: item_tree[typ.visibility].clone(),
75 is_extern: typ.is_extern,
74 bounds: typ.bounds.to_vec(), 76 bounds: typ.bounds.to_vec(),
75 }) 77 })
76 } 78 }
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs
index 3e19d9117..2ec0fd3fb 100644
--- a/crates/hir_def/src/diagnostics.rs
+++ b/crates/hir_def/src/diagnostics.rs
@@ -28,3 +28,45 @@ impl Diagnostic for UnresolvedModule {
28 self 28 self
29 } 29 }
30} 30}
31
32#[derive(Debug)]
33pub struct UnresolvedExternCrate {
34 pub file: HirFileId,
35 pub item: AstPtr<ast::ExternCrate>,
36}
37
38impl Diagnostic for UnresolvedExternCrate {
39 fn code(&self) -> DiagnosticCode {
40 DiagnosticCode("unresolved-extern-crate")
41 }
42 fn message(&self) -> String {
43 "unresolved extern crate".to_string()
44 }
45 fn display_source(&self) -> InFile<SyntaxNodePtr> {
46 InFile::new(self.file, self.item.clone().into())
47 }
48 fn as_any(&self) -> &(dyn Any + Send + 'static) {
49 self
50 }
51}
52
53#[derive(Debug)]
54pub struct UnresolvedImport {
55 pub file: HirFileId,
56 pub node: AstPtr<ast::UseTree>,
57}
58
59impl Diagnostic for UnresolvedImport {
60 fn code(&self) -> DiagnosticCode {
61 DiagnosticCode("unresolved-import")
62 }
63 fn message(&self) -> String {
64 "unresolved import".to_string()
65 }
66 fn display_source(&self) -> InFile<SyntaxNodePtr> {
67 InFile::new(self.file, self.node.clone().into())
68 }
69 fn as_any(&self) -> &(dyn Any + Send + 'static) {
70 self
71 }
72}
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index e14722cae..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 ...`.
@@ -592,6 +596,7 @@ pub struct TypeAlias {
592 pub bounds: Box<[TypeBound]>, 596 pub bounds: Box<[TypeBound]>,
593 pub generic_params: GenericParamsId, 597 pub generic_params: GenericParamsId,
594 pub type_ref: Option<TypeRef>, 598 pub type_ref: Option<TypeRef>,
599 pub is_extern: bool,
595 pub ast_id: FileAstId<ast::TypeAlias>, 600 pub ast_id: FileAstId<ast::TypeAlias>,
596} 601}
597 602
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 6a503d785..54814f141 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -364,6 +364,7 @@ impl Ctx {
364 generic_params, 364 generic_params,
365 type_ref, 365 type_ref,
366 ast_id, 366 ast_id,
367 is_extern: false,
367 }; 368 };
368 Some(id(self.data().type_aliases.alloc(res))) 369 Some(id(self.data().type_aliases.alloc(res)))
369 } 370 }
@@ -482,7 +483,7 @@ impl Ctx {
482 ModPath::expand_use_item( 483 ModPath::expand_use_item(
483 InFile::new(self.file, use_item.clone()), 484 InFile::new(self.file, use_item.clone()),
484 &self.hygiene, 485 &self.hygiene,
485 |path, _tree, is_glob, alias| { 486 |path, _use_tree, is_glob, alias| {
486 imports.push(id(tree.imports.alloc(Import { 487 imports.push(id(tree.imports.alloc(Import {
487 path, 488 path,
488 alias, 489 alias,
@@ -490,6 +491,7 @@ impl Ctx {
490 is_glob, 491 is_glob,
491 is_prelude, 492 is_prelude,
492 ast_id, 493 ast_id,
494 index: imports.len(),
493 }))); 495 })));
494 }, 496 },
495 ); 497 );
@@ -501,7 +503,7 @@ impl Ctx {
501 &mut self, 503 &mut self,
502 extern_crate: &ast::ExternCrate, 504 extern_crate: &ast::ExternCrate,
503 ) -> Option<FileItemTreeId<ExternCrate>> { 505 ) -> Option<FileItemTreeId<ExternCrate>> {
504 let path = ModPath::from_name_ref(&extern_crate.name_ref()?); 506 let name = extern_crate.name_ref()?.as_name();
505 let alias = extern_crate.rename().map(|a| { 507 let alias = extern_crate.rename().map(|a| {
506 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)
507 }); 509 });
@@ -510,7 +512,7 @@ impl Ctx {
510 // FIXME: cfg_attr 512 // FIXME: cfg_attr
511 let is_macro_use = extern_crate.has_atom_attr("macro_use"); 513 let is_macro_use = extern_crate.has_atom_attr("macro_use");
512 514
513 let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id }; 515 let res = ExternCrate { name, alias, visibility, is_macro_use, ast_id };
514 Some(id(self.data().extern_crates.alloc(res))) 516 Some(id(self.data().extern_crates.alloc(res)))
515 } 517 }
516 518
@@ -558,8 +560,9 @@ impl Ctx {
558 statik.into() 560 statik.into()
559 } 561 }
560 ast::ExternItem::TypeAlias(ty) => { 562 ast::ExternItem::TypeAlias(ty) => {
561 let id = self.lower_type_alias(&ty)?; 563 let foreign_ty = self.lower_type_alias(&ty)?;
562 id.into() 564 self.data().type_aliases[foreign_ty.index].is_extern = true;
565 foreign_ty.into()
563 } 566 }
564 ast::ExternItem::MacroCall(_) => return None, 567 ast::ExternItem::MacroCall(_) => return None,
565 }; 568 };
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs
index 620e697d4..1a806cda5 100644
--- a/crates/hir_def/src/item_tree/tests.rs
+++ b/crates/hir_def/src/item_tree/tests.rs
@@ -228,15 +228,15 @@ 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 }]) }]
239 > TypeAlias { name: Name(Text("AssocTy")), visibility: RawVisibilityId("pub(self)"), bounds: [Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Tr"))] }, generic_args: [Some(GenericArgs { args: [Type(Tuple([]))], has_self_type: false, bindings: [] })] })], generic_params: GenericParamsId(4294967295), type_ref: None, ast_id: FileAstId::<syntax::ast::generated::nodes::TypeAlias>(8) } 239 > TypeAlias { name: Name(Text("AssocTy")), visibility: RawVisibilityId("pub(self)"), bounds: [Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Tr"))] }, generic_args: [Some(GenericArgs { args: [Type(Tuple([]))], has_self_type: false, bindings: [] })] })], generic_params: GenericParamsId(4294967295), type_ref: None, is_extern: false, ast_id: FileAstId::<syntax::ast::generated::nodes::TypeAlias>(8) }
240 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_const"))] }, input: None }]) }] 240 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_const"))] }, input: None }]) }]
241 > Const { name: Some(Name(Text("CONST"))), visibility: RawVisibilityId("pub(self)"), type_ref: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("u8"))] }, generic_args: [None] }), ast_id: FileAstId::<syntax::ast::generated::nodes::Const>(9) } 241 > Const { name: Some(Name(Text("CONST"))), visibility: RawVisibilityId("pub(self)"), type_ref: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("u8"))] }, generic_args: [None] }), ast_id: FileAstId::<syntax::ast::generated::nodes::Const>(9) }
242 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_method"))] }, input: None }]) }] 242 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_method"))] }, input: None }]) }]
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index bf302172d..5e4d73c1f 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -288,31 +288,70 @@ pub enum ModuleSource {
288 288
289mod diagnostics { 289mod diagnostics {
290 use hir_expand::diagnostics::DiagnosticSink; 290 use hir_expand::diagnostics::DiagnosticSink;
291 use hir_expand::hygiene::Hygiene;
292 use hir_expand::InFile;
291 use syntax::{ast, AstPtr}; 293 use syntax::{ast, AstPtr};
292 294
293 use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId}; 295 use crate::path::ModPath;
296 use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId};
294 297
295 #[derive(Debug, PartialEq, Eq)] 298 #[derive(Debug, PartialEq, Eq)]
296 pub(super) enum DefDiagnostic { 299 enum DiagnosticKind {
297 UnresolvedModule { 300 UnresolvedModule { declaration: AstId<ast::Module>, candidate: String },
298 module: LocalModuleId, 301
299 declaration: AstId<ast::Module>, 302 UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
300 candidate: String, 303
301 }, 304 UnresolvedImport { ast: AstId<ast::Use>, index: usize },
305 }
306
307 #[derive(Debug, PartialEq, Eq)]
308 pub(super) struct DefDiagnostic {
309 in_module: LocalModuleId,
310 kind: DiagnosticKind,
302 } 311 }
303 312
304 impl DefDiagnostic { 313 impl DefDiagnostic {
314 pub(super) fn unresolved_module(
315 container: LocalModuleId,
316 declaration: AstId<ast::Module>,
317 candidate: String,
318 ) -> Self {
319 Self {
320 in_module: container,
321 kind: DiagnosticKind::UnresolvedModule { declaration, candidate },
322 }
323 }
324
325 pub(super) fn unresolved_extern_crate(
326 container: LocalModuleId,
327 declaration: AstId<ast::ExternCrate>,
328 ) -> Self {
329 Self {
330 in_module: container,
331 kind: DiagnosticKind::UnresolvedExternCrate { ast: declaration },
332 }
333 }
334
335 pub(super) fn unresolved_import(
336 container: LocalModuleId,
337 ast: AstId<ast::Use>,
338 index: usize,
339 ) -> Self {
340 Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } }
341 }
342
305 pub(super) fn add_to( 343 pub(super) fn add_to(
306 &self, 344 &self,
307 db: &dyn DefDatabase, 345 db: &dyn DefDatabase,
308 target_module: LocalModuleId, 346 target_module: LocalModuleId,
309 sink: &mut DiagnosticSink, 347 sink: &mut DiagnosticSink,
310 ) { 348 ) {
311 match self { 349 if self.in_module != target_module {
312 DefDiagnostic::UnresolvedModule { module, declaration, candidate } => { 350 return;
313 if *module != target_module { 351 }
314 return; 352
315 } 353 match &self.kind {
354 DiagnosticKind::UnresolvedModule { declaration, candidate } => {
316 let decl = declaration.to_node(db.upcast()); 355 let decl = declaration.to_node(db.upcast());
317 sink.push(UnresolvedModule { 356 sink.push(UnresolvedModule {
318 file: declaration.file_id, 357 file: declaration.file_id,
@@ -320,6 +359,36 @@ mod diagnostics {
320 candidate: candidate.clone(), 359 candidate: candidate.clone(),
321 }) 360 })
322 } 361 }
362
363 DiagnosticKind::UnresolvedExternCrate { ast } => {
364 let item = ast.to_node(db.upcast());
365 sink.push(UnresolvedExternCrate {
366 file: ast.file_id,
367 item: AstPtr::new(&item),
368 });
369 }
370
371 DiagnosticKind::UnresolvedImport { ast, index } => {
372 let use_item = ast.to_node(db.upcast());
373 let hygiene = Hygiene::new(db.upcast(), ast.file_id);
374 let mut cur = 0;
375 let mut tree = None;
376 ModPath::expand_use_item(
377 InFile::new(ast.file_id, use_item),
378 &hygiene,
379 |_mod_path, use_tree, _is_glob, _alias| {
380 if cur == *index {
381 tree = Some(use_tree.clone());
382 }
383
384 cur += 1;
385 },
386 );
387
388 if let Some(tree) = tree {
389 sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
390 }
391 }
323 } 392 }
324 } 393 }
325 } 394 }
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 3e99c8773..4c3993ff0 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -3,8 +3,11 @@
3//! `DefCollector::collect` contains the fixed-point iteration loop which 3//! `DefCollector::collect` contains the fixed-point iteration loop which
4//! resolves imports and expands macros. 4//! resolves imports and expands macros.
5 5
6use std::iter;
7
6use base_db::{CrateId, FileId, ProcMacroId}; 8use base_db::{CrateId, FileId, ProcMacroId};
7use cfg::CfgOptions; 9use cfg::CfgOptions;
10use hir_expand::InFile;
8use hir_expand::{ 11use hir_expand::{
9 ast_id_map::FileAstId, 12 ast_id_map::FileAstId,
10 builtin_derive::find_builtin_derive, 13 builtin_derive::find_builtin_derive,
@@ -14,6 +17,7 @@ use hir_expand::{
14 HirFileId, MacroCallId, MacroDefId, MacroDefKind, 17 HirFileId, MacroCallId, MacroDefId, MacroDefKind,
15}; 18};
16use rustc_hash::FxHashMap; 19use rustc_hash::FxHashMap;
20use rustc_hash::FxHashSet;
17use syntax::ast; 21use syntax::ast;
18use test_utils::mark; 22use test_utils::mark;
19 23
@@ -21,9 +25,7 @@ use crate::{
21 attr::Attrs, 25 attr::Attrs,
22 db::DefDatabase, 26 db::DefDatabase,
23 item_scope::{ImportType, PerNsGlobImports}, 27 item_scope::{ImportType, PerNsGlobImports},
24 item_tree::{ 28 item_tree::{self, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind},
25 self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind,
26 },
27 nameres::{ 29 nameres::{
28 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, 30 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
29 BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, 31 BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
@@ -112,6 +114,12 @@ impl PartialResolvedImport {
112} 114}
113 115
114#[derive(Clone, Debug, Eq, PartialEq)] 116#[derive(Clone, Debug, Eq, PartialEq)]
117enum ImportSource {
118 Import(ItemTreeId<item_tree::Import>),
119 ExternCrate(ItemTreeId<item_tree::ExternCrate>),
120}
121
122#[derive(Clone, Debug, Eq, PartialEq)]
115struct Import { 123struct Import {
116 pub path: ModPath, 124 pub path: ModPath,
117 pub alias: Option<ImportAlias>, 125 pub alias: Option<ImportAlias>,
@@ -120,11 +128,12 @@ struct Import {
120 pub is_prelude: bool, 128 pub is_prelude: bool,
121 pub is_extern_crate: bool, 129 pub is_extern_crate: bool,
122 pub is_macro_use: bool, 130 pub is_macro_use: bool,
131 source: ImportSource,
123} 132}
124 133
125impl Import { 134impl Import {
126 fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self { 135 fn from_use(tree: &ItemTree, id: ItemTreeId<item_tree::Import>) -> Self {
127 let it = &tree[id]; 136 let it = &tree[id.value];
128 let visibility = &tree[it.visibility]; 137 let visibility = &tree[it.visibility];
129 Self { 138 Self {
130 path: it.path.clone(), 139 path: it.path.clone(),
@@ -134,20 +143,22 @@ impl Import {
134 is_prelude: it.is_prelude, 143 is_prelude: it.is_prelude,
135 is_extern_crate: false, 144 is_extern_crate: false,
136 is_macro_use: false, 145 is_macro_use: false,
146 source: ImportSource::Import(id),
137 } 147 }
138 } 148 }
139 149
140 fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self { 150 fn from_extern_crate(tree: &ItemTree, id: ItemTreeId<item_tree::ExternCrate>) -> Self {
141 let it = &tree[id]; 151 let it = &tree[id.value];
142 let visibility = &tree[it.visibility]; 152 let visibility = &tree[it.visibility];
143 Self { 153 Self {
144 path: it.path.clone(), 154 path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())),
145 alias: it.alias.clone(), 155 alias: it.alias.clone(),
146 visibility: visibility.clone(), 156 visibility: visibility.clone(),
147 is_glob: false, 157 is_glob: false,
148 is_prelude: false, 158 is_prelude: false,
149 is_extern_crate: true, 159 is_extern_crate: true,
150 is_macro_use: it.is_macro_use, 160 is_macro_use: it.is_macro_use,
161 source: ImportSource::ExternCrate(id),
151 } 162 }
152 } 163 }
153} 164}
@@ -245,9 +256,10 @@ impl DefCollector<'_> {
245 256
246 let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); 257 let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
247 // show unresolved imports in completion, etc 258 // show unresolved imports in completion, etc
248 for directive in unresolved_imports { 259 for directive in &unresolved_imports {
249 self.record_resolved_import(&directive) 260 self.record_resolved_import(directive)
250 } 261 }
262 self.unresolved_imports = unresolved_imports;
251 263
252 // Record proc-macros 264 // Record proc-macros
253 self.collect_proc_macro(); 265 self.collect_proc_macro();
@@ -261,7 +273,7 @@ impl DefCollector<'_> {
261 let macro_id = MacroDefId { 273 let macro_id = MacroDefId {
262 ast_id: None, 274 ast_id: None,
263 krate: Some(krate), 275 krate: Some(krate),
264 kind: MacroDefKind::CustomDerive(expander), 276 kind: MacroDefKind::ProcMacro(expander),
265 local_inner: false, 277 local_inner: false,
266 }; 278 };
267 279
@@ -346,20 +358,15 @@ impl DefCollector<'_> {
346 fn import_macros_from_extern_crate( 358 fn import_macros_from_extern_crate(
347 &mut self, 359 &mut self,
348 current_module_id: LocalModuleId, 360 current_module_id: LocalModuleId,
349 import: &item_tree::ExternCrate, 361 extern_crate: &item_tree::ExternCrate,
350 ) { 362 ) {
351 log::debug!( 363 log::debug!(
352 "importing macros from extern crate: {:?} ({:?})", 364 "importing macros from extern crate: {:?} ({:?})",
353 import, 365 extern_crate,
354 self.def_map.edition, 366 self.def_map.edition,
355 ); 367 );
356 368
357 let res = self.def_map.resolve_name_in_extern_prelude( 369 let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name);
358 &import
359 .path
360 .as_ident()
361 .expect("extern crate should have been desugared to one-element path"),
362 );
363 370
364 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { 371 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
365 mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); 372 mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
@@ -420,7 +427,11 @@ impl DefCollector<'_> {
420 .as_ident() 427 .as_ident()
421 .expect("extern crate should have been desugared to one-element path"), 428 .expect("extern crate should have been desugared to one-element path"),
422 ); 429 );
423 PartialResolvedImport::Resolved(res) 430 if res.is_none() {
431 PartialResolvedImport::Unresolved
432 } else {
433 PartialResolvedImport::Resolved(res)
434 }
424 } else { 435 } else {
425 let res = self.def_map.resolve_path_fp_with_macro( 436 let res = self.def_map.resolve_path_fp_with_macro(
426 self.db, 437 self.db,
@@ -774,7 +785,51 @@ impl DefCollector<'_> {
774 .collect(item_tree.top_level_items()); 785 .collect(item_tree.top_level_items());
775 } 786 }
776 787
777 fn finish(self) -> CrateDefMap { 788 fn finish(mut self) -> CrateDefMap {
789 // Emit diagnostics for all remaining unresolved imports.
790
791 // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't
792 // resolve. We first emit diagnostics for unresolved extern crates and collect the missing
793 // crate names. Then we emit diagnostics for unresolved imports, but only if the import
794 // doesn't start with an unresolved crate's name. Due to renaming and reexports, this is a
795 // heuristic, but it works in practice.
796 let mut diagnosed_extern_crates = FxHashSet::default();
797 for directive in &self.unresolved_imports {
798 if let ImportSource::ExternCrate(krate) = directive.import.source {
799 let item_tree = self.db.item_tree(krate.file_id);
800 let extern_crate = &item_tree[krate.value];
801
802 diagnosed_extern_crates.insert(extern_crate.name.clone());
803
804 self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate(
805 directive.module_id,
806 InFile::new(krate.file_id, extern_crate.ast_id),
807 ));
808 }
809 }
810
811 for directive in &self.unresolved_imports {
812 if let ImportSource::Import(import) = &directive.import.source {
813 let item_tree = self.db.item_tree(import.file_id);
814 let import_data = &item_tree[import.value];
815
816 match (import_data.path.segments.first(), &import_data.path.kind) {
817 (Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => {
818 if diagnosed_extern_crates.contains(krate) {
819 continue;
820 }
821 }
822 _ => {}
823 }
824
825 self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
826 directive.module_id,
827 InFile::new(import.file_id, import_data.ast_id),
828 import_data.index,
829 ));
830 }
831 }
832
778 self.def_map 833 self.def_map
779 } 834 }
780} 835}
@@ -819,179 +874,184 @@ impl ModCollector<'_, '_> {
819 874
820 for &item in items { 875 for &item in items {
821 let attrs = self.item_tree.attrs(item.into()); 876 let attrs = self.item_tree.attrs(item.into());
822 if self.is_cfg_enabled(attrs) { 877 if !self.is_cfg_enabled(attrs) {
823 let module = 878 continue;
824 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; 879 }
825 let container = ContainerId::ModuleId(module); 880 let module =
826 881 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
827 let mut def = None; 882 let container = ContainerId::ModuleId(module);
828 match item { 883
829 ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs), 884 let mut def = None;
830 ModItem::Import(import_id) => { 885 match item {
831 self.def_collector.unresolved_imports.push(ImportDirective { 886 ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs),
832 module_id: self.module_id, 887 ModItem::Import(import_id) => {
833 import: Import::from_use(&self.item_tree, import_id), 888 self.def_collector.unresolved_imports.push(ImportDirective {
834 status: PartialResolvedImport::Unresolved, 889 module_id: self.module_id,
835 }) 890 import: Import::from_use(
836 } 891 &self.item_tree,
837 ModItem::ExternCrate(import_id) => { 892 InFile::new(self.file_id, import_id),
838 self.def_collector.unresolved_imports.push(ImportDirective { 893 ),
839 module_id: self.module_id, 894 status: PartialResolvedImport::Unresolved,
840 import: Import::from_extern_crate(&self.item_tree, import_id), 895 })
841 status: PartialResolvedImport::Unresolved, 896 }
842 }) 897 ModItem::ExternCrate(import_id) => {
843 } 898 self.def_collector.unresolved_imports.push(ImportDirective {
844 ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]), 899 module_id: self.module_id,
845 ModItem::Impl(imp) => { 900 import: Import::from_extern_crate(
846 let module = ModuleId { 901 &self.item_tree,
847 krate: self.def_collector.def_map.krate, 902 InFile::new(self.file_id, import_id),
848 local_id: self.module_id, 903 ),
849 }; 904 status: PartialResolvedImport::Unresolved,
850 let container = ContainerId::ModuleId(module); 905 })
851 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } 906 }
852 .intern(self.def_collector.db); 907 ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]),
853 self.def_collector.def_map.modules[self.module_id] 908 ModItem::Impl(imp) => {
854 .scope 909 let module = ModuleId {
855 .define_impl(impl_id) 910 krate: self.def_collector.def_map.krate,
856 } 911 local_id: self.module_id,
857 ModItem::Function(id) => { 912 };
858 let func = &self.item_tree[id]; 913 let container = ContainerId::ModuleId(module);
859 def = Some(DefData { 914 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) }
860 id: FunctionLoc { 915 .intern(self.def_collector.db);
861 container: container.into(), 916 self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id)
862 id: ItemTreeId::new(self.file_id, id), 917 }
863 } 918 ModItem::Function(id) => {
864 .intern(self.def_collector.db) 919 let func = &self.item_tree[id];
865 .into(), 920 def = Some(DefData {
866 name: &func.name, 921 id: FunctionLoc {
867 visibility: &self.item_tree[func.visibility], 922 container: container.into(),
868 has_constructor: false, 923 id: ItemTreeId::new(self.file_id, id),
869 }); 924 }
870 } 925 .intern(self.def_collector.db)
871 ModItem::Struct(id) => { 926 .into(),
872 let it = &self.item_tree[id]; 927 name: &func.name,
873 928 visibility: &self.item_tree[func.visibility],
874 // FIXME: check attrs to see if this is an attribute macro invocation; 929 has_constructor: false,
875 // in which case we don't add the invocation, just a single attribute 930 });
876 // macro invocation 931 }
877 self.collect_derives(attrs, it.ast_id.upcast()); 932 ModItem::Struct(id) => {
878 933 let it = &self.item_tree[id];
879 def = Some(DefData {
880 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
881 .intern(self.def_collector.db)
882 .into(),
883 name: &it.name,
884 visibility: &self.item_tree[it.visibility],
885 has_constructor: it.kind != StructDefKind::Record,
886 });
887 }
888 ModItem::Union(id) => {
889 let it = &self.item_tree[id];
890 934
891 // FIXME: check attrs to see if this is an attribute macro invocation; 935 // FIXME: check attrs to see if this is an attribute macro invocation;
892 // in which case we don't add the invocation, just a single attribute 936 // in which case we don't add the invocation, just a single attribute
893 // macro invocation 937 // macro invocation
894 self.collect_derives(attrs, it.ast_id.upcast()); 938 self.collect_derives(attrs, it.ast_id.upcast());
895 939
896 def = Some(DefData { 940 def = Some(DefData {
897 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } 941 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
898 .intern(self.def_collector.db) 942 .intern(self.def_collector.db)
899 .into(), 943 .into(),
900 name: &it.name, 944 name: &it.name,
901 visibility: &self.item_tree[it.visibility], 945 visibility: &self.item_tree[it.visibility],
902 has_constructor: false, 946 has_constructor: it.kind != StructDefKind::Record,
903 }); 947 });
904 } 948 }
905 ModItem::Enum(id) => { 949 ModItem::Union(id) => {
906 let it = &self.item_tree[id]; 950 let it = &self.item_tree[id];
907 951
908 // FIXME: check attrs to see if this is an attribute macro invocation; 952 // FIXME: check attrs to see if this is an attribute macro invocation;
909 // in which case we don't add the invocation, just a single attribute 953 // in which case we don't add the invocation, just a single attribute
910 // macro invocation 954 // macro invocation
911 self.collect_derives(attrs, it.ast_id.upcast()); 955 self.collect_derives(attrs, it.ast_id.upcast());
912 956
913 def = Some(DefData { 957 def = Some(DefData {
914 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } 958 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) }
915 .intern(self.def_collector.db) 959 .intern(self.def_collector.db)
916 .into(), 960 .into(),
917 name: &it.name, 961 name: &it.name,
918 visibility: &self.item_tree[it.visibility], 962 visibility: &self.item_tree[it.visibility],
919 has_constructor: false, 963 has_constructor: false,
920 }); 964 });
921 } 965 }
922 ModItem::Const(id) => { 966 ModItem::Enum(id) => {
923 let it = &self.item_tree[id]; 967 let it = &self.item_tree[id];
924
925 if let Some(name) = &it.name {
926 def = Some(DefData {
927 id: ConstLoc {
928 container: container.into(),
929 id: ItemTreeId::new(self.file_id, id),
930 }
931 .intern(self.def_collector.db)
932 .into(),
933 name,
934 visibility: &self.item_tree[it.visibility],
935 has_constructor: false,
936 });
937 }
938 }
939 ModItem::Static(id) => {
940 let it = &self.item_tree[id];
941 968
942 def = Some(DefData { 969 // FIXME: check attrs to see if this is an attribute macro invocation;
943 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } 970 // in which case we don't add the invocation, just a single attribute
944 .intern(self.def_collector.db) 971 // macro invocation
945 .into(), 972 self.collect_derives(attrs, it.ast_id.upcast());
946 name: &it.name,
947 visibility: &self.item_tree[it.visibility],
948 has_constructor: false,
949 });
950 }
951 ModItem::Trait(id) => {
952 let it = &self.item_tree[id];
953 973
954 def = Some(DefData { 974 def = Some(DefData {
955 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } 975 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) }
956 .intern(self.def_collector.db) 976 .intern(self.def_collector.db)
957 .into(), 977 .into(),
958 name: &it.name, 978 name: &it.name,
959 visibility: &self.item_tree[it.visibility], 979 visibility: &self.item_tree[it.visibility],
960 has_constructor: false, 980 has_constructor: false,
961 }); 981 });
962 } 982 }
963 ModItem::TypeAlias(id) => { 983 ModItem::Const(id) => {
964 let it = &self.item_tree[id]; 984 let it = &self.item_tree[id];
965 985
986 if let Some(name) = &it.name {
966 def = Some(DefData { 987 def = Some(DefData {
967 id: TypeAliasLoc { 988 id: ConstLoc {
968 container: container.into(), 989 container: container.into(),
969 id: ItemTreeId::new(self.file_id, id), 990 id: ItemTreeId::new(self.file_id, id),
970 } 991 }
971 .intern(self.def_collector.db) 992 .intern(self.def_collector.db)
972 .into(), 993 .into(),
973 name: &it.name, 994 name,
974 visibility: &self.item_tree[it.visibility], 995 visibility: &self.item_tree[it.visibility],
975 has_constructor: false, 996 has_constructor: false,
976 }); 997 });
977 } 998 }
978 } 999 }
1000 ModItem::Static(id) => {
1001 let it = &self.item_tree[id];
979 1002
980 if let Some(DefData { id, name, visibility, has_constructor }) = def { 1003 def = Some(DefData {
981 self.def_collector.def_map.modules[self.module_id].scope.define_def(id); 1004 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) }
982 let vis = self 1005 .intern(self.def_collector.db)
983 .def_collector 1006 .into(),
984 .def_map 1007 name: &it.name,
985 .resolve_visibility(self.def_collector.db, self.module_id, visibility) 1008 visibility: &self.item_tree[it.visibility],
986 .unwrap_or(Visibility::Public); 1009 has_constructor: false,
987 self.def_collector.update( 1010 });
988 self.module_id, 1011 }
989 &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))], 1012 ModItem::Trait(id) => {
990 vis, 1013 let it = &self.item_tree[id];
991 ImportType::Named, 1014
992 ) 1015 def = Some(DefData {
1016 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) }
1017 .intern(self.def_collector.db)
1018 .into(),
1019 name: &it.name,
1020 visibility: &self.item_tree[it.visibility],
1021 has_constructor: false,
1022 });
1023 }
1024 ModItem::TypeAlias(id) => {
1025 let it = &self.item_tree[id];
1026
1027 def = Some(DefData {
1028 id: TypeAliasLoc {
1029 container: container.into(),
1030 id: ItemTreeId::new(self.file_id, id),
1031 }
1032 .intern(self.def_collector.db)
1033 .into(),
1034 name: &it.name,
1035 visibility: &self.item_tree[it.visibility],
1036 has_constructor: false,
1037 });
993 } 1038 }
994 } 1039 }
1040
1041 if let Some(DefData { id, name, visibility, has_constructor }) = def {
1042 self.def_collector.def_map.modules[self.module_id].scope.define_def(id);
1043 let vis = self
1044 .def_collector
1045 .def_map
1046 .resolve_visibility(self.def_collector.db, self.module_id, visibility)
1047 .unwrap_or(Visibility::Public);
1048 self.def_collector.update(
1049 self.module_id,
1050 &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))],
1051 vis,
1052 ImportType::Named,
1053 )
1054 }
995 } 1055 }
996 } 1056 }
997 1057
@@ -1051,13 +1111,11 @@ impl ModCollector<'_, '_> {
1051 self.import_all_legacy_macros(module_id); 1111 self.import_all_legacy_macros(module_id);
1052 } 1112 }
1053 } 1113 }
1054 Err(candidate) => self.def_collector.def_map.diagnostics.push( 1114 Err(candidate) => {
1055 DefDiagnostic::UnresolvedModule { 1115 self.def_collector.def_map.diagnostics.push(
1056 module: self.module_id, 1116 DefDiagnostic::unresolved_module(self.module_id, ast_id, candidate),
1057 declaration: ast_id, 1117 );
1058 candidate, 1118 }
1059 },
1060 ),
1061 }; 1119 };
1062 } 1120 }
1063 } 1121 }
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index 5ca30dac9..11d84f808 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -2,6 +2,7 @@ mod globs;
2mod incremental; 2mod incremental;
3mod macros; 3mod macros;
4mod mod_resolution; 4mod mod_resolution;
5mod diagnostics;
5mod primitives; 6mod primitives;
6 7
7use std::sync::Arc; 8use std::sync::Arc;
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
new file mode 100644
index 000000000..576b813d2
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -0,0 +1,131 @@
1use base_db::fixture::WithFixture;
2use base_db::FileId;
3use base_db::SourceDatabaseExt;
4use hir_expand::db::AstDatabase;
5use rustc_hash::FxHashMap;
6use syntax::TextRange;
7use syntax::TextSize;
8
9use crate::test_db::TestDB;
10
11fn check_diagnostics(ra_fixture: &str) {
12 let db: TestDB = TestDB::with_files(ra_fixture);
13 let annotations = db.extract_annotations();
14 assert!(!annotations.is_empty());
15
16 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
17 db.diagnostics(|d| {
18 let src = d.display_source();
19 let root = db.parse_or_expand(src.file_id).unwrap();
20 // FIXME: macros...
21 let file_id = src.file_id.original_file(&db);
22 let range = src.value.to_node(&root).text_range();
23 let message = d.message().to_owned();
24 actual.entry(file_id).or_default().push((range, message));
25 });
26
27 for (file_id, diags) in actual.iter_mut() {
28 diags.sort_by_key(|it| it.0.start());
29 let text = db.file_text(*file_id);
30 // For multiline spans, place them on line start
31 for (range, content) in diags {
32 if text[*range].contains('\n') {
33 *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
34 *content = format!("... {}", content);
35 }
36 }
37 }
38
39 assert_eq!(annotations, actual);
40}
41
42#[test]
43fn unresolved_import() {
44 check_diagnostics(
45 r"
46 use does_exist;
47 use does_not_exist;
48 //^^^^^^^^^^^^^^ unresolved import
49
50 mod does_exist {}
51 ",
52 );
53}
54
55#[test]
56fn unresolved_import_in_use_tree() {
57 // Only the relevant part of a nested `use` item should be highlighted.
58 check_diagnostics(
59 r"
60 use does_exist::{Exists, DoesntExist};
61 //^^^^^^^^^^^ unresolved import
62
63 use {does_not_exist::*, does_exist};
64 //^^^^^^^^^^^^^^^^^ unresolved import
65
66 use does_not_exist::{
67 a,
68 //^ unresolved import
69 b,
70 //^ unresolved import
71 c,
72 //^ unresolved import
73 };
74
75 mod does_exist {
76 pub struct Exists;
77 }
78 ",
79 );
80}
81
82#[test]
83fn unresolved_extern_crate() {
84 check_diagnostics(
85 r"
86 //- /main.rs crate:main deps:core
87 extern crate core;
88 extern crate doesnotexist;
89 //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
90 //- /lib.rs crate:core
91 ",
92 );
93}
94
95#[test]
96fn dedup_unresolved_import_from_unresolved_crate() {
97 check_diagnostics(
98 r"
99 //- /main.rs crate:main
100 mod a {
101 extern crate doesnotexist;
102 //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
103
104 // Should not error, since we already errored for the missing crate.
105 use doesnotexist::{self, bla, *};
106
107 use crate::doesnotexist;
108 //^^^^^^^^^^^^^^^^^^^ unresolved import
109 }
110
111 mod m {
112 use super::doesnotexist;
113 //^^^^^^^^^^^^^^^^^^^ unresolved import
114 }
115 ",
116 );
117}
118
119#[test]
120fn unresolved_module() {
121 check_diagnostics(
122 r"
123 //- /lib.rs
124 mod foo;
125 mod bar;
126 //^^^^^^^^ unresolved module
127 mod baz {}
128 //- /foo.rs
129 ",
130 );
131}
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs
index 1f619787e..f93337a6e 100644
--- a/crates/hir_def/src/nameres/tests/mod_resolution.rs
+++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs
@@ -672,42 +672,6 @@ pub struct Baz;
672} 672}
673 673
674#[test] 674#[test]
675fn unresolved_module_diagnostics() {
676 let db = TestDB::with_files(
677 r"
678 //- /lib.rs
679 mod foo;
680 mod bar;
681 mod baz {}
682 //- /foo.rs
683 ",
684 );
685 let krate = db.test_crate();
686
687 let crate_def_map = db.crate_def_map(krate);
688
689 expect![[r#"
690 [
691 UnresolvedModule {
692 module: Idx::<ModuleData>(0),
693 declaration: InFile {
694 file_id: HirFileId(
695 FileId(
696 FileId(
697 0,
698 ),
699 ),
700 ),
701 value: FileAstId::<syntax::ast::generated::nodes::Module>(1),
702 },
703 candidate: "bar.rs",
704 },
705 ]
706 "#]]
707 .assert_debug_eq(&crate_def_map.diagnostics);
708}
709
710#[test]
711fn module_resolution_decl_inside_module_in_non_crate_root_2() { 675fn module_resolution_decl_inside_module_in_non_crate_root_2() {
712 check( 676 check(
713 r#" 677 r#"
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index 99395667d..734310458 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -56,10 +56,6 @@ impl ModPath {
56 ModPath { kind, segments } 56 ModPath { kind, segments }
57 } 57 }
58 58
59 pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> ModPath {
60 name_ref.as_name().into()
61 }
62
63 /// Converts an `tt::Ident` into a single-identifier `Path`. 59 /// Converts an `tt::Ident` into a single-identifier `Path`.
64 pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath { 60 pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath {
65 ident.as_name().into() 61 ident.as_name().into()
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs
index 42a762936..fb1d3c974 100644
--- a/crates/hir_def/src/test_db.rs
+++ b/crates/hir_def/src/test_db.rs
@@ -5,9 +5,15 @@ use std::{
5 sync::{Arc, Mutex}, 5 sync::{Arc, Mutex},
6}; 6};
7 7
8use base_db::SourceDatabase;
8use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; 9use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
9use hir_expand::db::AstDatabase; 10use hir_expand::db::AstDatabase;
11use hir_expand::diagnostics::Diagnostic;
12use hir_expand::diagnostics::DiagnosticSinkBuilder;
13use rustc_hash::FxHashMap;
10use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
15use syntax::TextRange;
16use test_utils::extract_annotations;
11 17
12use crate::db::DefDatabase; 18use crate::db::DefDatabase;
13 19
@@ -98,4 +104,40 @@ impl TestDB {
98 }) 104 })
99 .collect() 105 .collect()
100 } 106 }
107
108 pub fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
109 let mut files = Vec::new();
110 let crate_graph = self.crate_graph();
111 for krate in crate_graph.iter() {
112 let crate_def_map = self.crate_def_map(krate);
113 for (module_id, _) in crate_def_map.modules.iter() {
114 let file_id = crate_def_map[module_id].origin.file_id();
115 files.extend(file_id)
116 }
117 }
118 assert!(!files.is_empty());
119 files
120 .into_iter()
121 .filter_map(|file_id| {
122 let text = self.file_text(file_id);
123 let annotations = extract_annotations(&text);
124 if annotations.is_empty() {
125 return None;
126 }
127 Some((file_id, annotations))
128 })
129 .collect()
130 }
131
132 pub fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
133 let crate_graph = self.crate_graph();
134 for krate in crate_graph.iter() {
135 let crate_def_map = self.crate_def_map(krate);
136
137 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
138 for (module_id, _) in crate_def_map.modules.iter() {
139 crate_def_map.add_diagnostics(self, module_id, &mut sink);
140 }
141 }
142 }
101} 143}
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 710694a34..b591130ca 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -143,7 +143,7 @@ pub(crate) fn macro_def(
143 Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default()))) 143 Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default())))
144 } 144 }
145 MacroDefKind::BuiltInEager(_) => None, 145 MacroDefKind::BuiltInEager(_) => None,
146 MacroDefKind::CustomDerive(expander) => { 146 MacroDefKind::ProcMacro(expander) => {
147 Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default()))) 147 Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default())))
148 } 148 }
149 } 149 }
@@ -249,7 +249,7 @@ pub(crate) fn expand_proc_macro(
249 }; 249 };
250 250
251 let expander = match loc.def.kind { 251 let expander = match loc.def.kind {
252 MacroDefKind::CustomDerive(expander) => expander, 252 MacroDefKind::ProcMacro(expander) => expander,
253 _ => unreachable!(), 253 _ => unreachable!(),
254 }; 254 };
255 255
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index 10c45646f..2f37d7189 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -129,7 +129,7 @@ fn eager_macro_recur(
129 MacroDefKind::Declarative 129 MacroDefKind::Declarative
130 | MacroDefKind::BuiltIn(_) 130 | MacroDefKind::BuiltIn(_)
131 | MacroDefKind::BuiltInDerive(_) 131 | MacroDefKind::BuiltInDerive(_)
132 | MacroDefKind::CustomDerive(_) => { 132 | MacroDefKind::ProcMacro(_) => {
133 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?; 133 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?;
134 // replace macro inside 134 // replace macro inside
135 eager_macro_recur(db, expanded, krate, macro_resolver)? 135 eager_macro_recur(db, expanded, krate, macro_resolver)?
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index 845e9cbc1..d383b968d 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -33,7 +33,7 @@ impl Hygiene {
33 MacroDefKind::BuiltIn(_) => (None, false), 33 MacroDefKind::BuiltIn(_) => (None, false),
34 MacroDefKind::BuiltInDerive(_) => (None, false), 34 MacroDefKind::BuiltInDerive(_) => (None, false),
35 MacroDefKind::BuiltInEager(_) => (None, false), 35 MacroDefKind::BuiltInEager(_) => (None, false),
36 MacroDefKind::CustomDerive(_) => (None, false), 36 MacroDefKind::ProcMacro(_) => (None, false),
37 } 37 }
38 } 38 }
39 MacroCallId::EagerMacro(_id) => (None, false), 39 MacroCallId::EagerMacro(_id) => (None, false),
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 2be15e841..17f1178ed 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -246,7 +246,7 @@ pub enum MacroDefKind {
246 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander 246 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
247 BuiltInDerive(BuiltinDeriveExpander), 247 BuiltInDerive(BuiltinDeriveExpander),
248 BuiltInEager(EagerExpander), 248 BuiltInEager(EagerExpander),
249 CustomDerive(ProcMacroExpander), 249 ProcMacro(ProcMacroExpander),
250} 250}
251 251
252#[derive(Debug, Clone, PartialEq, Eq, Hash)] 252#[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 7d02aaf95..bc86df2b1 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.25.0" } 20chalk-solve = { version = "0.27.0" }
21chalk-ir = { version = "0.25.0" } 21chalk-ir = { version = "0.27.0" }
22chalk-recursive = { version = "0.25.0" } 22chalk-recursive = { version = "0.27.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/display.rs b/crates/hir_ty/src/display.rs
index efb48c7ee..f389c5a4b 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -380,6 +380,15 @@ impl HirDisplay for ApplicationTy {
380 write!(f, ">")?; 380 write!(f, ">")?;
381 } 381 }
382 } 382 }
383 TypeCtor::ForeignType(type_alias) => {
384 let type_alias = f.db.type_alias_data(type_alias);
385 write!(f, "{}", type_alias.name)?;
386 if self.parameters.len() > 0 {
387 write!(f, "<")?;
388 f.write_joined(&*self.parameters.0, ", ")?;
389 write!(f, ">")?;
390 }
391 }
383 TypeCtor::OpaqueType(opaque_ty_id) => { 392 TypeCtor::OpaqueType(opaque_ty_id) => {
384 match opaque_ty_id { 393 match opaque_ty_id {
385 OpaqueTyId::ReturnTypeImplTrait(func, idx) => { 394 OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index f16d1fc97..768d95eff 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -134,6 +134,9 @@ pub enum TypeCtor {
134 /// representing the Future::Output type. 134 /// representing the Future::Output type.
135 OpaqueType(OpaqueTyId), 135 OpaqueType(OpaqueTyId),
136 136
137 /// Represents a foreign type declared in external blocks.
138 ForeignType(TypeAliasId),
139
137 /// The type of a specific closure. 140 /// The type of a specific closure.
138 /// 141 ///
139 /// The closure signature is stored in a `FnPtr` type in the first type 142 /// The closure signature is stored in a `FnPtr` type in the first type
@@ -168,6 +171,10 @@ impl TypeCtor {
168 let generic_params = generics(db.upcast(), type_alias.into()); 171 let generic_params = generics(db.upcast(), type_alias.into());
169 generic_params.len() 172 generic_params.len()
170 } 173 }
174 TypeCtor::ForeignType(type_alias) => {
175 let generic_params = generics(db.upcast(), type_alias.into());
176 generic_params.len()
177 }
171 TypeCtor::OpaqueType(opaque_ty_id) => { 178 TypeCtor::OpaqueType(opaque_ty_id) => {
172 match opaque_ty_id { 179 match opaque_ty_id {
173 OpaqueTyId::ReturnTypeImplTrait(func, _) => { 180 OpaqueTyId::ReturnTypeImplTrait(func, _) => {
@@ -204,6 +211,9 @@ impl TypeCtor {
204 TypeCtor::AssociatedType(type_alias) => { 211 TypeCtor::AssociatedType(type_alias) => {
205 Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate) 212 Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate)
206 } 213 }
214 TypeCtor::ForeignType(type_alias) => {
215 Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate)
216 }
207 TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id { 217 TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id {
208 OpaqueTyId::ReturnTypeImplTrait(func, _) => { 218 OpaqueTyId::ReturnTypeImplTrait(func, _) => {
209 Some(func.lookup(db.upcast()).module(db.upcast()).krate) 219 Some(func.lookup(db.upcast()).module(db.upcast()).krate)
@@ -231,6 +241,7 @@ impl TypeCtor {
231 TypeCtor::Adt(adt) => Some(adt.into()), 241 TypeCtor::Adt(adt) => Some(adt.into()),
232 TypeCtor::FnDef(callable) => Some(callable.into()), 242 TypeCtor::FnDef(callable) => Some(callable.into()),
233 TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()), 243 TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()),
244 TypeCtor::ForeignType(type_alias) => Some(type_alias.into()),
234 TypeCtor::OpaqueType(_impl_trait_id) => None, 245 TypeCtor::OpaqueType(_impl_trait_id) => None,
235 } 246 }
236 } 247 }
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index cd574e983..708e2af0f 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -1101,10 +1101,14 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
1101 let resolver = t.resolver(db.upcast()); 1101 let resolver = t.resolver(db.upcast());
1102 let ctx = 1102 let ctx =
1103 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); 1103 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
1104 let type_ref = &db.type_alias_data(t).type_ref;
1105 let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); 1104 let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
1106 let inner = Ty::from_hir(&ctx, type_ref.as_ref().unwrap_or(&TypeRef::Error)); 1105 if db.type_alias_data(t).is_extern {
1107 Binders::new(substs.len(), inner) 1106 Binders::new(substs.len(), Ty::apply(TypeCtor::ForeignType(t), substs))
1107 } else {
1108 let type_ref = &db.type_alias_data(t).type_ref;
1109 let inner = Ty::from_hir(&ctx, type_ref.as_ref().unwrap_or(&TypeRef::Error));
1110 Binders::new(substs.len(), inner)
1111 }
1108} 1112}
1109 1113
1110#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 1114#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index ec59145c7..8961df404 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -250,6 +250,14 @@ impl Ty {
250 TypeCtor::Adt(def_id) => { 250 TypeCtor::Adt(def_id) => {
251 return Some(std::iter::once(def_id.module(db.upcast()).krate).collect()) 251 return Some(std::iter::once(def_id.module(db.upcast()).krate).collect())
252 } 252 }
253 TypeCtor::ForeignType(type_alias_id) => {
254 return Some(
255 std::iter::once(
256 type_alias_id.lookup(db.upcast()).module(db.upcast()).krate,
257 )
258 .collect(),
259 )
260 }
253 TypeCtor::Bool => lang_item_crate!("bool"), 261 TypeCtor::Bool => lang_item_crate!("bool"),
254 TypeCtor::Char => lang_item_crate!("char"), 262 TypeCtor::Char => lang_item_crate!("char"),
255 TypeCtor::Float(f) => match f.bitness { 263 TypeCtor::Float(f) => match f.bitness {
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index 23b2601e6..0f17ff151 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -1051,3 +1051,39 @@ fn dyn_trait_super_trait_not_in_scope() {
1051 "#]], 1051 "#]],
1052 ); 1052 );
1053} 1053}
1054
1055#[test]
1056fn method_resolution_foreign_opaque_type() {
1057 check_infer(
1058 r#"
1059 extern "C" {
1060 type S;
1061 fn f() -> &'static S;
1062 }
1063
1064 impl S {
1065 fn foo(&self) -> bool {
1066 true
1067 }
1068 }
1069
1070 fn test() {
1071 let s = unsafe { f() };
1072 s.foo();
1073 }
1074 "#,
1075 expect![[r#"
1076 75..79 'self': &S
1077 89..109 '{ ... }': bool
1078 99..103 'true': bool
1079 123..167 '{ ...o(); }': ()
1080 133..134 's': &S
1081 137..151 'unsafe { f() }': &S
1082 144..151 '{ f() }': &S
1083 146..147 'f': fn f() -> &S
1084 146..149 'f()': &S
1085 157..158 's': &S
1086 157..164 's.foo()': bool
1087 "#]],
1088 );
1089}
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index 57d0a32df..27f0ed628 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -23,7 +23,8 @@ use crate::{
23 ProjectionTy, Substs, TraitRef, Ty, TypeCtor, 23 ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
24}; 24};
25use mapping::{ 25use mapping::{
26 convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsValue, 26 convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsAssocType,
27 TypeAliasAsValue,
27}; 28};
28 29
29pub use self::interner::*; 30pub use self::interner::*;
@@ -340,7 +341,7 @@ pub(crate) fn associated_ty_data_query(
340 id: AssocTypeId, 341 id: AssocTypeId,
341) -> Arc<AssociatedTyDatum> { 342) -> Arc<AssociatedTyDatum> {
342 debug!("associated_ty_data {:?}", id); 343 debug!("associated_ty_data {:?}", id);
343 let type_alias: TypeAliasId = from_chalk(db, id); 344 let type_alias: TypeAliasId = from_chalk::<TypeAliasAsAssocType, _>(db, id).0;
344 let trait_ = match type_alias.lookup(db.upcast()).container { 345 let trait_ = match type_alias.lookup(db.upcast()).container {
345 AssocContainerId::TraitId(t) => t, 346 AssocContainerId::TraitId(t) => t,
346 _ => panic!("associated type not in trait"), 347 _ => panic!("associated type not in trait"),
@@ -394,8 +395,10 @@ pub(crate) fn trait_datum_query(
394 fundamental: false, 395 fundamental: false,
395 }; 396 };
396 let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); 397 let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars);
397 let associated_ty_ids = 398 let associated_ty_ids = trait_data
398 trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect(); 399 .associated_types()
400 .map(|type_alias| TypeAliasAsAssocType(type_alias).to_chalk(db))
401 .collect();
399 let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; 402 let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses };
400 let well_known = 403 let well_known =
401 lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); 404 lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name));
@@ -433,6 +436,7 @@ fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str {
433 WellKnownTrait::FnMut => "fn_mut", 436 WellKnownTrait::FnMut => "fn_mut",
434 WellKnownTrait::Fn => "fn", 437 WellKnownTrait::Fn => "fn",
435 WellKnownTrait::Unsize => "unsize", 438 WellKnownTrait::Unsize => "unsize",
439 WellKnownTrait::Unpin => "unpin",
436 } 440 }
437} 441}
438 442
@@ -576,7 +580,7 @@ fn type_alias_associated_ty_value(
576 let value_bound = rust_ir::AssociatedTyValueBound { ty: ty.value.to_chalk(db) }; 580 let value_bound = rust_ir::AssociatedTyValueBound { ty: ty.value.to_chalk(db) };
577 let value = rust_ir::AssociatedTyValue { 581 let value = rust_ir::AssociatedTyValue {
578 impl_id: impl_id.to_chalk(db), 582 impl_id: impl_id.to_chalk(db),
579 associated_ty_id: assoc_ty.to_chalk(db), 583 associated_ty_id: TypeAliasAsAssocType(assoc_ty).to_chalk(db),
580 value: make_binders(value_bound, ty.num_binders), 584 value: make_binders(value_bound, ty.num_binders),
581 }; 585 };
582 Arc::new(value) 586 Arc::new(value)
@@ -611,9 +615,11 @@ pub(crate) fn fn_def_datum_query(
611 }; 615 };
612 let datum = FnDefDatum { 616 let datum = FnDefDatum {
613 id: fn_def_id, 617 id: fn_def_id,
614 abi: (), 618 sig: chalk_ir::FnSig {
615 safety: chalk_ir::Safety::Safe, 619 abi: (),
616 variadic: sig.value.is_varargs, 620 safety: chalk_ir::Safety::Safe,
621 variadic: sig.value.is_varargs,
622 },
617 binders: make_binders(bound, sig.num_binders), 623 binders: make_binders(bound, sig.num_binders),
618 }; 624 };
619 Arc::new(datum) 625 Arc::new(datum)
diff --git a/crates/hir_ty/src/traits/chalk/interner.rs b/crates/hir_ty/src/traits/chalk/interner.rs
index eb35db3ff..f9304b7d0 100644
--- a/crates/hir_ty/src/traits/chalk/interner.rs
+++ b/crates/hir_ty/src/traits/chalk/interner.rs
@@ -12,6 +12,7 @@ pub struct Interner;
12 12
13pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>; 13pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
14pub type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>; 14pub type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>;
15pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>;
15pub type TraitId = chalk_ir::TraitId<Interner>; 16pub type TraitId = chalk_ir::TraitId<Interner>;
16pub type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>; 17pub type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>;
17pub type AdtId = chalk_ir::AdtId<Interner>; 18pub type AdtId = chalk_ir::AdtId<Interner>;
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs
index d6bacba1d..d42f4bba9 100644
--- a/crates/hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/hir_ty/src/traits/chalk/mapping.rs
@@ -34,9 +34,11 @@ impl ToChalk for Ty {
34 let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner); 34 let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner);
35 chalk_ir::TyData::Function(chalk_ir::FnPointer { 35 chalk_ir::TyData::Function(chalk_ir::FnPointer {
36 num_binders: 0, 36 num_binders: 0,
37 abi: (), 37 sig: chalk_ir::FnSig {
38 safety: chalk_ir::Safety::Safe, 38 abi: (),
39 variadic: is_varargs, 39 safety: chalk_ir::Safety::Safe,
40 variadic: is_varargs,
41 },
40 substitution, 42 substitution,
41 }) 43 })
42 .intern(&Interner) 44 .intern(&Interner)
@@ -48,7 +50,7 @@ impl ToChalk for Ty {
48 } 50 }
49 }, 51 },
50 Ty::Projection(proj_ty) => { 52 Ty::Projection(proj_ty) => {
51 let associated_ty_id = proj_ty.associated_ty.to_chalk(db); 53 let associated_ty_id = TypeAliasAsAssocType(proj_ty.associated_ty).to_chalk(db);
52 let substitution = proj_ty.parameters.to_chalk(db); 54 let substitution = proj_ty.parameters.to_chalk(db);
53 chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { 55 chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
54 associated_ty_id, 56 associated_ty_id,
@@ -114,7 +116,8 @@ impl ToChalk for Ty {
114 Ty::Placeholder(db.lookup_intern_type_param_id(interned_id)) 116 Ty::Placeholder(db.lookup_intern_type_param_id(interned_id))
115 } 117 }
116 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => { 118 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => {
117 let associated_ty = from_chalk(db, proj.associated_ty_id); 119 let associated_ty =
120 from_chalk::<TypeAliasAsAssocType, _>(db, proj.associated_ty_id).0;
118 let parameters = from_chalk(db, proj.substitution); 121 let parameters = from_chalk(db, proj.substitution);
119 Ty::Projection(ProjectionTy { associated_ty, parameters }) 122 Ty::Projection(ProjectionTy { associated_ty, parameters })
120 } 123 }
@@ -125,7 +128,7 @@ impl ToChalk for Ty {
125 } 128 }
126 chalk_ir::TyData::Function(chalk_ir::FnPointer { 129 chalk_ir::TyData::Function(chalk_ir::FnPointer {
127 num_binders, 130 num_binders,
128 variadic, 131 sig: chalk_ir::FnSig { variadic, .. },
129 substitution, 132 substitution,
130 .. 133 ..
131 }) => { 134 }) => {
@@ -290,8 +293,9 @@ impl ToChalk for TypeCtor {
290 fn to_chalk(self, db: &dyn HirDatabase) -> TypeName<Interner> { 293 fn to_chalk(self, db: &dyn HirDatabase) -> TypeName<Interner> {
291 match self { 294 match self {
292 TypeCtor::AssociatedType(type_alias) => { 295 TypeCtor::AssociatedType(type_alias) => {
293 let type_id = type_alias.to_chalk(db); 296 let assoc_type = TypeAliasAsAssocType(type_alias);
294 TypeName::AssociatedType(type_id) 297 let assoc_type_id = assoc_type.to_chalk(db);
298 TypeName::AssociatedType(assoc_type_id)
295 } 299 }
296 300
297 TypeCtor::OpaqueType(impl_trait_id) => { 301 TypeCtor::OpaqueType(impl_trait_id) => {
@@ -299,6 +303,12 @@ impl ToChalk for TypeCtor {
299 TypeName::OpaqueType(id) 303 TypeName::OpaqueType(id)
300 } 304 }
301 305
306 TypeCtor::ForeignType(type_alias) => {
307 let foreign_type = TypeAliasAsForeignType(type_alias);
308 let foreign_type_id = foreign_type.to_chalk(db);
309 TypeName::Foreign(foreign_type_id)
310 }
311
302 TypeCtor::Bool => TypeName::Scalar(Scalar::Bool), 312 TypeCtor::Bool => TypeName::Scalar(Scalar::Bool),
303 TypeCtor::Char => TypeName::Scalar(Scalar::Char), 313 TypeCtor::Char => TypeName::Scalar(Scalar::Char),
304 TypeCtor::Int(int_ty) => TypeName::Scalar(int_ty_to_chalk(int_ty)), 314 TypeCtor::Int(int_ty) => TypeName::Scalar(int_ty_to_chalk(int_ty)),
@@ -339,7 +349,9 @@ impl ToChalk for TypeCtor {
339 fn from_chalk(db: &dyn HirDatabase, type_name: TypeName<Interner>) -> TypeCtor { 349 fn from_chalk(db: &dyn HirDatabase, type_name: TypeName<Interner>) -> TypeCtor {
340 match type_name { 350 match type_name {
341 TypeName::Adt(struct_id) => TypeCtor::Adt(struct_id.0), 351 TypeName::Adt(struct_id) => TypeCtor::Adt(struct_id.0),
342 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), 352 TypeName::AssociatedType(type_id) => {
353 TypeCtor::AssociatedType(from_chalk::<TypeAliasAsAssocType, _>(db, type_id).0)
354 }
343 TypeName::OpaqueType(opaque_type_id) => { 355 TypeName::OpaqueType(opaque_type_id) => {
344 TypeCtor::OpaqueType(from_chalk(db, opaque_type_id)) 356 TypeCtor::OpaqueType(from_chalk(db, opaque_type_id))
345 } 357 }
@@ -379,6 +391,10 @@ impl ToChalk for TypeCtor {
379 TypeCtor::Closure { def, expr } 391 TypeCtor::Closure { def, expr }
380 } 392 }
381 393
394 TypeName::Foreign(foreign_def_id) => {
395 TypeCtor::ForeignType(from_chalk::<TypeAliasAsForeignType, _>(db, foreign_def_id).0)
396 }
397
382 TypeName::Error => { 398 TypeName::Error => {
383 // this should not be reached, since we don't represent TypeName::Error with TypeCtor 399 // this should not be reached, since we don't represent TypeName::Error with TypeCtor
384 unreachable!() 400 unreachable!()
@@ -488,15 +504,31 @@ impl ToChalk for CallableDefId {
488 } 504 }
489} 505}
490 506
491impl ToChalk for TypeAliasId { 507pub struct TypeAliasAsAssocType(pub TypeAliasId);
508
509impl ToChalk for TypeAliasAsAssocType {
492 type Chalk = AssocTypeId; 510 type Chalk = AssocTypeId;
493 511
494 fn to_chalk(self, _db: &dyn HirDatabase) -> AssocTypeId { 512 fn to_chalk(self, _db: &dyn HirDatabase) -> AssocTypeId {
495 chalk_ir::AssocTypeId(self.as_intern_id()) 513 chalk_ir::AssocTypeId(self.0.as_intern_id())
514 }
515
516 fn from_chalk(_db: &dyn HirDatabase, assoc_type_id: AssocTypeId) -> TypeAliasAsAssocType {
517 TypeAliasAsAssocType(InternKey::from_intern_id(assoc_type_id.0))
518 }
519}
520
521pub struct TypeAliasAsForeignType(pub TypeAliasId);
522
523impl ToChalk for TypeAliasAsForeignType {
524 type Chalk = ForeignDefId;
525
526 fn to_chalk(self, _db: &dyn HirDatabase) -> ForeignDefId {
527 chalk_ir::ForeignDefId(self.0.as_intern_id())
496 } 528 }
497 529
498 fn from_chalk(_db: &dyn HirDatabase, type_alias_id: AssocTypeId) -> TypeAliasId { 530 fn from_chalk(_db: &dyn HirDatabase, foreign_def_id: ForeignDefId) -> TypeAliasAsForeignType {
499 InternKey::from_intern_id(type_alias_id.0) 531 TypeAliasAsForeignType(InternKey::from_intern_id(foreign_def_id.0))
500 } 532 }
501} 533}
502 534
@@ -580,7 +612,7 @@ impl ToChalk for ProjectionTy {
580 612
581 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy<Interner> { 613 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy<Interner> {
582 chalk_ir::ProjectionTy { 614 chalk_ir::ProjectionTy {
583 associated_ty_id: self.associated_ty.to_chalk(db), 615 associated_ty_id: TypeAliasAsAssocType(self.associated_ty).to_chalk(db),
584 substitution: self.parameters.to_chalk(db), 616 substitution: self.parameters.to_chalk(db),
585 } 617 }
586 } 618 }
@@ -590,7 +622,11 @@ impl ToChalk for ProjectionTy {
590 projection_ty: chalk_ir::ProjectionTy<Interner>, 622 projection_ty: chalk_ir::ProjectionTy<Interner>,
591 ) -> ProjectionTy { 623 ) -> ProjectionTy {
592 ProjectionTy { 624 ProjectionTy {
593 associated_ty: from_chalk(db, projection_ty.associated_ty_id), 625 associated_ty: from_chalk::<TypeAliasAsAssocType, _>(
626 db,
627 projection_ty.associated_ty_id,
628 )
629 .0,
594 parameters: from_chalk(db, projection_ty.substitution), 630 parameters: from_chalk(db, projection_ty.substitution),
595 } 631 }
596 } 632 }
@@ -789,7 +825,8 @@ pub(super) fn generic_predicate_to_inline_bound(
789 let alias_eq_bound = rust_ir::AliasEqBound { 825 let alias_eq_bound = rust_ir::AliasEqBound {
790 value: proj.ty.clone().to_chalk(db), 826 value: proj.ty.clone().to_chalk(db),
791 trait_bound: rust_ir::TraitBound { trait_id: trait_.to_chalk(db), args_no_self }, 827 trait_bound: rust_ir::TraitBound { trait_id: trait_.to_chalk(db), args_no_self },
792 associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db), 828 associated_ty_id: TypeAliasAsAssocType(proj.projection_ty.associated_ty)
829 .to_chalk(db),
793 parameters: Vec::new(), // FIXME we don't support generic associated types yet 830 parameters: Vec::new(), // FIXME we don't support generic associated types yet
794 }; 831 };
795 Some(rust_ir::InlineBound::AliasEqBound(alias_eq_bound)) 832 Some(rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
diff --git a/crates/hir_ty/src/traits/chalk/tls.rs b/crates/hir_ty/src/traits/chalk/tls.rs
index cb6b0fe81..b4568cff6 100644
--- a/crates/hir_ty/src/traits/chalk/tls.rs
+++ b/crates/hir_ty/src/traits/chalk/tls.rs
@@ -4,7 +4,7 @@ use std::fmt;
4use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication, TypeName}; 4use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication, TypeName};
5use itertools::Itertools; 5use itertools::Itertools;
6 6
7use super::{from_chalk, Interner}; 7use super::{from_chalk, Interner, TypeAliasAsAssocType};
8use crate::{db::HirDatabase, CallableDefId, TypeCtor}; 8use crate::{db::HirDatabase, CallableDefId, TypeCtor};
9use hir_def::{AdtId, AssocContainerId, DefWithBodyId, Lookup, TypeAliasId}; 9use hir_def::{AdtId, AssocContainerId, DefWithBodyId, Lookup, TypeAliasId};
10 10
@@ -77,6 +77,10 @@ impl DebugContext<'_> {
77 write!(f, "{{impl trait of async block {} of {:?}}}", idx.into_raw(), def)?; 77 write!(f, "{{impl trait of async block {} of {:?}}}", idx.into_raw(), def)?;
78 } 78 }
79 }, 79 },
80 TypeCtor::ForeignType(type_alias) => {
81 let name = self.0.type_alias_data(type_alias).name.clone();
82 write!(f, "{}", name)?;
83 }
80 TypeCtor::Closure { def, expr } => { 84 TypeCtor::Closure { def, expr } => {
81 write!(f, "{{closure {:?} in ", expr.into_raw())?; 85 write!(f, "{{closure {:?} in ", expr.into_raw())?;
82 match def { 86 match def {
@@ -119,7 +123,7 @@ impl DebugContext<'_> {
119 id: super::AssocTypeId, 123 id: super::AssocTypeId,
120 fmt: &mut fmt::Formatter<'_>, 124 fmt: &mut fmt::Formatter<'_>,
121 ) -> Result<(), fmt::Error> { 125 ) -> Result<(), fmt::Error> {
122 let type_alias: TypeAliasId = from_chalk(self.0, id); 126 let type_alias: TypeAliasId = from_chalk::<TypeAliasAsAssocType, _>(self.0, id).0;
123 let type_alias_data = self.0.type_alias_data(type_alias); 127 let type_alias_data = self.0.type_alias_data(type_alias);
124 let trait_ = match type_alias.lookup(self.0.upcast()).container { 128 let trait_ = match type_alias.lookup(self.0.upcast()).container {
125 AssocContainerId::TraitId(t) => t, 129 AssocContainerId::TraitId(t) => t,
@@ -153,7 +157,8 @@ impl DebugContext<'_> {
153 projection_ty: &chalk_ir::ProjectionTy<Interner>, 157 projection_ty: &chalk_ir::ProjectionTy<Interner>,
154 fmt: &mut fmt::Formatter<'_>, 158 fmt: &mut fmt::Formatter<'_>,
155 ) -> Result<(), fmt::Error> { 159 ) -> Result<(), fmt::Error> {
156 let type_alias: TypeAliasId = from_chalk(self.0, projection_ty.associated_ty_id); 160 let type_alias: TypeAliasId =
161 from_chalk::<TypeAliasAsAssocType, _>(self.0, projection_ty.associated_ty_id).0;
157 let type_alias_data = self.0.type_alias_data(type_alias); 162 let type_alias_data = self.0.type_alias_data(type_alias);
158 let trait_ = match type_alias.lookup(self.0.upcast()).container { 163 let trait_ = match type_alias.lookup(self.0.upcast()).container {
159 AssocContainerId::TraitId(t) => t, 164 AssocContainerId::TraitId(t) => t,
diff --git a/crates/ide/src/completion/complete_trait_impl.rs b/crates/ide/src/completion/complete_trait_impl.rs
index bff2603b8..ff115df92 100644
--- a/crates/ide/src/completion/complete_trait_impl.rs
+++ b/crates/ide/src/completion/complete_trait_impl.rs
@@ -86,36 +86,46 @@ fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, Synt
86 token = token.prev_token()?; 86 token = token.prev_token()?;
87 } 87 }
88 88
89 let (kind, trigger, impl_def_offset) = token.ancestors().find_map(|p| match p.kind() { 89 let impl_item_offset = match token.kind() {
90 // `const` can be a modifier of an item, so the `const` token may be inside another item syntax node. 90 // `impl .. { const <|> }`
91 // Eg. `impl .. { const <|> fn bar() .. }` 91 // ERROR 0
92 SyntaxKind::FN | SyntaxKind::TYPE_ALIAS | SyntaxKind::CONST 92 // CONST_KW <- *
93 if token.kind() == SyntaxKind::CONST_KW => 93 SyntaxKind::CONST_KW => 0,
94 { 94 // `impl .. { fn/type <|> }`
95 Some((ImplCompletionKind::Const, p, 2)) 95 // FN/TYPE_ALIAS 0
96 } 96 // FN_KW <- *
97 SyntaxKind::FN => Some((ImplCompletionKind::Fn, p, 2)), 97 SyntaxKind::FN_KW | SyntaxKind::TYPE_KW => 0,
98 SyntaxKind::TYPE_ALIAS => Some((ImplCompletionKind::TypeAlias, p, 2)), 98 // `impl .. { fn/type/const foo<|> }`
99 SyntaxKind::CONST => Some((ImplCompletionKind::Const, p, 2)), 99 // FN/TYPE_ALIAS/CONST 1
100 // `impl .. { const <|> }` is parsed as: 100 // NAME 0
101 // IMPL 101 // IDENT <- *
102 // ASSOC_ITEM_LIST 102 SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME => 1,
103 // ERROR 103 // `impl .. { foo<|> }`
104 // CONST_KW <- token 104 // MACRO_CALL 3
105 // WHITESPACE <- ctx.token 105 // PATH 2
106 SyntaxKind::ERROR 106 // PATH_SEGMENT 1
107 if p.first_token().map_or(false, |t| t.kind() == SyntaxKind::CONST_KW) => 107 // NAME_REF 0
108 { 108 // IDENT <- *
109 Some((ImplCompletionKind::Const, p, 2)) 109 SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME_REF => 3,
110 } 110 _ => return None,
111 SyntaxKind::NAME_REF => Some((ImplCompletionKind::All, p, 5)), 111 };
112 _ => None,
113 })?;
114 112
115 let impl_def = (0..impl_def_offset - 1) 113 let impl_item = token.ancestors().nth(impl_item_offset)?;
116 .try_fold(trigger.parent()?, |t, _| t.parent()) 114 // Must directly belong to an impl block.
117 .and_then(ast::Impl::cast)?; 115 // IMPL
118 Some((kind, trigger, impl_def)) 116 // ASSOC_ITEM_LIST
117 // <item>
118 let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?;
119 let kind = match impl_item.kind() {
120 // `impl ... { const <|> fn/type/const }`
121 _ if token.kind() == SyntaxKind::CONST_KW => ImplCompletionKind::Const,
122 SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const,
123 SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias,
124 SyntaxKind::FN => ImplCompletionKind::Fn,
125 SyntaxKind::MACRO_CALL => ImplCompletionKind::All,
126 _ => return None,
127 };
128 Some((kind, impl_item, impl_def))
119} 129}
120 130
121fn add_function_impl( 131fn add_function_impl(
@@ -261,19 +271,191 @@ ta type TestType = \n\
261 } 271 }
262 272
263 #[test] 273 #[test]
264 fn no_nested_fn_completions() { 274 fn no_completion_inside_fn() {
265 check( 275 check(
266 r" 276 r"
267trait Test { 277trait Test { fn test(); fn test2(); }
268 fn test(); 278struct T;
269 fn test2(); 279
280impl Test for T {
281 fn test() {
282 t<|>
283 }
284}
285",
286 expect![[""]],
287 );
288
289 check(
290 r"
291trait Test { fn test(); fn test2(); }
292struct T;
293
294impl Test for T {
295 fn test() {
296 fn t<|>
297 }
270} 298}
299",
300 expect![[""]],
301 );
302
303 check(
304 r"
305trait Test { fn test(); fn test2(); }
271struct T; 306struct T;
272 307
273impl Test for T { 308impl Test for T {
274 fn test() { 309 fn test() {
310 fn <|>
311 }
312}
313",
314 expect![[""]],
315 );
316
317 // https://github.com/rust-analyzer/rust-analyzer/pull/5976#issuecomment-692332191
318 check(
319 r"
320trait Test { fn test(); fn test2(); }
321struct T;
322
323impl Test for T {
324 fn test() {
325 foo.<|>
326 }
327}
328",
329 expect![[""]],
330 );
331
332 check(
333 r"
334trait Test { fn test(_: i32); fn test2(); }
335struct T;
336
337impl Test for T {
338 fn test(t<|>)
339}
340",
341 expect![[""]],
342 );
343
344 check(
345 r"
346trait Test { fn test(_: fn()); fn test2(); }
347struct T;
348
349impl Test for T {
350 fn test(f: fn <|>)
351}
352",
353 expect![[""]],
354 );
355 }
356
357 #[test]
358 fn no_completion_inside_const() {
359 check(
360 r"
361trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); }
362struct T;
363
364impl Test for T {
365 const TEST: fn <|>
366}
367",
368 expect![[""]],
369 );
370
371 check(
372 r"
373trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
374struct T;
375
376impl Test for T {
377 const TEST: T<|>
378}
379",
380 expect![[""]],
381 );
382
383 check(
384 r"
385trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
386struct T;
387
388impl Test for T {
389 const TEST: u32 = f<|>
390}
391",
392 expect![[""]],
393 );
394
395 check(
396 r"
397trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
398struct T;
399
400impl Test for T {
401 const TEST: u32 = {
275 t<|> 402 t<|>
403 };
404}
405",
406 expect![[""]],
407 );
408
409 check(
410 r"
411trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
412struct T;
413
414impl Test for T {
415 const TEST: u32 = {
416 fn <|>
417 };
418}
419",
420 expect![[""]],
421 );
422
423 check(
424 r"
425trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
426struct T;
427
428impl Test for T {
429 const TEST: u32 = {
430 fn t<|>
431 };
432}
433",
434 expect![[""]],
435 );
276 } 436 }
437
438 #[test]
439 fn no_completion_inside_type() {
440 check(
441 r"
442trait Test { type Test; type Test2; fn test(); }
443struct T;
444
445impl Test for T {
446 type Test = T<|>;
447}
448",
449 expect![[""]],
450 );
451
452 check(
453 r"
454trait Test { type Test; type Test2; fn test(); }
455struct T;
456
457impl Test for T {
458 type Test = fn <|>;
277} 459}
278", 460",
279 expect![[""]], 461 expect![[""]],
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index b2b972b02..dc815a483 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -622,13 +622,65 @@ pub struct Foo { pub a: i32, pub b: i32 }
622 r#" 622 r#"
623use a; 623use a;
624use a::{c, d::e}; 624use a::{c, d::e};
625
626mod a {
627 mod c {}
628 mod d {
629 mod e {}
630 }
631}
625"#, 632"#,
626 ); 633 );
627 check_fix(r#"use {<|>b};"#, r#"use b;"#); 634 check_fix(
628 check_fix(r#"use {b<|>};"#, r#"use b;"#); 635 r"
629 check_fix(r#"use a::{c<|>};"#, r#"use a::c;"#); 636 mod b {}
630 check_fix(r#"use a::{self<|>};"#, r#"use a;"#); 637 use {<|>b};
631 check_fix(r#"use a::{c, d::{e<|>}};"#, r#"use a::{c, d::e};"#); 638 ",
639 r"
640 mod b {}
641 use b;
642 ",
643 );
644 check_fix(
645 r"
646 mod b {}
647 use {b<|>};
648 ",
649 r"
650 mod b {}
651 use b;
652 ",
653 );
654 check_fix(
655 r"
656 mod a { mod c {} }
657 use a::{c<|>};
658 ",
659 r"
660 mod a { mod c {} }
661 use a::c;
662 ",
663 );
664 check_fix(
665 r"
666 mod a {}
667 use a::{self<|>};
668 ",
669 r"
670 mod a {}
671 use a;
672 ",
673 );
674 check_fix(
675 r"
676 mod a { mod c {} mod d { mod e {} } }
677 use a::{c, d::{e<|>}};
678 ",
679 r"
680 mod a { mod c {} mod d { mod e {} } }
681 use a::{c, d::e};
682 ",
683 );
632 } 684 }
633 685
634 #[test] 686 #[test]
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index 288c39e49..258f60e28 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -308,7 +308,13 @@ impl ProjectWorkspace {
308 .crates() 308 .crates()
309 .filter_map(|(crate_id, krate)| { 309 .filter_map(|(crate_id, krate)| {
310 let file_path = &krate.root_module; 310 let file_path = &krate.root_module;
311 let file_id = load(&file_path)?; 311 let file_id = match load(&file_path) {
312 Some(id) => id,
313 None => {
314 log::error!("failed to load crate root {}", file_path.display());
315 return None;
316 }
317 };
312 318
313 let env = krate.env.clone().into_iter().collect(); 319 let env = krate.env.clone().into_iter().collect();
314 let proc_macro = krate 320 let proc_macro = krate
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 8db0b0d72..0fbb9cb0d 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.81.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/config.rs b/crates/rust-analyzer/src/config.rs
index 1a74286f5..69d05aed5 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -288,7 +288,10 @@ impl Config {
288 let path = self.root_path.join(it); 288 let path = self.root_path.join(it);
289 match ProjectManifest::from_manifest_file(path) { 289 match ProjectManifest::from_manifest_file(path) {
290 Ok(it) => it.into(), 290 Ok(it) => it.into(),
291 Err(_) => continue, 291 Err(e) => {
292 log::error!("failed to load linked project: {}", e);
293 continue;
294 }
292 } 295 }
293 } 296 }
294 ManifestOrProjectJson::ProjectJson(it) => { 297 ManifestOrProjectJson::ProjectJson(it) => {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 64cb4d96c..c0943a54d 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -748,10 +748,15 @@ pub(crate) fn handle_formatting(
748 } 748 }
749 } 749 }
750 750
751 Ok(Some(vec![lsp_types::TextEdit { 751 if *file == captured_stdout {
752 range: Range::new(Position::new(0, 0), end_position), 752 // The document is already formatted correctly -- no edits needed.
753 new_text: captured_stdout, 753 Ok(None)
754 }])) 754 } else {
755 Ok(Some(vec![lsp_types::TextEdit {
756 range: Range::new(Position::new(0, 0), end_position),
757 new_text: captured_stdout,
758 }]))
759 }
755} 760}
756 761
757fn handle_fixes( 762fn handle_fixes(
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index a052f36a7..b070087a4 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -201,11 +201,14 @@ impl GlobalState {
201 let mut crate_graph = CrateGraph::default(); 201 let mut crate_graph = CrateGraph::default();
202 let vfs = &mut self.vfs.write().0; 202 let vfs = &mut self.vfs.write().0;
203 let loader = &mut self.loader; 203 let loader = &mut self.loader;
204 let mem_docs = &self.mem_docs;
204 let mut load = |path: &AbsPath| { 205 let mut load = |path: &AbsPath| {
205 let contents = loader.handle.load_sync(path); 206 let vfs_path = vfs::VfsPath::from(path.to_path_buf());
206 let path = vfs::VfsPath::from(path.to_path_buf()); 207 if !mem_docs.contains_key(&vfs_path) {
207 vfs.set_file_contents(path.clone(), contents); 208 let contents = loader.handle.load_sync(path);
208 vfs.file_id(&path) 209 vfs.set_file_contents(vfs_path.clone(), contents);
210 }
211 vfs.file_id(&vfs_path)
209 }; 212 };
210 for ws in workspaces.iter() { 213 for ws in workspaces.iter() {
211 crate_graph.extend(ws.to_crate_graph( 214 crate_graph.extend(ws.to_crate_graph(
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..45cf31f13 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -347,6 +347,7 @@ impl ast::UseTree {
347 self.clone() 347 self.clone()
348 } 348 }
349 349
350 /// 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] 351 #[must_use]
351 pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree { 352 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() { 353 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 25e8a359d..6868feed9 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -38,6 +38,10 @@ pub fn path_from_text(text: &str) -> ast::Path {
38 ast_from_text(text) 38 ast_from_text(text)
39} 39}
40 40
41pub fn glob_use_tree() -> ast::UseTree {
42 ast_from_text("use *;")
43}
44
41pub fn use_tree( 45pub fn use_tree(
42 path: ast::Path, 46 path: ast::Path,
43 use_tree_list: Option<ast::UseTreeList>, 47 use_tree_list: Option<ast::UseTreeList>,