diff options
Diffstat (limited to 'crates/hir_def/src')
-rw-r--r-- | crates/hir_def/src/body/lower.rs | 11 | ||||
-rw-r--r-- | crates/hir_def/src/data.rs | 2 | ||||
-rw-r--r-- | crates/hir_def/src/diagnostics.rs | 42 | ||||
-rw-r--r-- | crates/hir_def/src/expr.rs | 7 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree.rs | 9 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/lower.rs | 13 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/tests.rs | 8 | ||||
-rw-r--r-- | crates/hir_def/src/nameres.rs | 93 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 418 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests.rs | 1 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/diagnostics.rs | 131 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/mod_resolution.rs | 36 | ||||
-rw-r--r-- | crates/hir_def/src/path.rs | 4 | ||||
-rw-r--r-- | crates/hir_def/src/test_db.rs | 42 |
14 files changed, 571 insertions, 246 deletions
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 30ac12a12..2d91bb21f 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -239,7 +239,10 @@ impl ExprCollector<'_> { | |||
239 | None => self.missing_expr(), | 239 | None => self.missing_expr(), |
240 | }, | 240 | }, |
241 | // FIXME: we need to record these effects somewhere... | 241 | // FIXME: we need to record these effects somewhere... |
242 | ast::Effect::Async(_) => self.collect_block_opt(e.block_expr()), | 242 | ast::Effect::Async(_) => { |
243 | let body = self.collect_block_opt(e.block_expr()); | ||
244 | self.alloc_expr(Expr::Async { body }, syntax_ptr) | ||
245 | } | ||
243 | }, | 246 | }, |
244 | ast::Expr::BlockExpr(e) => self.collect_block(e), | 247 | ast::Expr::BlockExpr(e) => self.collect_block(e), |
245 | ast::Expr::LoopExpr(e) => { | 248 | ast::Expr::LoopExpr(e) => { |
@@ -835,8 +838,12 @@ impl ExprCollector<'_> { | |||
835 | 838 | ||
836 | Pat::Missing | 839 | Pat::Missing |
837 | } | 840 | } |
841 | ast::Pat::BoxPat(boxpat) => { | ||
842 | let inner = self.collect_pat_opt(boxpat.pat()); | ||
843 | Pat::Box { inner } | ||
844 | } | ||
838 | // FIXME: implement | 845 | // FIXME: implement |
839 | ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing, | 846 | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing, |
840 | }; | 847 | }; |
841 | let ptr = AstPtr::new(&pat); | 848 | let ptr = AstPtr::new(&pat); |
842 | self.alloc_pat(pattern, Either::Left(ptr)) | 849 | self.alloc_pat(pattern, Either::Left(ptr)) |
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)] | ||
33 | pub struct UnresolvedExternCrate { | ||
34 | pub file: HirFileId, | ||
35 | pub item: AstPtr<ast::ExternCrate>, | ||
36 | } | ||
37 | |||
38 | impl 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)] | ||
54 | pub struct UnresolvedImport { | ||
55 | pub file: HirFileId, | ||
56 | pub node: AstPtr<ast::UseTree>, | ||
57 | } | ||
58 | |||
59 | impl 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/expr.rs b/crates/hir_def/src/expr.rs index c94b3a36f..e5d740a36 100644 --- a/crates/hir_def/src/expr.rs +++ b/crates/hir_def/src/expr.rs | |||
@@ -111,6 +111,9 @@ pub enum Expr { | |||
111 | TryBlock { | 111 | TryBlock { |
112 | body: ExprId, | 112 | body: ExprId, |
113 | }, | 113 | }, |
114 | Async { | ||
115 | body: ExprId, | ||
116 | }, | ||
114 | Cast { | 117 | Cast { |
115 | expr: ExprId, | 118 | expr: ExprId, |
116 | type_ref: TypeRef, | 119 | type_ref: TypeRef, |
@@ -250,7 +253,7 @@ impl Expr { | |||
250 | f(*expr); | 253 | f(*expr); |
251 | } | 254 | } |
252 | } | 255 | } |
253 | Expr::TryBlock { body } | Expr::Unsafe { body } => f(*body), | 256 | Expr::TryBlock { body } | Expr::Unsafe { body } | Expr::Async { body } => f(*body), |
254 | Expr::Loop { body, .. } => f(*body), | 257 | Expr::Loop { body, .. } => f(*body), |
255 | Expr::While { condition, body, .. } => { | 258 | Expr::While { condition, body, .. } => { |
256 | f(*condition); | 259 | f(*condition); |
@@ -395,6 +398,7 @@ pub enum Pat { | |||
395 | Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> }, | 398 | Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> }, |
396 | TupleStruct { path: Option<Path>, args: Vec<PatId>, ellipsis: Option<usize> }, | 399 | TupleStruct { path: Option<Path>, args: Vec<PatId>, ellipsis: Option<usize> }, |
397 | Ref { pat: PatId, mutability: Mutability }, | 400 | Ref { pat: PatId, mutability: Mutability }, |
401 | Box { inner: PatId }, | ||
398 | } | 402 | } |
399 | 403 | ||
400 | impl Pat { | 404 | impl Pat { |
@@ -415,6 +419,7 @@ impl Pat { | |||
415 | Pat::Record { args, .. } => { | 419 | Pat::Record { args, .. } => { |
416 | args.iter().map(|f| f.pat).for_each(f); | 420 | args.iter().map(|f| f.pat).for_each(f); |
417 | } | 421 | } |
422 | Pat::Box { inner } => f(*inner), | ||
418 | } | 423 | } |
419 | } | 424 | } |
420 | } | 425 | } |
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 | ||
297 | macro_rules! from_attrs { | 296 | macro_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)] |
489 | pub struct ExternCrate { | 493 | pub 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 | ||
289 | mod diagnostics { | 289 | mod 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 | ||
6 | use std::iter; | ||
7 | |||
6 | use base_db::{CrateId, FileId, ProcMacroId}; | 8 | use base_db::{CrateId, FileId, ProcMacroId}; |
7 | use cfg::CfgOptions; | 9 | use cfg::CfgOptions; |
10 | use hir_expand::InFile; | ||
8 | use hir_expand::{ | 11 | use 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 | }; |
16 | use rustc_hash::FxHashMap; | 19 | use rustc_hash::FxHashMap; |
20 | use rustc_hash::FxHashSet; | ||
17 | use syntax::ast; | 21 | use syntax::ast; |
18 | use test_utils::mark; | 22 | use 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)] |
117 | enum ImportSource { | ||
118 | Import(ItemTreeId<item_tree::Import>), | ||
119 | ExternCrate(ItemTreeId<item_tree::ExternCrate>), | ||
120 | } | ||
121 | |||
122 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
115 | struct Import { | 123 | struct 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 | ||
125 | impl Import { | 134 | impl 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; | |||
2 | mod incremental; | 2 | mod incremental; |
3 | mod macros; | 3 | mod macros; |
4 | mod mod_resolution; | 4 | mod mod_resolution; |
5 | mod diagnostics; | ||
5 | mod primitives; | 6 | mod primitives; |
6 | 7 | ||
7 | use std::sync::Arc; | 8 | use 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 @@ | |||
1 | use base_db::fixture::WithFixture; | ||
2 | use base_db::FileId; | ||
3 | use base_db::SourceDatabaseExt; | ||
4 | use hir_expand::db::AstDatabase; | ||
5 | use rustc_hash::FxHashMap; | ||
6 | use syntax::TextRange; | ||
7 | use syntax::TextSize; | ||
8 | |||
9 | use crate::test_db::TestDB; | ||
10 | |||
11 | fn 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] | ||
43 | fn 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] | ||
56 | fn 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] | ||
83 | fn 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] | ||
96 | fn 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] | ||
120 | fn 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] |
675 | fn 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] | ||
711 | fn module_resolution_decl_inside_module_in_non_crate_root_2() { | 675 | fn 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 | ||
8 | use base_db::SourceDatabase; | ||
8 | use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; | 9 | use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; |
9 | use hir_expand::db::AstDatabase; | 10 | use hir_expand::db::AstDatabase; |
11 | use hir_expand::diagnostics::Diagnostic; | ||
12 | use hir_expand::diagnostics::DiagnosticSinkBuilder; | ||
13 | use rustc_hash::FxHashMap; | ||
10 | use rustc_hash::FxHashSet; | 14 | use rustc_hash::FxHashSet; |
15 | use syntax::TextRange; | ||
16 | use test_utils::extract_annotations; | ||
11 | 17 | ||
12 | use crate::db::DefDatabase; | 18 | use 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 | } |