diff options
41 files changed, 1209 insertions, 827 deletions
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 414c3f35e..22ec7c6ac 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs | |||
@@ -3,13 +3,227 @@ | |||
3 | //! | 3 | //! |
4 | //! This probably isn't the best way to do this -- ideally, diagnistics should | 4 | //! This probably isn't the best way to do this -- ideally, diagnistics should |
5 | //! be expressed in terms of hir types themselves. | 5 | //! be expressed in terms of hir types themselves. |
6 | pub use hir_def::diagnostics::{ | 6 | use std::any::Any; |
7 | InactiveCode, UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro, | 7 | |
8 | }; | 8 | use cfg::{CfgExpr, CfgOptions, DnfExpr}; |
9 | pub use hir_expand::diagnostics::{ | 9 | use hir_def::path::ModPath; |
10 | Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, | 10 | use hir_expand::{HirFileId, InFile}; |
11 | }; | 11 | use stdx::format_to; |
12 | pub use hir_ty::diagnostics::{ | 12 | use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; |
13 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, | 13 | |
14 | NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, | 14 | pub use hir_ty::{ |
15 | diagnostics::{ | ||
16 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, | ||
17 | MissingOkOrSomeInTailExpr, NoSuchField, RemoveThisSemicolon, | ||
18 | ReplaceFilterMapNextWithFindMap, | ||
19 | }, | ||
20 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder}, | ||
15 | }; | 21 | }; |
22 | |||
23 | // Diagnostic: unresolved-module | ||
24 | // | ||
25 | // This diagnostic is triggered if rust-analyzer is unable to discover referred module. | ||
26 | #[derive(Debug)] | ||
27 | pub struct UnresolvedModule { | ||
28 | pub file: HirFileId, | ||
29 | pub decl: AstPtr<ast::Module>, | ||
30 | pub candidate: String, | ||
31 | } | ||
32 | |||
33 | impl Diagnostic for UnresolvedModule { | ||
34 | fn code(&self) -> DiagnosticCode { | ||
35 | DiagnosticCode("unresolved-module") | ||
36 | } | ||
37 | fn message(&self) -> String { | ||
38 | "unresolved module".to_string() | ||
39 | } | ||
40 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
41 | InFile::new(self.file, self.decl.clone().into()) | ||
42 | } | ||
43 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
44 | self | ||
45 | } | ||
46 | } | ||
47 | |||
48 | // Diagnostic: unresolved-extern-crate | ||
49 | // | ||
50 | // This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate. | ||
51 | #[derive(Debug)] | ||
52 | pub struct UnresolvedExternCrate { | ||
53 | pub file: HirFileId, | ||
54 | pub item: AstPtr<ast::ExternCrate>, | ||
55 | } | ||
56 | |||
57 | impl Diagnostic for UnresolvedExternCrate { | ||
58 | fn code(&self) -> DiagnosticCode { | ||
59 | DiagnosticCode("unresolved-extern-crate") | ||
60 | } | ||
61 | fn message(&self) -> String { | ||
62 | "unresolved extern crate".to_string() | ||
63 | } | ||
64 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
65 | InFile::new(self.file, self.item.clone().into()) | ||
66 | } | ||
67 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
68 | self | ||
69 | } | ||
70 | } | ||
71 | |||
72 | #[derive(Debug)] | ||
73 | pub struct UnresolvedImport { | ||
74 | pub file: HirFileId, | ||
75 | pub node: AstPtr<ast::UseTree>, | ||
76 | } | ||
77 | |||
78 | impl Diagnostic for UnresolvedImport { | ||
79 | fn code(&self) -> DiagnosticCode { | ||
80 | DiagnosticCode("unresolved-import") | ||
81 | } | ||
82 | fn message(&self) -> String { | ||
83 | "unresolved import".to_string() | ||
84 | } | ||
85 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
86 | InFile::new(self.file, self.node.clone().into()) | ||
87 | } | ||
88 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
89 | self | ||
90 | } | ||
91 | fn is_experimental(&self) -> bool { | ||
92 | // This currently results in false positives in the following cases: | ||
93 | // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly) | ||
94 | // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly) | ||
95 | // - proc macros and/or proc macro generated code | ||
96 | true | ||
97 | } | ||
98 | } | ||
99 | |||
100 | // Diagnostic: unresolved-macro-call | ||
101 | // | ||
102 | // This diagnostic is triggered if rust-analyzer is unable to resolve the path to a | ||
103 | // macro in a macro invocation. | ||
104 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
105 | pub struct UnresolvedMacroCall { | ||
106 | pub file: HirFileId, | ||
107 | pub node: AstPtr<ast::MacroCall>, | ||
108 | pub path: ModPath, | ||
109 | } | ||
110 | |||
111 | impl Diagnostic for UnresolvedMacroCall { | ||
112 | fn code(&self) -> DiagnosticCode { | ||
113 | DiagnosticCode("unresolved-macro-call") | ||
114 | } | ||
115 | fn message(&self) -> String { | ||
116 | format!("unresolved macro `{}!`", self.path) | ||
117 | } | ||
118 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
119 | InFile::new(self.file, self.node.clone().into()) | ||
120 | } | ||
121 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
122 | self | ||
123 | } | ||
124 | fn is_experimental(&self) -> bool { | ||
125 | true | ||
126 | } | ||
127 | } | ||
128 | |||
129 | // Diagnostic: inactive-code | ||
130 | // | ||
131 | // This diagnostic is shown for code with inactive `#[cfg]` attributes. | ||
132 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
133 | pub struct InactiveCode { | ||
134 | pub file: HirFileId, | ||
135 | pub node: SyntaxNodePtr, | ||
136 | pub cfg: CfgExpr, | ||
137 | pub opts: CfgOptions, | ||
138 | } | ||
139 | |||
140 | impl Diagnostic for InactiveCode { | ||
141 | fn code(&self) -> DiagnosticCode { | ||
142 | DiagnosticCode("inactive-code") | ||
143 | } | ||
144 | fn message(&self) -> String { | ||
145 | let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts); | ||
146 | let mut buf = "code is inactive due to #[cfg] directives".to_string(); | ||
147 | |||
148 | if let Some(inactive) = inactive { | ||
149 | format_to!(buf, ": {}", inactive); | ||
150 | } | ||
151 | |||
152 | buf | ||
153 | } | ||
154 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
155 | InFile::new(self.file, self.node.clone()) | ||
156 | } | ||
157 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
158 | self | ||
159 | } | ||
160 | } | ||
161 | |||
162 | // Diagnostic: unresolved-proc-macro | ||
163 | // | ||
164 | // This diagnostic is shown when a procedural macro can not be found. This usually means that | ||
165 | // procedural macro support is simply disabled (and hence is only a weak hint instead of an error), | ||
166 | // but can also indicate project setup problems. | ||
167 | // | ||
168 | // If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the | ||
169 | // `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can | ||
170 | // enable support for procedural macros (see `rust-analyzer.procMacro.enable`). | ||
171 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
172 | pub struct UnresolvedProcMacro { | ||
173 | pub file: HirFileId, | ||
174 | pub node: SyntaxNodePtr, | ||
175 | /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange` | ||
176 | /// to use instead. | ||
177 | pub precise_location: Option<TextRange>, | ||
178 | pub macro_name: Option<String>, | ||
179 | } | ||
180 | |||
181 | impl Diagnostic for UnresolvedProcMacro { | ||
182 | fn code(&self) -> DiagnosticCode { | ||
183 | DiagnosticCode("unresolved-proc-macro") | ||
184 | } | ||
185 | |||
186 | fn message(&self) -> String { | ||
187 | match &self.macro_name { | ||
188 | Some(name) => format!("proc macro `{}` not expanded", name), | ||
189 | None => "proc macro not expanded".to_string(), | ||
190 | } | ||
191 | } | ||
192 | |||
193 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
194 | InFile::new(self.file, self.node.clone()) | ||
195 | } | ||
196 | |||
197 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
198 | self | ||
199 | } | ||
200 | } | ||
201 | |||
202 | // Diagnostic: macro-error | ||
203 | // | ||
204 | // This diagnostic is shown for macro expansion errors. | ||
205 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
206 | pub struct MacroError { | ||
207 | pub file: HirFileId, | ||
208 | pub node: SyntaxNodePtr, | ||
209 | pub message: String, | ||
210 | } | ||
211 | |||
212 | impl Diagnostic for MacroError { | ||
213 | fn code(&self) -> DiagnosticCode { | ||
214 | DiagnosticCode("macro-error") | ||
215 | } | ||
216 | fn message(&self) -> String { | ||
217 | self.message.clone() | ||
218 | } | ||
219 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
220 | InFile::new(self.file, self.node.clone()) | ||
221 | } | ||
222 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
223 | self | ||
224 | } | ||
225 | fn is_experimental(&self) -> bool { | ||
226 | // Newly added and not very well-tested, might contain false positives. | ||
227 | true | ||
228 | } | ||
229 | } | ||
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index cdf65a044..1ecd2391b 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -35,12 +35,18 @@ use std::{iter, sync::Arc}; | |||
35 | 35 | ||
36 | use arrayvec::ArrayVec; | 36 | use arrayvec::ArrayVec; |
37 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; | 37 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; |
38 | use diagnostics::{ | ||
39 | InactiveCode, MacroError, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, | ||
40 | UnresolvedModule, UnresolvedProcMacro, | ||
41 | }; | ||
38 | use either::Either; | 42 | use either::Either; |
39 | use hir_def::{ | 43 | use hir_def::{ |
40 | adt::{ReprKind, VariantData}, | 44 | adt::{ReprKind, VariantData}, |
45 | body::BodyDiagnostic, | ||
41 | expr::{BindingAnnotation, LabelId, Pat, PatId}, | 46 | expr::{BindingAnnotation, LabelId, Pat, PatId}, |
42 | item_tree::ItemTreeNode, | 47 | item_tree::ItemTreeNode, |
43 | lang_item::LangItemTarget, | 48 | lang_item::LangItemTarget, |
49 | nameres, | ||
44 | per_ns::PerNs, | 50 | per_ns::PerNs, |
45 | resolver::{HasResolver, Resolver}, | 51 | resolver::{HasResolver, Resolver}, |
46 | src::HasSource as _, | 52 | src::HasSource as _, |
@@ -50,11 +56,12 @@ use hir_def::{ | |||
50 | LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, | 56 | LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, |
51 | TypeParamId, UnionId, | 57 | TypeParamId, UnionId, |
52 | }; | 58 | }; |
53 | use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; | 59 | use hir_expand::{name::name, MacroCallKind, MacroDefKind}; |
54 | use hir_ty::{ | 60 | use hir_ty::{ |
55 | autoderef, | 61 | autoderef, |
56 | consteval::ConstExt, | 62 | consteval::ConstExt, |
57 | could_unify, | 63 | could_unify, |
64 | diagnostics_sink::DiagnosticSink, | ||
58 | method_resolution::{self, def_crates, TyFingerprint}, | 65 | method_resolution::{self, def_crates, TyFingerprint}, |
59 | primitive::UintTy, | 66 | primitive::UintTy, |
60 | subst_prefix, | 67 | subst_prefix, |
@@ -65,11 +72,12 @@ use hir_ty::{ | |||
65 | WhereClause, | 72 | WhereClause, |
66 | }; | 73 | }; |
67 | use itertools::Itertools; | 74 | use itertools::Itertools; |
75 | use nameres::diagnostics::DefDiagnosticKind; | ||
68 | use rustc_hash::FxHashSet; | 76 | use rustc_hash::FxHashSet; |
69 | use stdx::{format_to, impl_from}; | 77 | use stdx::{format_to, impl_from}; |
70 | use syntax::{ | 78 | use syntax::{ |
71 | ast::{self, AttrsOwner, NameOwner}, | 79 | ast::{self, AttrsOwner, NameOwner}, |
72 | AstNode, SmolStr, | 80 | AstNode, AstPtr, SmolStr, SyntaxKind, SyntaxNodePtr, |
73 | }; | 81 | }; |
74 | use tt::{Ident, Leaf, Literal, TokenTree}; | 82 | use tt::{Ident, Leaf, Literal, TokenTree}; |
75 | 83 | ||
@@ -442,7 +450,137 @@ impl Module { | |||
442 | format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) | 450 | format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) |
443 | }); | 451 | }); |
444 | let def_map = self.id.def_map(db.upcast()); | 452 | let def_map = self.id.def_map(db.upcast()); |
445 | def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); | 453 | for diag in def_map.diagnostics() { |
454 | if diag.in_module != self.id.local_id { | ||
455 | // FIXME: This is accidentally quadratic. | ||
456 | continue; | ||
457 | } | ||
458 | match &diag.kind { | ||
459 | DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate } => { | ||
460 | let decl = declaration.to_node(db.upcast()); | ||
461 | sink.push(UnresolvedModule { | ||
462 | file: declaration.file_id, | ||
463 | decl: AstPtr::new(&decl), | ||
464 | candidate: candidate.clone(), | ||
465 | }) | ||
466 | } | ||
467 | DefDiagnosticKind::UnresolvedExternCrate { ast } => { | ||
468 | let item = ast.to_node(db.upcast()); | ||
469 | sink.push(UnresolvedExternCrate { | ||
470 | file: ast.file_id, | ||
471 | item: AstPtr::new(&item), | ||
472 | }); | ||
473 | } | ||
474 | |||
475 | DefDiagnosticKind::UnresolvedImport { ast, index } => { | ||
476 | let use_item = ast.to_node(db.upcast()); | ||
477 | let hygiene = Hygiene::new(db.upcast(), ast.file_id); | ||
478 | let mut cur = 0; | ||
479 | let mut tree = None; | ||
480 | ModPath::expand_use_item( | ||
481 | db.upcast(), | ||
482 | InFile::new(ast.file_id, use_item), | ||
483 | &hygiene, | ||
484 | |_mod_path, use_tree, _is_glob, _alias| { | ||
485 | if cur == *index { | ||
486 | tree = Some(use_tree.clone()); | ||
487 | } | ||
488 | |||
489 | cur += 1; | ||
490 | }, | ||
491 | ); | ||
492 | |||
493 | if let Some(tree) = tree { | ||
494 | sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) }); | ||
495 | } | ||
496 | } | ||
497 | |||
498 | DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { | ||
499 | let item = ast.to_node(db.upcast()); | ||
500 | sink.push(InactiveCode { | ||
501 | file: ast.file_id, | ||
502 | node: AstPtr::new(&item).into(), | ||
503 | cfg: cfg.clone(), | ||
504 | opts: opts.clone(), | ||
505 | }); | ||
506 | } | ||
507 | |||
508 | DefDiagnosticKind::UnresolvedProcMacro { ast } => { | ||
509 | let mut precise_location = None; | ||
510 | let (file, ast, name) = match ast { | ||
511 | MacroCallKind::FnLike { ast_id, .. } => { | ||
512 | let node = ast_id.to_node(db.upcast()); | ||
513 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) | ||
514 | } | ||
515 | MacroCallKind::Derive { ast_id, derive_name, .. } => { | ||
516 | let node = ast_id.to_node(db.upcast()); | ||
517 | |||
518 | // Compute the precise location of the macro name's token in the derive | ||
519 | // list. | ||
520 | // FIXME: This does not handle paths to the macro, but neither does the | ||
521 | // rest of r-a. | ||
522 | let derive_attrs = | ||
523 | node.attrs().filter_map(|attr| match attr.as_simple_call() { | ||
524 | Some((name, args)) if name == "derive" => Some(args), | ||
525 | _ => None, | ||
526 | }); | ||
527 | 'outer: for attr in derive_attrs { | ||
528 | let tokens = | ||
529 | attr.syntax().children_with_tokens().filter_map(|elem| { | ||
530 | match elem { | ||
531 | syntax::NodeOrToken::Node(_) => None, | ||
532 | syntax::NodeOrToken::Token(tok) => Some(tok), | ||
533 | } | ||
534 | }); | ||
535 | for token in tokens { | ||
536 | if token.kind() == SyntaxKind::IDENT | ||
537 | && token.text() == derive_name.as_str() | ||
538 | { | ||
539 | precise_location = Some(token.text_range()); | ||
540 | break 'outer; | ||
541 | } | ||
542 | } | ||
543 | } | ||
544 | |||
545 | ( | ||
546 | ast_id.file_id, | ||
547 | SyntaxNodePtr::from(AstPtr::new(&node)), | ||
548 | Some(derive_name.clone()), | ||
549 | ) | ||
550 | } | ||
551 | }; | ||
552 | sink.push(UnresolvedProcMacro { | ||
553 | file, | ||
554 | node: ast, | ||
555 | precise_location, | ||
556 | macro_name: name, | ||
557 | }); | ||
558 | } | ||
559 | |||
560 | DefDiagnosticKind::UnresolvedMacroCall { ast, path } => { | ||
561 | let node = ast.to_node(db.upcast()); | ||
562 | sink.push(UnresolvedMacroCall { | ||
563 | file: ast.file_id, | ||
564 | node: AstPtr::new(&node), | ||
565 | path: path.clone(), | ||
566 | }); | ||
567 | } | ||
568 | |||
569 | DefDiagnosticKind::MacroError { ast, message } => { | ||
570 | let (file, ast) = match ast { | ||
571 | MacroCallKind::FnLike { ast_id, .. } => { | ||
572 | let node = ast_id.to_node(db.upcast()); | ||
573 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | ||
574 | } | ||
575 | MacroCallKind::Derive { ast_id, .. } => { | ||
576 | let node = ast_id.to_node(db.upcast()); | ||
577 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | ||
578 | } | ||
579 | }; | ||
580 | sink.push(MacroError { file, node: ast, message: message.clone() }); | ||
581 | } | ||
582 | } | ||
583 | } | ||
446 | for decl in self.declarations(db) { | 584 | for decl in self.declarations(db) { |
447 | match decl { | 585 | match decl { |
448 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | 586 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), |
@@ -865,7 +1003,37 @@ impl Function { | |||
865 | 1003 | ||
866 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | 1004 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { |
867 | let krate = self.module(db).id.krate(); | 1005 | let krate = self.module(db).id.krate(); |
868 | hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink); | 1006 | |
1007 | let source_map = db.body_with_source_map(self.id.into()).1; | ||
1008 | for diag in source_map.diagnostics() { | ||
1009 | match diag { | ||
1010 | BodyDiagnostic::InactiveCode { node, cfg, opts } => sink.push(InactiveCode { | ||
1011 | file: node.file_id, | ||
1012 | node: node.value.clone(), | ||
1013 | cfg: cfg.clone(), | ||
1014 | opts: opts.clone(), | ||
1015 | }), | ||
1016 | BodyDiagnostic::MacroError { node, message } => sink.push(MacroError { | ||
1017 | file: node.file_id, | ||
1018 | node: node.value.clone().into(), | ||
1019 | message: message.to_string(), | ||
1020 | }), | ||
1021 | BodyDiagnostic::UnresolvedProcMacro { node } => sink.push(UnresolvedProcMacro { | ||
1022 | file: node.file_id, | ||
1023 | node: node.value.clone().into(), | ||
1024 | precise_location: None, | ||
1025 | macro_name: None, | ||
1026 | }), | ||
1027 | BodyDiagnostic::UnresolvedMacroCall { node, path } => { | ||
1028 | sink.push(UnresolvedMacroCall { | ||
1029 | file: node.file_id, | ||
1030 | node: node.value.clone(), | ||
1031 | path: path.clone(), | ||
1032 | }) | ||
1033 | } | ||
1034 | } | ||
1035 | } | ||
1036 | |||
869 | hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); | 1037 | hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); |
870 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink); | 1038 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink); |
871 | } | 1039 | } |
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index 98b485b60..c521879c8 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | //! Defines `Body`: a lowered representation of bodies of functions, statics and | 1 | //! Defines `Body`: a lowered representation of bodies of functions, statics and |
2 | //! consts. | 2 | //! consts. |
3 | mod lower; | 3 | mod lower; |
4 | mod diagnostics; | ||
5 | #[cfg(test)] | 4 | #[cfg(test)] |
6 | mod tests; | 5 | mod tests; |
7 | pub mod scope; | 6 | pub mod scope; |
@@ -9,17 +8,16 @@ pub mod scope; | |||
9 | use std::{mem, ops::Index, sync::Arc}; | 8 | use std::{mem, ops::Index, sync::Arc}; |
10 | 9 | ||
11 | use base_db::CrateId; | 10 | use base_db::CrateId; |
12 | use cfg::CfgOptions; | 11 | use cfg::{CfgExpr, CfgOptions}; |
13 | use drop_bomb::DropBomb; | 12 | use drop_bomb::DropBomb; |
14 | use either::Either; | 13 | use either::Either; |
15 | use hir_expand::{ | 14 | use hir_expand::{ |
16 | ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, ExpandResult, | 15 | ast_id_map::AstIdMap, hygiene::Hygiene, AstId, ExpandResult, HirFileId, InFile, MacroDefId, |
17 | HirFileId, InFile, MacroDefId, | ||
18 | }; | 16 | }; |
19 | use la_arena::{Arena, ArenaMap}; | 17 | use la_arena::{Arena, ArenaMap}; |
20 | use profile::Count; | 18 | use profile::Count; |
21 | use rustc_hash::FxHashMap; | 19 | use rustc_hash::FxHashMap; |
22 | use syntax::{ast, AstNode, AstPtr}; | 20 | use syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; |
23 | 21 | ||
24 | use crate::{ | 22 | use crate::{ |
25 | attr::{Attrs, RawAttrs}, | 23 | attr::{Attrs, RawAttrs}, |
@@ -273,12 +271,20 @@ pub struct BodySourceMap { | |||
273 | 271 | ||
274 | /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in | 272 | /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in |
275 | /// the source map (since they're just as volatile). | 273 | /// the source map (since they're just as volatile). |
276 | diagnostics: Vec<diagnostics::BodyDiagnostic>, | 274 | diagnostics: Vec<BodyDiagnostic>, |
277 | } | 275 | } |
278 | 276 | ||
279 | #[derive(Default, Debug, Eq, PartialEq, Clone, Copy)] | 277 | #[derive(Default, Debug, Eq, PartialEq, Clone, Copy)] |
280 | pub struct SyntheticSyntax; | 278 | pub struct SyntheticSyntax; |
281 | 279 | ||
280 | #[derive(Debug, Eq, PartialEq)] | ||
281 | pub enum BodyDiagnostic { | ||
282 | InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions }, | ||
283 | MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String }, | ||
284 | UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>> }, | ||
285 | UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath }, | ||
286 | } | ||
287 | |||
282 | impl Body { | 288 | impl Body { |
283 | pub(crate) fn body_with_source_map_query( | 289 | pub(crate) fn body_with_source_map_query( |
284 | db: &dyn DefDatabase, | 290 | db: &dyn DefDatabase, |
@@ -416,9 +422,8 @@ impl BodySourceMap { | |||
416 | self.field_map.get(&src).cloned() | 422 | self.field_map.get(&src).cloned() |
417 | } | 423 | } |
418 | 424 | ||
419 | pub(crate) fn add_diagnostics(&self, _db: &dyn DefDatabase, sink: &mut DiagnosticSink<'_>) { | 425 | /// Get a reference to the body source map's diagnostics. |
420 | for diag in &self.diagnostics { | 426 | pub fn diagnostics(&self) -> &[BodyDiagnostic] { |
421 | diag.add_to(sink); | 427 | &self.diagnostics |
422 | } | ||
423 | } | 428 | } |
424 | } | 429 | } |
diff --git a/crates/hir_def/src/body/diagnostics.rs b/crates/hir_def/src/body/diagnostics.rs deleted file mode 100644 index f6992c9a8..000000000 --- a/crates/hir_def/src/body/diagnostics.rs +++ /dev/null | |||
@@ -1,32 +0,0 @@ | |||
1 | //! Diagnostics emitted during body lowering. | ||
2 | |||
3 | use hir_expand::diagnostics::DiagnosticSink; | ||
4 | |||
5 | use crate::diagnostics::{InactiveCode, MacroError, UnresolvedMacroCall, UnresolvedProcMacro}; | ||
6 | |||
7 | #[derive(Debug, Eq, PartialEq)] | ||
8 | pub(crate) enum BodyDiagnostic { | ||
9 | InactiveCode(InactiveCode), | ||
10 | MacroError(MacroError), | ||
11 | UnresolvedProcMacro(UnresolvedProcMacro), | ||
12 | UnresolvedMacroCall(UnresolvedMacroCall), | ||
13 | } | ||
14 | |||
15 | impl BodyDiagnostic { | ||
16 | pub(crate) fn add_to(&self, sink: &mut DiagnosticSink<'_>) { | ||
17 | match self { | ||
18 | BodyDiagnostic::InactiveCode(diag) => { | ||
19 | sink.push(diag.clone()); | ||
20 | } | ||
21 | BodyDiagnostic::MacroError(diag) => { | ||
22 | sink.push(diag.clone()); | ||
23 | } | ||
24 | BodyDiagnostic::UnresolvedProcMacro(diag) => { | ||
25 | sink.push(diag.clone()); | ||
26 | } | ||
27 | BodyDiagnostic::UnresolvedMacroCall(diag) => { | ||
28 | sink.push(diag.clone()); | ||
29 | } | ||
30 | } | ||
31 | } | ||
32 | } | ||
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 2a7e0205f..da1fdac33 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -8,7 +8,7 @@ use hir_expand::{ | |||
8 | ast_id_map::{AstIdMap, FileAstId}, | 8 | ast_id_map::{AstIdMap, FileAstId}, |
9 | hygiene::Hygiene, | 9 | hygiene::Hygiene, |
10 | name::{name, AsName, Name}, | 10 | name::{name, AsName, Name}, |
11 | ExpandError, HirFileId, | 11 | ExpandError, HirFileId, InFile, |
12 | }; | 12 | }; |
13 | use la_arena::Arena; | 13 | use la_arena::Arena; |
14 | use profile::Count; | 14 | use profile::Count; |
@@ -23,9 +23,9 @@ use syntax::{ | |||
23 | use crate::{ | 23 | use crate::{ |
24 | adt::StructKind, | 24 | adt::StructKind, |
25 | body::{Body, BodySourceMap, Expander, LabelSource, PatPtr, SyntheticSyntax}, | 25 | body::{Body, BodySourceMap, Expander, LabelSource, PatPtr, SyntheticSyntax}, |
26 | body::{BodyDiagnostic, ExprSource, PatSource}, | ||
26 | builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, | 27 | builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, |
27 | db::DefDatabase, | 28 | db::DefDatabase, |
28 | diagnostics::{InactiveCode, MacroError, UnresolvedMacroCall, UnresolvedProcMacro}, | ||
29 | expr::{ | 29 | expr::{ |
30 | dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label, | 30 | dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label, |
31 | LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, | 31 | LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, |
@@ -38,8 +38,6 @@ use crate::{ | |||
38 | AdtId, BlockLoc, ModuleDefId, UnresolvedMacro, | 38 | AdtId, BlockLoc, ModuleDefId, UnresolvedMacro, |
39 | }; | 39 | }; |
40 | 40 | ||
41 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; | ||
42 | |||
43 | pub struct LowerCtx<'a> { | 41 | pub struct LowerCtx<'a> { |
44 | pub db: &'a dyn DefDatabase, | 42 | pub db: &'a dyn DefDatabase, |
45 | hygiene: Hygiene, | 43 | hygiene: Hygiene, |
@@ -592,13 +590,10 @@ impl ExprCollector<'_> { | |||
592 | let res = match res { | 590 | let res = match res { |
593 | Ok(res) => res, | 591 | Ok(res) => res, |
594 | Err(UnresolvedMacro { path }) => { | 592 | Err(UnresolvedMacro { path }) => { |
595 | self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall( | 593 | self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall { |
596 | UnresolvedMacroCall { | 594 | node: InFile::new(outer_file, syntax_ptr), |
597 | file: outer_file, | 595 | path, |
598 | node: syntax_ptr.cast().unwrap(), | 596 | }); |
599 | path, | ||
600 | }, | ||
601 | )); | ||
602 | collector(self, None); | 597 | collector(self, None); |
603 | return; | 598 | return; |
604 | } | 599 | } |
@@ -606,21 +601,15 @@ impl ExprCollector<'_> { | |||
606 | 601 | ||
607 | match &res.err { | 602 | match &res.err { |
608 | Some(ExpandError::UnresolvedProcMacro) => { | 603 | Some(ExpandError::UnresolvedProcMacro) => { |
609 | self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro( | 604 | self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro { |
610 | UnresolvedProcMacro { | 605 | node: InFile::new(outer_file, syntax_ptr), |
611 | file: outer_file, | 606 | }); |
612 | node: syntax_ptr.into(), | ||
613 | precise_location: None, | ||
614 | macro_name: None, | ||
615 | }, | ||
616 | )); | ||
617 | } | 607 | } |
618 | Some(err) => { | 608 | Some(err) => { |
619 | self.source_map.diagnostics.push(BodyDiagnostic::MacroError(MacroError { | 609 | self.source_map.diagnostics.push(BodyDiagnostic::MacroError { |
620 | file: outer_file, | 610 | node: InFile::new(outer_file, syntax_ptr), |
621 | node: syntax_ptr.into(), | ||
622 | message: err.to_string(), | 611 | message: err.to_string(), |
623 | })); | 612 | }); |
624 | } | 613 | } |
625 | None => {} | 614 | None => {} |
626 | } | 615 | } |
@@ -945,12 +934,14 @@ impl ExprCollector<'_> { | |||
945 | return Some(()); | 934 | return Some(()); |
946 | } | 935 | } |
947 | 936 | ||
948 | self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode(InactiveCode { | 937 | self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode { |
949 | file: self.expander.current_file_id, | 938 | node: InFile::new( |
950 | node: SyntaxNodePtr::new(owner.syntax()), | 939 | self.expander.current_file_id, |
940 | SyntaxNodePtr::new(owner.syntax()), | ||
941 | ), | ||
951 | cfg, | 942 | cfg, |
952 | opts: self.expander.cfg_options().clone(), | 943 | opts: self.expander.cfg_options().clone(), |
953 | })); | 944 | }); |
954 | 945 | ||
955 | None | 946 | None |
956 | } | 947 | } |
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index 3e8f16306..d4fae05a6 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs | |||
@@ -96,26 +96,26 @@ fn f() { | |||
96 | // The three g̶e̶n̶d̶e̶r̶s̶ statements: | 96 | // The three g̶e̶n̶d̶e̶r̶s̶ statements: |
97 | 97 | ||
98 | #[cfg(a)] fn f() {} // Item statement | 98 | #[cfg(a)] fn f() {} // Item statement |
99 | //^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | 99 | //^^^^^^^^^^^^^^^^^^^ InactiveCode |
100 | #[cfg(a)] {} // Expression statement | 100 | #[cfg(a)] {} // Expression statement |
101 | //^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | 101 | //^^^^^^^^^^^^ InactiveCode |
102 | #[cfg(a)] let x = 0; // let statement | 102 | #[cfg(a)] let x = 0; // let statement |
103 | //^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | 103 | //^^^^^^^^^^^^^^^^^^^^ InactiveCode |
104 | 104 | ||
105 | abc(#[cfg(a)] 0); | 105 | abc(#[cfg(a)] 0); |
106 | //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | 106 | //^^^^^^^^^^^ InactiveCode |
107 | let x = Struct { | 107 | let x = Struct { |
108 | #[cfg(a)] f: 0, | 108 | #[cfg(a)] f: 0, |
109 | //^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | 109 | //^^^^^^^^^^^^^^ InactiveCode |
110 | }; | 110 | }; |
111 | match () { | 111 | match () { |
112 | () => (), | 112 | () => (), |
113 | #[cfg(a)] () => (), | 113 | #[cfg(a)] () => (), |
114 | //^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | 114 | //^^^^^^^^^^^^^^^^^^ InactiveCode |
115 | } | 115 | } |
116 | 116 | ||
117 | #[cfg(a)] 0 // Trailing expression of block | 117 | #[cfg(a)] 0 // Trailing expression of block |
118 | //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | 118 | //^^^^^^^^^^^ InactiveCode |
119 | } | 119 | } |
120 | ", | 120 | ", |
121 | ); | 121 | ); |
@@ -188,7 +188,7 @@ fn unresolved_macro_diag() { | |||
188 | r#" | 188 | r#" |
189 | fn f() { | 189 | fn f() { |
190 | m!(); | 190 | m!(); |
191 | //^^^^ unresolved macro `m!` | 191 | //^^^^ UnresolvedMacroCall |
192 | } | 192 | } |
193 | "#, | 193 | "#, |
194 | ); | 194 | ); |
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs deleted file mode 100644 index a71ae2668..000000000 --- a/crates/hir_def/src/diagnostics.rs +++ /dev/null | |||
@@ -1,227 +0,0 @@ | |||
1 | //! Diagnostics produced by `hir_def`. | ||
2 | |||
3 | use std::any::Any; | ||
4 | use stdx::format_to; | ||
5 | |||
6 | use cfg::{CfgExpr, CfgOptions, DnfExpr}; | ||
7 | use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; | ||
8 | use hir_expand::{HirFileId, InFile}; | ||
9 | use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; | ||
10 | |||
11 | use crate::{db::DefDatabase, path::ModPath, DefWithBodyId}; | ||
12 | |||
13 | pub fn validate_body(db: &dyn DefDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { | ||
14 | let source_map = db.body_with_source_map(owner).1; | ||
15 | source_map.add_diagnostics(db, sink); | ||
16 | } | ||
17 | |||
18 | // Diagnostic: unresolved-module | ||
19 | // | ||
20 | // This diagnostic is triggered if rust-analyzer is unable to discover referred module. | ||
21 | #[derive(Debug)] | ||
22 | pub struct UnresolvedModule { | ||
23 | pub file: HirFileId, | ||
24 | pub decl: AstPtr<ast::Module>, | ||
25 | pub candidate: String, | ||
26 | } | ||
27 | |||
28 | impl Diagnostic for UnresolvedModule { | ||
29 | fn code(&self) -> DiagnosticCode { | ||
30 | DiagnosticCode("unresolved-module") | ||
31 | } | ||
32 | fn message(&self) -> String { | ||
33 | "unresolved module".to_string() | ||
34 | } | ||
35 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
36 | InFile::new(self.file, self.decl.clone().into()) | ||
37 | } | ||
38 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
39 | self | ||
40 | } | ||
41 | } | ||
42 | |||
43 | // Diagnostic: unresolved-extern-crate | ||
44 | // | ||
45 | // This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate. | ||
46 | #[derive(Debug)] | ||
47 | pub struct UnresolvedExternCrate { | ||
48 | pub file: HirFileId, | ||
49 | pub item: AstPtr<ast::ExternCrate>, | ||
50 | } | ||
51 | |||
52 | impl Diagnostic for UnresolvedExternCrate { | ||
53 | fn code(&self) -> DiagnosticCode { | ||
54 | DiagnosticCode("unresolved-extern-crate") | ||
55 | } | ||
56 | fn message(&self) -> String { | ||
57 | "unresolved extern crate".to_string() | ||
58 | } | ||
59 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
60 | InFile::new(self.file, self.item.clone().into()) | ||
61 | } | ||
62 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
63 | self | ||
64 | } | ||
65 | } | ||
66 | |||
67 | // Diagnostic: unresolved-import | ||
68 | // | ||
69 | // This diagnostic is triggered if rust-analyzer is unable to discover imported module. | ||
70 | #[derive(Debug)] | ||
71 | pub struct UnresolvedImport { | ||
72 | pub file: HirFileId, | ||
73 | pub node: AstPtr<ast::UseTree>, | ||
74 | } | ||
75 | |||
76 | impl Diagnostic for UnresolvedImport { | ||
77 | fn code(&self) -> DiagnosticCode { | ||
78 | DiagnosticCode("unresolved-import") | ||
79 | } | ||
80 | fn message(&self) -> String { | ||
81 | "unresolved import".to_string() | ||
82 | } | ||
83 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
84 | InFile::new(self.file, self.node.clone().into()) | ||
85 | } | ||
86 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
87 | self | ||
88 | } | ||
89 | fn is_experimental(&self) -> bool { | ||
90 | // This currently results in false positives in the following cases: | ||
91 | // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly) | ||
92 | // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly) | ||
93 | // - proc macros and/or proc macro generated code | ||
94 | true | ||
95 | } | ||
96 | } | ||
97 | |||
98 | // Diagnostic: unresolved-macro-call | ||
99 | // | ||
100 | // This diagnostic is triggered if rust-analyzer is unable to resolve the path to a | ||
101 | // macro in a macro invocation. | ||
102 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
103 | pub struct UnresolvedMacroCall { | ||
104 | pub file: HirFileId, | ||
105 | pub node: AstPtr<ast::MacroCall>, | ||
106 | pub path: ModPath, | ||
107 | } | ||
108 | |||
109 | impl Diagnostic for UnresolvedMacroCall { | ||
110 | fn code(&self) -> DiagnosticCode { | ||
111 | DiagnosticCode("unresolved-macro-call") | ||
112 | } | ||
113 | fn message(&self) -> String { | ||
114 | format!("unresolved macro `{}!`", self.path) | ||
115 | } | ||
116 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
117 | InFile::new(self.file, self.node.clone().into()) | ||
118 | } | ||
119 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
120 | self | ||
121 | } | ||
122 | fn is_experimental(&self) -> bool { | ||
123 | true | ||
124 | } | ||
125 | } | ||
126 | |||
127 | // Diagnostic: inactive-code | ||
128 | // | ||
129 | // This diagnostic is shown for code with inactive `#[cfg]` attributes. | ||
130 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
131 | pub struct InactiveCode { | ||
132 | pub file: HirFileId, | ||
133 | pub node: SyntaxNodePtr, | ||
134 | pub cfg: CfgExpr, | ||
135 | pub opts: CfgOptions, | ||
136 | } | ||
137 | |||
138 | impl Diagnostic for InactiveCode { | ||
139 | fn code(&self) -> DiagnosticCode { | ||
140 | DiagnosticCode("inactive-code") | ||
141 | } | ||
142 | fn message(&self) -> String { | ||
143 | let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts); | ||
144 | let mut buf = "code is inactive due to #[cfg] directives".to_string(); | ||
145 | |||
146 | if let Some(inactive) = inactive { | ||
147 | format_to!(buf, ": {}", inactive); | ||
148 | } | ||
149 | |||
150 | buf | ||
151 | } | ||
152 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
153 | InFile::new(self.file, self.node.clone()) | ||
154 | } | ||
155 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
156 | self | ||
157 | } | ||
158 | } | ||
159 | |||
160 | // Diagnostic: unresolved-proc-macro | ||
161 | // | ||
162 | // This diagnostic is shown when a procedural macro can not be found. This usually means that | ||
163 | // procedural macro support is simply disabled (and hence is only a weak hint instead of an error), | ||
164 | // but can also indicate project setup problems. | ||
165 | // | ||
166 | // If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the | ||
167 | // `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can | ||
168 | // enable support for procedural macros (see `rust-analyzer.procMacro.enable`). | ||
169 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
170 | pub struct UnresolvedProcMacro { | ||
171 | pub file: HirFileId, | ||
172 | pub node: SyntaxNodePtr, | ||
173 | /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange` | ||
174 | /// to use instead. | ||
175 | pub precise_location: Option<TextRange>, | ||
176 | pub macro_name: Option<String>, | ||
177 | } | ||
178 | |||
179 | impl Diagnostic for UnresolvedProcMacro { | ||
180 | fn code(&self) -> DiagnosticCode { | ||
181 | DiagnosticCode("unresolved-proc-macro") | ||
182 | } | ||
183 | |||
184 | fn message(&self) -> String { | ||
185 | match &self.macro_name { | ||
186 | Some(name) => format!("proc macro `{}` not expanded", name), | ||
187 | None => "proc macro not expanded".to_string(), | ||
188 | } | ||
189 | } | ||
190 | |||
191 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
192 | InFile::new(self.file, self.node.clone()) | ||
193 | } | ||
194 | |||
195 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
196 | self | ||
197 | } | ||
198 | } | ||
199 | |||
200 | // Diagnostic: macro-error | ||
201 | // | ||
202 | // This diagnostic is shown for macro expansion errors. | ||
203 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
204 | pub struct MacroError { | ||
205 | pub file: HirFileId, | ||
206 | pub node: SyntaxNodePtr, | ||
207 | pub message: String, | ||
208 | } | ||
209 | |||
210 | impl Diagnostic for MacroError { | ||
211 | fn code(&self) -> DiagnosticCode { | ||
212 | DiagnosticCode("macro-error") | ||
213 | } | ||
214 | fn message(&self) -> String { | ||
215 | self.message.clone() | ||
216 | } | ||
217 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
218 | InFile::new(self.file, self.node.clone()) | ||
219 | } | ||
220 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
221 | self | ||
222 | } | ||
223 | fn is_experimental(&self) -> bool { | ||
224 | // Newly added and not very well-tested, might contain false positives. | ||
225 | true | ||
226 | } | ||
227 | } | ||
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index 70001cac8..9aa95720a 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -19,7 +19,6 @@ pub mod path; | |||
19 | pub mod type_ref; | 19 | pub mod type_ref; |
20 | pub mod builtin_type; | 20 | pub mod builtin_type; |
21 | pub mod builtin_attr; | 21 | pub mod builtin_attr; |
22 | pub mod diagnostics; | ||
23 | pub mod per_ns; | 22 | pub mod per_ns; |
24 | pub mod item_scope; | 23 | pub mod item_scope; |
25 | 24 | ||
@@ -56,7 +55,6 @@ use std::{ | |||
56 | sync::Arc, | 55 | sync::Arc, |
57 | }; | 56 | }; |
58 | 57 | ||
59 | use adt::VariantData; | ||
60 | use base_db::{impl_intern_key, salsa, CrateId}; | 58 | use base_db::{impl_intern_key, salsa, CrateId}; |
61 | use hir_expand::{ | 59 | use hir_expand::{ |
62 | ast_id_map::FileAstId, | 60 | ast_id_map::FileAstId, |
@@ -67,15 +65,18 @@ use hir_expand::{ | |||
67 | use la_arena::Idx; | 65 | use la_arena::Idx; |
68 | use nameres::DefMap; | 66 | use nameres::DefMap; |
69 | use path::ModPath; | 67 | use path::ModPath; |
68 | use stdx::impl_from; | ||
70 | use syntax::ast; | 69 | use syntax::ast; |
71 | 70 | ||
72 | use crate::attr::AttrId; | 71 | use crate::{ |
73 | use crate::builtin_type::BuiltinType; | 72 | adt::VariantData, |
74 | use item_tree::{ | 73 | attr::AttrId, |
75 | Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait, | 74 | builtin_type::BuiltinType, |
76 | TypeAlias, Union, | 75 | item_tree::{ |
76 | Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait, | ||
77 | TypeAlias, Union, | ||
78 | }, | ||
77 | }; | 79 | }; |
78 | use stdx::impl_from; | ||
79 | 80 | ||
80 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 81 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
81 | pub struct ModuleId { | 82 | pub struct ModuleId { |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 249af6fc8..ebfcc26c4 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -47,18 +47,19 @@ | |||
47 | //! path and, upon success, we run macro expansion and "collect module" phase on | 47 | //! path and, upon success, we run macro expansion and "collect module" phase on |
48 | //! the result | 48 | //! the result |
49 | 49 | ||
50 | pub mod diagnostics; | ||
50 | mod collector; | 51 | mod collector; |
51 | mod mod_resolution; | 52 | mod mod_resolution; |
52 | mod path_resolution; | 53 | mod path_resolution; |
54 | mod proc_macro; | ||
53 | 55 | ||
54 | #[cfg(test)] | 56 | #[cfg(test)] |
55 | mod tests; | 57 | mod tests; |
56 | mod proc_macro; | ||
57 | 58 | ||
58 | use std::sync::Arc; | 59 | use std::sync::Arc; |
59 | 60 | ||
60 | use base_db::{CrateId, Edition, FileId}; | 61 | use base_db::{CrateId, Edition, FileId}; |
61 | use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile, MacroDefId}; | 62 | use hir_expand::{name::Name, InFile, MacroDefId}; |
62 | use la_arena::Arena; | 63 | use la_arena::Arena; |
63 | use profile::Count; | 64 | use profile::Count; |
64 | use rustc_hash::FxHashMap; | 65 | use rustc_hash::FxHashMap; |
@@ -254,15 +255,6 @@ impl DefMap { | |||
254 | } | 255 | } |
255 | } | 256 | } |
256 | 257 | ||
257 | pub fn add_diagnostics( | ||
258 | &self, | ||
259 | db: &dyn DefDatabase, | ||
260 | module: LocalModuleId, | ||
261 | sink: &mut DiagnosticSink, | ||
262 | ) { | ||
263 | self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink)) | ||
264 | } | ||
265 | |||
266 | pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalModuleId> + '_ { | 258 | pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalModuleId> + '_ { |
267 | self.modules | 259 | self.modules |
268 | .iter() | 260 | .iter() |
@@ -448,6 +440,11 @@ impl DefMap { | |||
448 | module.scope.shrink_to_fit(); | 440 | module.scope.shrink_to_fit(); |
449 | } | 441 | } |
450 | } | 442 | } |
443 | |||
444 | /// Get a reference to the def map's diagnostics. | ||
445 | pub fn diagnostics(&self) -> &[DefDiagnostic] { | ||
446 | self.diagnostics.as_slice() | ||
447 | } | ||
451 | } | 448 | } |
452 | 449 | ||
453 | impl ModuleData { | 450 | impl ModuleData { |
@@ -471,236 +468,3 @@ pub enum ModuleSource { | |||
471 | Module(ast::Module), | 468 | Module(ast::Module), |
472 | BlockExpr(ast::BlockExpr), | 469 | BlockExpr(ast::BlockExpr), |
473 | } | 470 | } |
474 | |||
475 | mod diagnostics { | ||
476 | use cfg::{CfgExpr, CfgOptions}; | ||
477 | use hir_expand::diagnostics::DiagnosticSink; | ||
478 | use hir_expand::hygiene::Hygiene; | ||
479 | use hir_expand::{InFile, MacroCallKind}; | ||
480 | use syntax::ast::AttrsOwner; | ||
481 | use syntax::{ast, AstNode, AstPtr, SyntaxKind, SyntaxNodePtr}; | ||
482 | |||
483 | use crate::path::ModPath; | ||
484 | use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; | ||
485 | |||
486 | #[derive(Debug, PartialEq, Eq)] | ||
487 | enum DiagnosticKind { | ||
488 | UnresolvedModule { declaration: AstId<ast::Module>, candidate: String }, | ||
489 | |||
490 | UnresolvedExternCrate { ast: AstId<ast::ExternCrate> }, | ||
491 | |||
492 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, | ||
493 | |||
494 | UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions }, | ||
495 | |||
496 | UnresolvedProcMacro { ast: MacroCallKind }, | ||
497 | |||
498 | UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath }, | ||
499 | |||
500 | MacroError { ast: MacroCallKind, message: String }, | ||
501 | } | ||
502 | |||
503 | #[derive(Debug, PartialEq, Eq)] | ||
504 | pub(super) struct DefDiagnostic { | ||
505 | in_module: LocalModuleId, | ||
506 | kind: DiagnosticKind, | ||
507 | } | ||
508 | |||
509 | impl DefDiagnostic { | ||
510 | pub(super) fn unresolved_module( | ||
511 | container: LocalModuleId, | ||
512 | declaration: AstId<ast::Module>, | ||
513 | candidate: String, | ||
514 | ) -> Self { | ||
515 | Self { | ||
516 | in_module: container, | ||
517 | kind: DiagnosticKind::UnresolvedModule { declaration, candidate }, | ||
518 | } | ||
519 | } | ||
520 | |||
521 | pub(super) fn unresolved_extern_crate( | ||
522 | container: LocalModuleId, | ||
523 | declaration: AstId<ast::ExternCrate>, | ||
524 | ) -> Self { | ||
525 | Self { | ||
526 | in_module: container, | ||
527 | kind: DiagnosticKind::UnresolvedExternCrate { ast: declaration }, | ||
528 | } | ||
529 | } | ||
530 | |||
531 | pub(super) fn unresolved_import( | ||
532 | container: LocalModuleId, | ||
533 | ast: AstId<ast::Use>, | ||
534 | index: usize, | ||
535 | ) -> Self { | ||
536 | Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } } | ||
537 | } | ||
538 | |||
539 | pub(super) fn unconfigured_code( | ||
540 | container: LocalModuleId, | ||
541 | ast: AstId<ast::Item>, | ||
542 | cfg: CfgExpr, | ||
543 | opts: CfgOptions, | ||
544 | ) -> Self { | ||
545 | Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } } | ||
546 | } | ||
547 | |||
548 | pub(super) fn unresolved_proc_macro(container: LocalModuleId, ast: MacroCallKind) -> Self { | ||
549 | Self { in_module: container, kind: DiagnosticKind::UnresolvedProcMacro { ast } } | ||
550 | } | ||
551 | |||
552 | pub(super) fn macro_error( | ||
553 | container: LocalModuleId, | ||
554 | ast: MacroCallKind, | ||
555 | message: String, | ||
556 | ) -> Self { | ||
557 | Self { in_module: container, kind: DiagnosticKind::MacroError { ast, message } } | ||
558 | } | ||
559 | |||
560 | pub(super) fn unresolved_macro_call( | ||
561 | container: LocalModuleId, | ||
562 | ast: AstId<ast::MacroCall>, | ||
563 | path: ModPath, | ||
564 | ) -> Self { | ||
565 | Self { in_module: container, kind: DiagnosticKind::UnresolvedMacroCall { ast, path } } | ||
566 | } | ||
567 | |||
568 | pub(super) fn add_to( | ||
569 | &self, | ||
570 | db: &dyn DefDatabase, | ||
571 | target_module: LocalModuleId, | ||
572 | sink: &mut DiagnosticSink, | ||
573 | ) { | ||
574 | if self.in_module != target_module { | ||
575 | return; | ||
576 | } | ||
577 | |||
578 | match &self.kind { | ||
579 | DiagnosticKind::UnresolvedModule { declaration, candidate } => { | ||
580 | let decl = declaration.to_node(db.upcast()); | ||
581 | sink.push(UnresolvedModule { | ||
582 | file: declaration.file_id, | ||
583 | decl: AstPtr::new(&decl), | ||
584 | candidate: candidate.clone(), | ||
585 | }) | ||
586 | } | ||
587 | |||
588 | DiagnosticKind::UnresolvedExternCrate { ast } => { | ||
589 | let item = ast.to_node(db.upcast()); | ||
590 | sink.push(UnresolvedExternCrate { | ||
591 | file: ast.file_id, | ||
592 | item: AstPtr::new(&item), | ||
593 | }); | ||
594 | } | ||
595 | |||
596 | DiagnosticKind::UnresolvedImport { ast, index } => { | ||
597 | let use_item = ast.to_node(db.upcast()); | ||
598 | let hygiene = Hygiene::new(db.upcast(), ast.file_id); | ||
599 | let mut cur = 0; | ||
600 | let mut tree = None; | ||
601 | ModPath::expand_use_item( | ||
602 | db, | ||
603 | InFile::new(ast.file_id, use_item), | ||
604 | &hygiene, | ||
605 | |_mod_path, use_tree, _is_glob, _alias| { | ||
606 | if cur == *index { | ||
607 | tree = Some(use_tree.clone()); | ||
608 | } | ||
609 | |||
610 | cur += 1; | ||
611 | }, | ||
612 | ); | ||
613 | |||
614 | if let Some(tree) = tree { | ||
615 | sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) }); | ||
616 | } | ||
617 | } | ||
618 | |||
619 | DiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { | ||
620 | let item = ast.to_node(db.upcast()); | ||
621 | sink.push(InactiveCode { | ||
622 | file: ast.file_id, | ||
623 | node: AstPtr::new(&item).into(), | ||
624 | cfg: cfg.clone(), | ||
625 | opts: opts.clone(), | ||
626 | }); | ||
627 | } | ||
628 | |||
629 | DiagnosticKind::UnresolvedProcMacro { ast } => { | ||
630 | let mut precise_location = None; | ||
631 | let (file, ast, name) = match ast { | ||
632 | MacroCallKind::FnLike { ast_id, .. } => { | ||
633 | let node = ast_id.to_node(db.upcast()); | ||
634 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) | ||
635 | } | ||
636 | MacroCallKind::Derive { ast_id, derive_name, .. } => { | ||
637 | let node = ast_id.to_node(db.upcast()); | ||
638 | |||
639 | // Compute the precise location of the macro name's token in the derive | ||
640 | // list. | ||
641 | // FIXME: This does not handle paths to the macro, but neither does the | ||
642 | // rest of r-a. | ||
643 | let derive_attrs = | ||
644 | node.attrs().filter_map(|attr| match attr.as_simple_call() { | ||
645 | Some((name, args)) if name == "derive" => Some(args), | ||
646 | _ => None, | ||
647 | }); | ||
648 | 'outer: for attr in derive_attrs { | ||
649 | let tokens = | ||
650 | attr.syntax().children_with_tokens().filter_map(|elem| { | ||
651 | match elem { | ||
652 | syntax::NodeOrToken::Node(_) => None, | ||
653 | syntax::NodeOrToken::Token(tok) => Some(tok), | ||
654 | } | ||
655 | }); | ||
656 | for token in tokens { | ||
657 | if token.kind() == SyntaxKind::IDENT | ||
658 | && token.text() == derive_name.as_str() | ||
659 | { | ||
660 | precise_location = Some(token.text_range()); | ||
661 | break 'outer; | ||
662 | } | ||
663 | } | ||
664 | } | ||
665 | |||
666 | ( | ||
667 | ast_id.file_id, | ||
668 | SyntaxNodePtr::from(AstPtr::new(&node)), | ||
669 | Some(derive_name.clone()), | ||
670 | ) | ||
671 | } | ||
672 | }; | ||
673 | sink.push(UnresolvedProcMacro { | ||
674 | file, | ||
675 | node: ast, | ||
676 | precise_location, | ||
677 | macro_name: name, | ||
678 | }); | ||
679 | } | ||
680 | |||
681 | DiagnosticKind::UnresolvedMacroCall { ast, path } => { | ||
682 | let node = ast.to_node(db.upcast()); | ||
683 | sink.push(UnresolvedMacroCall { | ||
684 | file: ast.file_id, | ||
685 | node: AstPtr::new(&node), | ||
686 | path: path.clone(), | ||
687 | }); | ||
688 | } | ||
689 | |||
690 | DiagnosticKind::MacroError { ast, message } => { | ||
691 | let (file, ast) = match ast { | ||
692 | MacroCallKind::FnLike { ast_id, .. } => { | ||
693 | let node = ast_id.to_node(db.upcast()); | ||
694 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | ||
695 | } | ||
696 | MacroCallKind::Derive { ast_id, .. } => { | ||
697 | let node = ast_id.to_node(db.upcast()); | ||
698 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | ||
699 | } | ||
700 | }; | ||
701 | sink.push(MacroError { file, node: ast, message: message.clone() }); | ||
702 | } | ||
703 | } | ||
704 | } | ||
705 | } | ||
706 | } | ||
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 2ae740d0e..3ea472908 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -33,7 +33,10 @@ use crate::{ | |||
33 | }, | 33 | }, |
34 | macro_call_as_call_id, | 34 | macro_call_as_call_id, |
35 | nameres::{ | 35 | nameres::{ |
36 | diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, | 36 | diagnostics::DefDiagnostic, |
37 | mod_resolution::ModDir, | ||
38 | path_resolution::ReachedFixedPoint, | ||
39 | proc_macro::{ProcMacroDef, ProcMacroKind}, | ||
37 | BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode, | 40 | BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode, |
38 | }, | 41 | }, |
39 | path::{ImportAlias, ModPath, PathKind}, | 42 | path::{ImportAlias, ModPath, PathKind}, |
@@ -44,8 +47,6 @@ use crate::{ | |||
44 | UnresolvedMacro, | 47 | UnresolvedMacro, |
45 | }; | 48 | }; |
46 | 49 | ||
47 | use super::proc_macro::{ProcMacroDef, ProcMacroKind}; | ||
48 | |||
49 | const GLOB_RECURSION_LIMIT: usize = 100; | 50 | const GLOB_RECURSION_LIMIT: usize = 100; |
50 | const EXPANSION_DEPTH_LIMIT: usize = 128; | 51 | const EXPANSION_DEPTH_LIMIT: usize = 128; |
51 | const FIXED_POINT_LIMIT: usize = 8192; | 52 | const FIXED_POINT_LIMIT: usize = 8192; |
diff --git a/crates/hir_def/src/nameres/diagnostics.rs b/crates/hir_def/src/nameres/diagnostics.rs new file mode 100644 index 000000000..8f2f0ff9f --- /dev/null +++ b/crates/hir_def/src/nameres/diagnostics.rs | |||
@@ -0,0 +1,90 @@ | |||
1 | //! Diagnostics emitted during DefMap construction. | ||
2 | |||
3 | use cfg::{CfgExpr, CfgOptions}; | ||
4 | use hir_expand::MacroCallKind; | ||
5 | use syntax::ast; | ||
6 | |||
7 | use crate::{nameres::LocalModuleId, path::ModPath, AstId}; | ||
8 | |||
9 | #[derive(Debug, PartialEq, Eq)] | ||
10 | pub enum DefDiagnosticKind { | ||
11 | UnresolvedModule { ast: AstId<ast::Module>, candidate: String }, | ||
12 | |||
13 | UnresolvedExternCrate { ast: AstId<ast::ExternCrate> }, | ||
14 | |||
15 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, | ||
16 | |||
17 | UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions }, | ||
18 | |||
19 | UnresolvedProcMacro { ast: MacroCallKind }, | ||
20 | |||
21 | UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath }, | ||
22 | |||
23 | MacroError { ast: MacroCallKind, message: String }, | ||
24 | } | ||
25 | |||
26 | #[derive(Debug, PartialEq, Eq)] | ||
27 | pub struct DefDiagnostic { | ||
28 | pub in_module: LocalModuleId, | ||
29 | pub kind: DefDiagnosticKind, | ||
30 | } | ||
31 | |||
32 | impl DefDiagnostic { | ||
33 | pub(super) fn unresolved_module( | ||
34 | container: LocalModuleId, | ||
35 | declaration: AstId<ast::Module>, | ||
36 | candidate: String, | ||
37 | ) -> Self { | ||
38 | Self { | ||
39 | in_module: container, | ||
40 | kind: DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate }, | ||
41 | } | ||
42 | } | ||
43 | |||
44 | pub(super) fn unresolved_extern_crate( | ||
45 | container: LocalModuleId, | ||
46 | declaration: AstId<ast::ExternCrate>, | ||
47 | ) -> Self { | ||
48 | Self { | ||
49 | in_module: container, | ||
50 | kind: DefDiagnosticKind::UnresolvedExternCrate { ast: declaration }, | ||
51 | } | ||
52 | } | ||
53 | |||
54 | pub(super) fn unresolved_import( | ||
55 | container: LocalModuleId, | ||
56 | ast: AstId<ast::Use>, | ||
57 | index: usize, | ||
58 | ) -> Self { | ||
59 | Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { ast, index } } | ||
60 | } | ||
61 | |||
62 | pub(super) fn unconfigured_code( | ||
63 | container: LocalModuleId, | ||
64 | ast: AstId<ast::Item>, | ||
65 | cfg: CfgExpr, | ||
66 | opts: CfgOptions, | ||
67 | ) -> Self { | ||
68 | Self { in_module: container, kind: DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } } | ||
69 | } | ||
70 | |||
71 | pub(super) fn unresolved_proc_macro(container: LocalModuleId, ast: MacroCallKind) -> Self { | ||
72 | Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast } } | ||
73 | } | ||
74 | |||
75 | pub(super) fn macro_error( | ||
76 | container: LocalModuleId, | ||
77 | ast: MacroCallKind, | ||
78 | message: String, | ||
79 | ) -> Self { | ||
80 | Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } } | ||
81 | } | ||
82 | |||
83 | pub(super) fn unresolved_macro_call( | ||
84 | container: LocalModuleId, | ||
85 | ast: AstId<ast::MacroCall>, | ||
86 | path: ModPath, | ||
87 | ) -> Self { | ||
88 | Self { in_module: container, kind: DefDiagnosticKind::UnresolvedMacroCall { ast, path } } | ||
89 | } | ||
90 | } | ||
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index 75147d973..ec6670952 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -18,7 +18,7 @@ fn unresolved_import() { | |||
18 | r" | 18 | r" |
19 | use does_exist; | 19 | use does_exist; |
20 | use does_not_exist; | 20 | use does_not_exist; |
21 | //^^^^^^^^^^^^^^ unresolved import | 21 | //^^^^^^^^^^^^^^^^^^^ UnresolvedImport |
22 | 22 | ||
23 | mod does_exist {} | 23 | mod does_exist {} |
24 | ", | 24 | ", |
@@ -26,40 +26,13 @@ fn unresolved_import() { | |||
26 | } | 26 | } |
27 | 27 | ||
28 | #[test] | 28 | #[test] |
29 | fn unresolved_import_in_use_tree() { | ||
30 | // Only the relevant part of a nested `use` item should be highlighted. | ||
31 | check_diagnostics( | ||
32 | r" | ||
33 | use does_exist::{Exists, DoesntExist}; | ||
34 | //^^^^^^^^^^^ unresolved import | ||
35 | |||
36 | use {does_not_exist::*, does_exist}; | ||
37 | //^^^^^^^^^^^^^^^^^ unresolved import | ||
38 | |||
39 | use does_not_exist::{ | ||
40 | a, | ||
41 | //^ unresolved import | ||
42 | b, | ||
43 | //^ unresolved import | ||
44 | c, | ||
45 | //^ unresolved import | ||
46 | }; | ||
47 | |||
48 | mod does_exist { | ||
49 | pub struct Exists; | ||
50 | } | ||
51 | ", | ||
52 | ); | ||
53 | } | ||
54 | |||
55 | #[test] | ||
56 | fn unresolved_extern_crate() { | 29 | fn unresolved_extern_crate() { |
57 | check_diagnostics( | 30 | check_diagnostics( |
58 | r" | 31 | r" |
59 | //- /main.rs crate:main deps:core | 32 | //- /main.rs crate:main deps:core |
60 | extern crate core; | 33 | extern crate core; |
61 | extern crate doesnotexist; | 34 | extern crate doesnotexist; |
62 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate | 35 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate |
63 | //- /lib.rs crate:core | 36 | //- /lib.rs crate:core |
64 | ", | 37 | ", |
65 | ); | 38 | ); |
@@ -72,7 +45,7 @@ fn extern_crate_self_as() { | |||
72 | r" | 45 | r" |
73 | //- /lib.rs | 46 | //- /lib.rs |
74 | extern crate doesnotexist; | 47 | extern crate doesnotexist; |
75 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate | 48 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate |
76 | // Should not error. | 49 | // Should not error. |
77 | extern crate self as foo; | 50 | extern crate self as foo; |
78 | struct Foo; | 51 | struct Foo; |
@@ -88,18 +61,18 @@ fn dedup_unresolved_import_from_unresolved_crate() { | |||
88 | //- /main.rs crate:main | 61 | //- /main.rs crate:main |
89 | mod a { | 62 | mod a { |
90 | extern crate doesnotexist; | 63 | extern crate doesnotexist; |
91 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate | 64 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate |
92 | 65 | ||
93 | // Should not error, since we already errored for the missing crate. | 66 | // Should not error, since we already errored for the missing crate. |
94 | use doesnotexist::{self, bla, *}; | 67 | use doesnotexist::{self, bla, *}; |
95 | 68 | ||
96 | use crate::doesnotexist; | 69 | use crate::doesnotexist; |
97 | //^^^^^^^^^^^^^^^^^^^ unresolved import | 70 | //^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport |
98 | } | 71 | } |
99 | 72 | ||
100 | mod m { | 73 | mod m { |
101 | use super::doesnotexist; | 74 | use super::doesnotexist; |
102 | //^^^^^^^^^^^^^^^^^^^ unresolved import | 75 | //^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport |
103 | } | 76 | } |
104 | ", | 77 | ", |
105 | ); | 78 | ); |
@@ -112,7 +85,7 @@ fn unresolved_module() { | |||
112 | //- /lib.rs | 85 | //- /lib.rs |
113 | mod foo; | 86 | mod foo; |
114 | mod bar; | 87 | mod bar; |
115 | //^^^^^^^^ unresolved module | 88 | //^^^^^^^^ UnresolvedModule |
116 | mod baz {} | 89 | mod baz {} |
117 | //- /foo.rs | 90 | //- /foo.rs |
118 | ", | 91 | ", |
@@ -127,16 +100,16 @@ fn inactive_item() { | |||
127 | r#" | 100 | r#" |
128 | //- /lib.rs | 101 | //- /lib.rs |
129 | #[cfg(no)] pub fn f() {} | 102 | #[cfg(no)] pub fn f() {} |
130 | //^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled | 103 | //^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode |
131 | 104 | ||
132 | #[cfg(no)] #[cfg(no2)] mod m; | 105 | #[cfg(no)] #[cfg(no2)] mod m; |
133 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no and no2 are disabled | 106 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode |
134 | 107 | ||
135 | #[cfg(all(not(a), b))] enum E {} | 108 | #[cfg(all(not(a), b))] enum E {} |
136 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: b is disabled | 109 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode |
137 | 110 | ||
138 | #[cfg(feature = "std")] use std; | 111 | #[cfg(feature = "std")] use std; |
139 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled | 112 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode |
140 | "#, | 113 | "#, |
141 | ); | 114 | ); |
142 | } | 115 | } |
@@ -149,14 +122,14 @@ fn inactive_via_cfg_attr() { | |||
149 | r#" | 122 | r#" |
150 | //- /lib.rs | 123 | //- /lib.rs |
151 | #[cfg_attr(not(never), cfg(no))] fn f() {} | 124 | #[cfg_attr(not(never), cfg(no))] fn f() {} |
152 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled | 125 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode |
153 | 126 | ||
154 | #[cfg_attr(not(never), cfg(not(no)))] fn f() {} | 127 | #[cfg_attr(not(never), cfg(not(no)))] fn f() {} |
155 | 128 | ||
156 | #[cfg_attr(never, cfg(no))] fn g() {} | 129 | #[cfg_attr(never, cfg(no))] fn g() {} |
157 | 130 | ||
158 | #[cfg_attr(not(never), inline, cfg(no))] fn h() {} | 131 | #[cfg_attr(not(never), inline, cfg(no))] fn h() {} |
159 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled | 132 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode |
160 | "#, | 133 | "#, |
161 | ); | 134 | ); |
162 | } | 135 | } |
@@ -170,7 +143,7 @@ fn unresolved_legacy_scope_macro() { | |||
170 | 143 | ||
171 | m!(); | 144 | m!(); |
172 | m2!(); | 145 | m2!(); |
173 | //^^^^^^ unresolved macro `self::m2!` | 146 | //^^^^^^ UnresolvedMacroCall |
174 | "#, | 147 | "#, |
175 | ); | 148 | ); |
176 | } | 149 | } |
@@ -187,7 +160,7 @@ fn unresolved_module_scope_macro() { | |||
187 | 160 | ||
188 | self::m!(); | 161 | self::m!(); |
189 | self::m2!(); | 162 | self::m2!(); |
190 | //^^^^^^^^^^^^ unresolved macro `self::m2!` | 163 | //^^^^^^^^^^^^ UnresolvedMacroCall |
191 | "#, | 164 | "#, |
192 | ); | 165 | ); |
193 | } | 166 | } |
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index 45ab9d0ff..d9ec03d2d 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs | |||
@@ -71,7 +71,7 @@ impl ModPath { | |||
71 | } | 71 | } |
72 | 72 | ||
73 | /// Calls `cb` with all paths, represented by this use item. | 73 | /// Calls `cb` with all paths, represented by this use item. |
74 | pub(crate) fn expand_use_item( | 74 | pub fn expand_use_item( |
75 | db: &dyn DefDatabase, | 75 | db: &dyn DefDatabase, |
76 | item_src: InFile<ast::Use>, | 76 | item_src: InFile<ast::Use>, |
77 | hygiene: &Hygiene, | 77 | hygiene: &Hygiene, |
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs index 5d5dd9c8f..54ede7393 100644 --- a/crates/hir_def/src/path/lower.rs +++ b/crates/hir_def/src/path/lower.rs | |||
@@ -205,15 +205,14 @@ fn lower_generic_args_from_fn_path( | |||
205 | ) -> Option<GenericArgs> { | 205 | ) -> Option<GenericArgs> { |
206 | let mut args = Vec::new(); | 206 | let mut args = Vec::new(); |
207 | let mut bindings = Vec::new(); | 207 | let mut bindings = Vec::new(); |
208 | if let Some(params) = params { | 208 | let params = params?; |
209 | let mut param_types = Vec::new(); | 209 | let mut param_types = Vec::new(); |
210 | for param in params.params() { | 210 | for param in params.params() { |
211 | let type_ref = TypeRef::from_ast_opt(&ctx, param.ty()); | 211 | let type_ref = TypeRef::from_ast_opt(&ctx, param.ty()); |
212 | param_types.push(type_ref); | 212 | param_types.push(type_ref); |
213 | } | ||
214 | let arg = GenericArg::Type(TypeRef::Tuple(param_types)); | ||
215 | args.push(arg); | ||
216 | } | 213 | } |
214 | let arg = GenericArg::Type(TypeRef::Tuple(param_types)); | ||
215 | args.push(arg); | ||
217 | if let Some(ret_type) = ret_type { | 216 | if let Some(ret_type) = ret_type { |
218 | let type_ref = TypeRef::from_ast_opt(&ctx, ret_type.ty()); | 217 | let type_ref = TypeRef::from_ast_opt(&ctx, ret_type.ty()); |
219 | bindings.push(AssociatedTypeBinding { | 218 | bindings.push(AssociatedTypeBinding { |
@@ -221,10 +220,14 @@ fn lower_generic_args_from_fn_path( | |||
221 | type_ref: Some(type_ref), | 220 | type_ref: Some(type_ref), |
222 | bounds: Vec::new(), | 221 | bounds: Vec::new(), |
223 | }); | 222 | }); |
224 | } | ||
225 | if args.is_empty() && bindings.is_empty() { | ||
226 | None | ||
227 | } else { | 223 | } else { |
228 | Some(GenericArgs { args, has_self_type: false, bindings }) | 224 | // -> () |
225 | let type_ref = TypeRef::Tuple(Vec::new()); | ||
226 | bindings.push(AssociatedTypeBinding { | ||
227 | name: name![Output], | ||
228 | type_ref: Some(type_ref), | ||
229 | bounds: Vec::new(), | ||
230 | }); | ||
229 | } | 231 | } |
232 | Some(GenericArgs { args, has_self_type: false, bindings }) | ||
230 | } | 233 | } |
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs index 0391cc49b..fb8a6f260 100644 --- a/crates/hir_def/src/resolver.rs +++ b/crates/hir_def/src/resolver.rs | |||
@@ -337,22 +337,34 @@ impl Resolver { | |||
337 | pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> { | 337 | pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> { |
338 | let mut traits = FxHashSet::default(); | 338 | let mut traits = FxHashSet::default(); |
339 | for scope in &self.scopes { | 339 | for scope in &self.scopes { |
340 | if let Scope::ModuleScope(m) = scope { | 340 | match scope { |
341 | if let Some(prelude) = m.def_map.prelude() { | 341 | Scope::ModuleScope(m) => { |
342 | let prelude_def_map = prelude.def_map(db); | 342 | if let Some(prelude) = m.def_map.prelude() { |
343 | traits.extend(prelude_def_map[prelude.local_id].scope.traits()); | ||
344 | } | ||
345 | traits.extend(m.def_map[m.module_id].scope.traits()); | ||
346 | |||
347 | // Add all traits that are in scope because of the containing DefMaps | ||
348 | m.def_map.with_ancestor_maps(db, m.module_id, &mut |def_map, module| { | ||
349 | if let Some(prelude) = def_map.prelude() { | ||
350 | let prelude_def_map = prelude.def_map(db); | 343 | let prelude_def_map = prelude.def_map(db); |
351 | traits.extend(prelude_def_map[prelude.local_id].scope.traits()); | 344 | traits.extend(prelude_def_map[prelude.local_id].scope.traits()); |
352 | } | 345 | } |
353 | traits.extend(def_map[module].scope.traits()); | 346 | traits.extend(m.def_map[m.module_id].scope.traits()); |
354 | None::<()> | 347 | |
355 | }); | 348 | // Add all traits that are in scope because of the containing DefMaps |
349 | m.def_map.with_ancestor_maps(db, m.module_id, &mut |def_map, module| { | ||
350 | if let Some(prelude) = def_map.prelude() { | ||
351 | let prelude_def_map = prelude.def_map(db); | ||
352 | traits.extend(prelude_def_map[prelude.local_id].scope.traits()); | ||
353 | } | ||
354 | traits.extend(def_map[module].scope.traits()); | ||
355 | None::<()> | ||
356 | }); | ||
357 | } | ||
358 | &Scope::ImplDefScope(impl_) => { | ||
359 | if let Some(target_trait) = &db.impl_data(impl_).target_trait { | ||
360 | if let Some(TypeNs::TraitId(trait_)) = | ||
361 | self.resolve_path_in_type_ns_fully(db, target_trait.path.mod_path()) | ||
362 | { | ||
363 | traits.insert(trait_); | ||
364 | } | ||
365 | } | ||
366 | } | ||
367 | _ => (), | ||
356 | } | 368 | } |
357 | } | 369 | } |
358 | traits | 370 | traits |
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index 8fa703a57..6c357c915 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs | |||
@@ -5,19 +5,20 @@ use std::{ | |||
5 | sync::{Arc, Mutex}, | 5 | sync::{Arc, Mutex}, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, Upcast}; | 8 | use base_db::{ |
9 | salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, FileRange, Upcast, | ||
10 | }; | ||
9 | use base_db::{AnchoredPath, SourceDatabase}; | 11 | use base_db::{AnchoredPath, SourceDatabase}; |
10 | use hir_expand::diagnostics::Diagnostic; | ||
11 | use hir_expand::diagnostics::DiagnosticSinkBuilder; | ||
12 | use hir_expand::{db::AstDatabase, InFile}; | 12 | use hir_expand::{db::AstDatabase, InFile}; |
13 | use rustc_hash::FxHashMap; | 13 | use rustc_hash::FxHashMap; |
14 | use rustc_hash::FxHashSet; | 14 | use rustc_hash::FxHashSet; |
15 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; | 15 | use syntax::{algo, ast, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize}; |
16 | use test_utils::extract_annotations; | 16 | use test_utils::extract_annotations; |
17 | 17 | ||
18 | use crate::{ | 18 | use crate::{ |
19 | body::BodyDiagnostic, | ||
19 | db::DefDatabase, | 20 | db::DefDatabase, |
20 | nameres::{DefMap, ModuleSource}, | 21 | nameres::{diagnostics::DefDiagnosticKind, DefMap, ModuleSource}, |
21 | src::HasSource, | 22 | src::HasSource, |
22 | LocalModuleId, Lookup, ModuleDefId, ModuleId, | 23 | LocalModuleId, Lookup, ModuleDefId, ModuleId, |
23 | }; | 24 | }; |
@@ -262,19 +263,70 @@ impl TestDB { | |||
262 | .collect() | 263 | .collect() |
263 | } | 264 | } |
264 | 265 | ||
265 | pub(crate) fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { | 266 | pub(crate) fn diagnostics(&self, cb: &mut dyn FnMut(FileRange, String)) { |
266 | let crate_graph = self.crate_graph(); | 267 | let crate_graph = self.crate_graph(); |
267 | for krate in crate_graph.iter() { | 268 | for krate in crate_graph.iter() { |
268 | let crate_def_map = self.crate_def_map(krate); | 269 | let crate_def_map = self.crate_def_map(krate); |
269 | 270 | ||
270 | let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); | 271 | for diag in crate_def_map.diagnostics() { |
271 | for (module_id, module) in crate_def_map.modules() { | 272 | let (node, message): (InFile<SyntaxNode>, &str) = match &diag.kind { |
272 | crate_def_map.add_diagnostics(self, module_id, &mut sink); | 273 | DefDiagnosticKind::UnresolvedModule { ast, .. } => { |
274 | let node = ast.to_node(self.upcast()); | ||
275 | (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedModule") | ||
276 | } | ||
277 | DefDiagnosticKind::UnresolvedExternCrate { ast, .. } => { | ||
278 | let node = ast.to_node(self.upcast()); | ||
279 | (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedExternCrate") | ||
280 | } | ||
281 | DefDiagnosticKind::UnresolvedImport { ast, .. } => { | ||
282 | let node = ast.to_node(self.upcast()); | ||
283 | (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedImport") | ||
284 | } | ||
285 | DefDiagnosticKind::UnconfiguredCode { ast, .. } => { | ||
286 | let node = ast.to_node(self.upcast()); | ||
287 | (InFile::new(ast.file_id, node.syntax().clone()), "UnconfiguredCode") | ||
288 | } | ||
289 | DefDiagnosticKind::UnresolvedProcMacro { ast, .. } => { | ||
290 | (ast.to_node(self.upcast()), "UnresolvedProcMacro") | ||
291 | } | ||
292 | DefDiagnosticKind::UnresolvedMacroCall { ast, .. } => { | ||
293 | let node = ast.to_node(self.upcast()); | ||
294 | (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedMacroCall") | ||
295 | } | ||
296 | DefDiagnosticKind::MacroError { ast, message } => { | ||
297 | (ast.to_node(self.upcast()), message.as_str()) | ||
298 | } | ||
299 | }; | ||
300 | |||
301 | let frange = node.as_ref().original_file_range(self); | ||
302 | cb(frange, message.to_string()) | ||
303 | } | ||
273 | 304 | ||
305 | for (_module_id, module) in crate_def_map.modules() { | ||
274 | for decl in module.scope.declarations() { | 306 | for decl in module.scope.declarations() { |
275 | if let ModuleDefId::FunctionId(it) = decl { | 307 | if let ModuleDefId::FunctionId(it) = decl { |
276 | let source_map = self.body_with_source_map(it.into()).1; | 308 | let source_map = self.body_with_source_map(it.into()).1; |
277 | source_map.add_diagnostics(self, &mut sink); | 309 | for diag in source_map.diagnostics() { |
310 | let (ptr, message): (InFile<SyntaxNodePtr>, &str) = match diag { | ||
311 | BodyDiagnostic::InactiveCode { node, .. } => { | ||
312 | (node.clone().map(|it| it.into()), "InactiveCode") | ||
313 | } | ||
314 | BodyDiagnostic::MacroError { node, message } => { | ||
315 | (node.clone().map(|it| it.into()), message.as_str()) | ||
316 | } | ||
317 | BodyDiagnostic::UnresolvedProcMacro { node } => { | ||
318 | (node.clone().map(|it| it.into()), "UnresolvedProcMacro") | ||
319 | } | ||
320 | BodyDiagnostic::UnresolvedMacroCall { node, .. } => { | ||
321 | (node.clone().map(|it| it.into()), "UnresolvedMacroCall") | ||
322 | } | ||
323 | }; | ||
324 | |||
325 | let root = self.parse_or_expand(ptr.file_id).unwrap(); | ||
326 | let node = ptr.map(|ptr| ptr.to_node(&root)); | ||
327 | let frange = node.as_ref().original_file_range(self); | ||
328 | cb(frange, message.to_string()) | ||
329 | } | ||
278 | } | 330 | } |
279 | } | 331 | } |
280 | } | 332 | } |
@@ -287,14 +339,7 @@ impl TestDB { | |||
287 | assert!(!annotations.is_empty()); | 339 | assert!(!annotations.is_empty()); |
288 | 340 | ||
289 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | 341 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); |
290 | db.diagnostics(|d| { | 342 | db.diagnostics(&mut |frange, message| { |
291 | let src = d.display_source(); | ||
292 | let root = db.parse_or_expand(src.file_id).unwrap(); | ||
293 | |||
294 | let node = src.map(|ptr| ptr.to_node(&root)); | ||
295 | let frange = node.as_ref().original_file_range(db); | ||
296 | |||
297 | let message = d.message(); | ||
298 | actual.entry(frange.file_id).or_default().push((frange.range, message)); | 343 | actual.entry(frange.file_id).or_default().push((frange.range, message)); |
299 | }); | 344 | }); |
300 | 345 | ||
@@ -319,7 +364,7 @@ impl TestDB { | |||
319 | assert!(annotations.is_empty()); | 364 | assert!(annotations.is_empty()); |
320 | 365 | ||
321 | let mut has_diagnostics = false; | 366 | let mut has_diagnostics = false; |
322 | db.diagnostics(|_| { | 367 | db.diagnostics(&mut |_, _| { |
323 | has_diagnostics = true; | 368 | has_diagnostics = true; |
324 | }); | 369 | }); |
325 | 370 | ||
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 625c26f0a..e8f4af309 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -186,7 +186,7 @@ fn parse_macro_expansion( | |||
186 | // The final goal we would like to make all parse_macro success, | 186 | // The final goal we would like to make all parse_macro success, |
187 | // such that the following log will not call anyway. | 187 | // such that the following log will not call anyway. |
188 | let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); | 188 | let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); |
189 | let node = loc.kind.node(db); | 189 | let node = loc.kind.to_node(db); |
190 | 190 | ||
191 | // collect parent information for warning log | 191 | // collect parent information for warning log |
192 | let parents = | 192 | let parents = |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 6be4516a3..10d37234e 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -8,7 +8,6 @@ pub mod db; | |||
8 | pub mod ast_id_map; | 8 | pub mod ast_id_map; |
9 | pub mod name; | 9 | pub mod name; |
10 | pub mod hygiene; | 10 | pub mod hygiene; |
11 | pub mod diagnostics; | ||
12 | pub mod builtin_derive; | 11 | pub mod builtin_derive; |
13 | pub mod builtin_macro; | 12 | pub mod builtin_macro; |
14 | pub mod proc_macro; | 13 | pub mod proc_macro; |
@@ -108,7 +107,7 @@ impl HirFileId { | |||
108 | HirFileIdRepr::FileId(_) => None, | 107 | HirFileIdRepr::FileId(_) => None, |
109 | HirFileIdRepr::MacroFile(macro_file) => { | 108 | HirFileIdRepr::MacroFile(macro_file) => { |
110 | let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); | 109 | let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); |
111 | Some(loc.kind.node(db)) | 110 | Some(loc.kind.to_node(db)) |
112 | } | 111 | } |
113 | } | 112 | } |
114 | } | 113 | } |
@@ -153,7 +152,7 @@ impl HirFileId { | |||
153 | HirFileIdRepr::MacroFile(macro_file) => { | 152 | HirFileIdRepr::MacroFile(macro_file) => { |
154 | let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); | 153 | let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id); |
155 | let item = match loc.def.kind { | 154 | let item = match loc.def.kind { |
156 | MacroDefKind::BuiltInDerive(..) => loc.kind.node(db), | 155 | MacroDefKind::BuiltInDerive(..) => loc.kind.to_node(db), |
157 | _ => return None, | 156 | _ => return None, |
158 | }; | 157 | }; |
159 | Some(item.with_value(ast::Item::cast(item.value.clone())?)) | 158 | Some(item.with_value(ast::Item::cast(item.value.clone())?)) |
@@ -269,7 +268,7 @@ impl MacroCallKind { | |||
269 | } | 268 | } |
270 | } | 269 | } |
271 | 270 | ||
272 | fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> { | 271 | pub fn to_node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> { |
273 | match self { | 272 | match self { |
274 | MacroCallKind::FnLike { ast_id, .. } => { | 273 | MacroCallKind::FnLike { ast_id, .. } => { |
275 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) | 274 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) |
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index 84fc8ce14..7598e2193 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs | |||
@@ -8,12 +8,14 @@ use std::{any::Any, fmt}; | |||
8 | 8 | ||
9 | use base_db::CrateId; | 9 | use base_db::CrateId; |
10 | use hir_def::{DefWithBodyId, ModuleDefId}; | 10 | use hir_def::{DefWithBodyId, ModuleDefId}; |
11 | use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; | ||
12 | use hir_expand::{name::Name, HirFileId, InFile}; | 11 | use hir_expand::{name::Name, HirFileId, InFile}; |
13 | use stdx::format_to; | 12 | use stdx::format_to; |
14 | use syntax::{ast, AstPtr, SyntaxNodePtr}; | 13 | use syntax::{ast, AstPtr, SyntaxNodePtr}; |
15 | 14 | ||
16 | use crate::db::HirDatabase; | 15 | use crate::{ |
16 | db::HirDatabase, | ||
17 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink}, | ||
18 | }; | ||
17 | 19 | ||
18 | pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; | 20 | pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; |
19 | 21 | ||
@@ -446,15 +448,13 @@ impl Diagnostic for ReplaceFilterMapNextWithFindMap { | |||
446 | mod tests { | 448 | mod tests { |
447 | use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; | 449 | use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; |
448 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; | 450 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; |
449 | use hir_expand::{ | 451 | use hir_expand::db::AstDatabase; |
450 | db::AstDatabase, | ||
451 | diagnostics::{Diagnostic, DiagnosticSinkBuilder}, | ||
452 | }; | ||
453 | use rustc_hash::FxHashMap; | 452 | use rustc_hash::FxHashMap; |
454 | use syntax::{TextRange, TextSize}; | 453 | use syntax::{TextRange, TextSize}; |
455 | 454 | ||
456 | use crate::{ | 455 | use crate::{ |
457 | diagnostics::{validate_body, validate_module_item}, | 456 | diagnostics::{validate_body, validate_module_item}, |
457 | diagnostics_sink::{Diagnostic, DiagnosticSinkBuilder}, | ||
458 | test_db::TestDB, | 458 | test_db::TestDB, |
459 | }; | 459 | }; |
460 | 460 | ||
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs index 075dc4131..ef982cbcd 100644 --- a/crates/hir_ty/src/diagnostics/decl_check.rs +++ b/crates/hir_ty/src/diagnostics/decl_check.rs | |||
@@ -19,10 +19,7 @@ use hir_def::{ | |||
19 | src::HasSource, | 19 | src::HasSource, |
20 | AdtId, AttrDefId, ConstId, EnumId, FunctionId, Lookup, ModuleDefId, StaticId, StructId, | 20 | AdtId, AttrDefId, ConstId, EnumId, FunctionId, Lookup, ModuleDefId, StaticId, StructId, |
21 | }; | 21 | }; |
22 | use hir_expand::{ | 22 | use hir_expand::name::{AsName, Name}; |
23 | diagnostics::DiagnosticSink, | ||
24 | name::{AsName, Name}, | ||
25 | }; | ||
26 | use stdx::{always, never}; | 23 | use stdx::{always, never}; |
27 | use syntax::{ | 24 | use syntax::{ |
28 | ast::{self, NameOwner}, | 25 | ast::{self, NameOwner}, |
@@ -32,6 +29,7 @@ use syntax::{ | |||
32 | use crate::{ | 29 | use crate::{ |
33 | db::HirDatabase, | 30 | db::HirDatabase, |
34 | diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase}, | 31 | diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase}, |
32 | diagnostics_sink::DiagnosticSink, | ||
35 | }; | 33 | }; |
36 | 34 | ||
37 | mod allow { | 35 | mod allow { |
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index d1f113e7f..86f82e3fa 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -5,7 +5,7 @@ | |||
5 | use std::sync::Arc; | 5 | use std::sync::Arc; |
6 | 6 | ||
7 | use hir_def::{expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId}; | 7 | use hir_def::{expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId}; |
8 | use hir_expand::{diagnostics::DiagnosticSink, name}; | 8 | use hir_expand::name; |
9 | use rustc_hash::FxHashSet; | 9 | use rustc_hash::FxHashSet; |
10 | use syntax::{ast, AstPtr}; | 10 | use syntax::{ast, AstPtr}; |
11 | 11 | ||
@@ -16,6 +16,7 @@ use crate::{ | |||
16 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, | 16 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, |
17 | MissingPatFields, RemoveThisSemicolon, | 17 | MissingPatFields, RemoveThisSemicolon, |
18 | }, | 18 | }, |
19 | diagnostics_sink::DiagnosticSink, | ||
19 | AdtId, InferenceResult, Interner, TyExt, TyKind, | 20 | AdtId, InferenceResult, Interner, TyExt, TyKind, |
20 | }; | 21 | }; |
21 | 22 | ||
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs index 5d13bddea..c3c483425 100644 --- a/crates/hir_ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -9,10 +9,10 @@ use hir_def::{ | |||
9 | resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, | 9 | resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, |
10 | DefWithBodyId, | 10 | DefWithBodyId, |
11 | }; | 11 | }; |
12 | use hir_expand::diagnostics::DiagnosticSink; | ||
13 | 12 | ||
14 | use crate::{ | 13 | use crate::{ |
15 | db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Interner, TyExt, TyKind, | 14 | db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult, |
15 | Interner, TyExt, TyKind, | ||
16 | }; | 16 | }; |
17 | 17 | ||
18 | pub(super) struct UnsafeValidator<'a, 'b: 'a> { | 18 | pub(super) struct UnsafeValidator<'a, 'b: 'a> { |
diff --git a/crates/hir_expand/src/diagnostics.rs b/crates/hir_ty/src/diagnostics_sink.rs index bf0b85ce9..084fa8b06 100644 --- a/crates/hir_expand/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics_sink.rs | |||
@@ -16,10 +16,9 @@ | |||
16 | 16 | ||
17 | use std::{any::Any, fmt}; | 17 | use std::{any::Any, fmt}; |
18 | 18 | ||
19 | use hir_expand::InFile; | ||
19 | use syntax::SyntaxNodePtr; | 20 | use syntax::SyntaxNodePtr; |
20 | 21 | ||
21 | use crate::InFile; | ||
22 | |||
23 | #[derive(Copy, Clone, Debug, PartialEq)] | 22 | #[derive(Copy, Clone, Debug, PartialEq)] |
24 | pub struct DiagnosticCode(pub &'static str); | 23 | pub struct DiagnosticCode(pub &'static str); |
25 | 24 | ||
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 637bbc634..44f843bf3 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs | |||
@@ -778,8 +778,10 @@ fn write_bounds_like_dyn_trait( | |||
778 | } | 778 | } |
779 | WhereClause::AliasEq(alias_eq) if is_fn_trait => { | 779 | WhereClause::AliasEq(alias_eq) if is_fn_trait => { |
780 | is_fn_trait = false; | 780 | is_fn_trait = false; |
781 | write!(f, " -> ")?; | 781 | if !alias_eq.ty.is_unit() { |
782 | alias_eq.ty.hir_fmt(f)?; | 782 | write!(f, " -> ")?; |
783 | alias_eq.ty.hir_fmt(f)?; | ||
784 | } | ||
783 | } | 785 | } |
784 | WhereClause::AliasEq(AliasEq { ty, alias }) => { | 786 | WhereClause::AliasEq(AliasEq { ty, alias }) => { |
785 | // in types in actual Rust, these will always come | 787 | // in types in actual Rust, these will always come |
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index db3c937ff..8cefd80f3 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -28,13 +28,14 @@ use hir_def::{ | |||
28 | AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup, | 28 | AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup, |
29 | TraitId, TypeAliasId, VariantId, | 29 | TraitId, TypeAliasId, VariantId, |
30 | }; | 30 | }; |
31 | use hir_expand::{diagnostics::DiagnosticSink, name::name}; | 31 | use hir_expand::name::name; |
32 | use la_arena::ArenaMap; | 32 | use la_arena::ArenaMap; |
33 | use rustc_hash::FxHashMap; | 33 | use rustc_hash::FxHashMap; |
34 | use stdx::impl_from; | 34 | use stdx::impl_from; |
35 | use syntax::SmolStr; | 35 | use syntax::SmolStr; |
36 | 36 | ||
37 | use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; | 37 | use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; |
38 | use crate::diagnostics_sink::DiagnosticSink; | ||
38 | use crate::{ | 39 | use crate::{ |
39 | db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, | 40 | db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, |
40 | lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, | 41 | lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, |
@@ -578,9 +579,14 @@ impl<'a> InferenceContext<'a> { | |||
578 | } | 579 | } |
579 | 580 | ||
580 | fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> { | 581 | fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> { |
582 | // FIXME resolve via lang_item once try v2 is stable | ||
581 | let path = path![core::ops::Try]; | 583 | let path = path![core::ops::Try]; |
582 | let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; | 584 | let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; |
583 | self.db.trait_data(trait_).associated_type_by_name(&name![Ok]) | 585 | let trait_data = self.db.trait_data(trait_); |
586 | trait_data | ||
587 | // FIXME remove once try v2 is stable | ||
588 | .associated_type_by_name(&name![Ok]) | ||
589 | .or_else(|| trait_data.associated_type_by_name(&name![Output])) | ||
584 | } | 590 | } |
585 | 591 | ||
586 | fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> { | 592 | fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> { |
@@ -793,11 +799,11 @@ impl std::ops::BitOrAssign for Diverges { | |||
793 | 799 | ||
794 | mod diagnostics { | 800 | mod diagnostics { |
795 | use hir_def::{expr::ExprId, DefWithBodyId}; | 801 | use hir_def::{expr::ExprId, DefWithBodyId}; |
796 | use hir_expand::diagnostics::DiagnosticSink; | ||
797 | 802 | ||
798 | use crate::{ | 803 | use crate::{ |
799 | db::HirDatabase, | 804 | db::HirDatabase, |
800 | diagnostics::{BreakOutsideOfLoop, NoSuchField}, | 805 | diagnostics::{BreakOutsideOfLoop, NoSuchField}, |
806 | diagnostics_sink::DiagnosticSink, | ||
801 | }; | 807 | }; |
802 | 808 | ||
803 | #[derive(Debug, PartialEq, Eq, Clone)] | 809 | #[derive(Debug, PartialEq, Eq, Clone)] |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index eab8fac91..79a732106 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -805,7 +805,7 @@ impl<'a> InferenceContext<'a> { | |||
805 | None => self.table.new_float_var(), | 805 | None => self.table.new_float_var(), |
806 | }, | 806 | }, |
807 | }, | 807 | }, |
808 | Expr::MacroStmts { tail } => self.infer_expr(*tail, expected), | 808 | Expr::MacroStmts { tail } => self.infer_expr_inner(*tail, expected), |
809 | }; | 809 | }; |
810 | // use a new type variable if we got unknown here | 810 | // use a new type variable if we got unknown here |
811 | let ty = self.insert_type_vars_shallow(ty); | 811 | let ty = self.insert_type_vars_shallow(ty); |
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index ef021978a..50e0d6333 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -21,6 +21,7 @@ mod utils; | |||
21 | mod walk; | 21 | mod walk; |
22 | pub mod db; | 22 | pub mod db; |
23 | pub mod diagnostics; | 23 | pub mod diagnostics; |
24 | pub mod diagnostics_sink; | ||
24 | pub mod display; | 25 | pub mod display; |
25 | pub mod method_resolution; | 26 | pub mod method_resolution; |
26 | pub mod primitive; | 27 | pub mod primitive; |
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index bb568ea37..6dac7e103 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs | |||
@@ -832,11 +832,9 @@ fn coerce_unsize_super_trait_cycle() { | |||
832 | ); | 832 | ); |
833 | } | 833 | } |
834 | 834 | ||
835 | #[ignore] | ||
836 | #[test] | 835 | #[test] |
837 | fn coerce_unsize_generic() { | 836 | fn coerce_unsize_generic() { |
838 | // FIXME: Implement this | 837 | // FIXME: fix the type mismatches here |
839 | // https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions | ||
840 | check_infer_with_mismatches( | 838 | check_infer_with_mismatches( |
841 | r#" | 839 | r#" |
842 | #[lang = "unsize"] | 840 | #[lang = "unsize"] |
@@ -854,8 +852,58 @@ fn coerce_unsize_generic() { | |||
854 | let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); | 852 | let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); |
855 | } | 853 | } |
856 | "#, | 854 | "#, |
857 | expect![[r" | 855 | expect![[r#" |
858 | "]], | 856 | 209..317 '{ ... }); }': () |
857 | 219..220 '_': &Foo<[usize]> | ||
858 | 238..259 '&Foo {..., 3] }': &Foo<[usize]> | ||
859 | 239..259 'Foo { ..., 3] }': Foo<[usize]> | ||
860 | 248..257 '[1, 2, 3]': [usize; 3] | ||
861 | 249..250 '1': usize | ||
862 | 252..253 '2': usize | ||
863 | 255..256 '3': usize | ||
864 | 269..270 '_': &Bar<[usize]> | ||
865 | 288..314 '&Bar(F... 3] })': &Bar<[i32; 3]> | ||
866 | 289..292 'Bar': Bar<[i32; 3]>(Foo<[i32; 3]>) -> Bar<[i32; 3]> | ||
867 | 289..314 'Bar(Fo... 3] })': Bar<[i32; 3]> | ||
868 | 293..313 'Foo { ..., 3] }': Foo<[i32; 3]> | ||
869 | 302..311 '[1, 2, 3]': [i32; 3] | ||
870 | 303..304 '1': i32 | ||
871 | 306..307 '2': i32 | ||
872 | 309..310 '3': i32 | ||
873 | 248..257: expected [usize], got [usize; 3] | ||
874 | 288..314: expected &Bar<[usize]>, got &Bar<[i32; 3]> | ||
875 | "#]], | ||
876 | ); | ||
877 | } | ||
878 | |||
879 | #[test] | ||
880 | fn coerce_unsize_apit() { | ||
881 | // FIXME: #8984 | ||
882 | check_infer_with_mismatches( | ||
883 | r#" | ||
884 | #[lang = "sized"] | ||
885 | pub trait Sized {} | ||
886 | #[lang = "unsize"] | ||
887 | pub trait Unsize<T> {} | ||
888 | #[lang = "coerce_unsized"] | ||
889 | pub trait CoerceUnsized<T> {} | ||
890 | |||
891 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | ||
892 | |||
893 | trait Foo {} | ||
894 | |||
895 | fn test(f: impl Foo) { | ||
896 | let _: &dyn Foo = &f; | ||
897 | } | ||
898 | "#, | ||
899 | expect![[r#" | ||
900 | 210..211 'f': impl Foo | ||
901 | 223..252 '{ ... &f; }': () | ||
902 | 233..234 '_': &dyn Foo | ||
903 | 247..249 '&f': &impl Foo | ||
904 | 248..249 'f': impl Foo | ||
905 | 247..249: expected &dyn Foo, got &impl Foo | ||
906 | "#]], | ||
859 | ); | 907 | ); |
860 | } | 908 | } |
861 | 909 | ||
@@ -912,3 +960,46 @@ fn test() -> i32 { | |||
912 | "#, | 960 | "#, |
913 | ) | 961 | ) |
914 | } | 962 | } |
963 | |||
964 | #[test] | ||
965 | fn panic_macro() { | ||
966 | check_infer_with_mismatches( | ||
967 | r#" | ||
968 | mod panic { | ||
969 | #[macro_export] | ||
970 | pub macro panic_2015 { | ||
971 | () => ( | ||
972 | $crate::panicking::panic() | ||
973 | ), | ||
974 | } | ||
975 | } | ||
976 | |||
977 | mod panicking { | ||
978 | pub fn panic() -> ! { loop {} } | ||
979 | } | ||
980 | |||
981 | #[rustc_builtin_macro = "core_panic"] | ||
982 | macro_rules! panic { | ||
983 | // Expands to either `$crate::panic::panic_2015` or `$crate::panic::panic_2021` | ||
984 | // depending on the edition of the caller. | ||
985 | ($($arg:tt)*) => { | ||
986 | /* compiler built-in */ | ||
987 | }; | ||
988 | } | ||
989 | |||
990 | fn main() { | ||
991 | panic!() | ||
992 | } | ||
993 | "#, | ||
994 | expect![[r#" | ||
995 | 174..185 '{ loop {} }': ! | ||
996 | 176..183 'loop {}': ! | ||
997 | 181..183 '{}': () | ||
998 | !0..24 '$crate...:panic': fn panic() -> ! | ||
999 | !0..26 '$crate...anic()': ! | ||
1000 | !0..26 '$crate...anic()': ! | ||
1001 | !0..28 '$crate...015!()': ! | ||
1002 | 454..470 '{ ...c!() }': () | ||
1003 | "#]], | ||
1004 | ); | ||
1005 | } | ||
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index a5a2df54c..6ad96bfe3 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs | |||
@@ -161,6 +161,43 @@ mod result { | |||
161 | } | 161 | } |
162 | 162 | ||
163 | #[test] | 163 | #[test] |
164 | fn infer_tryv2() { | ||
165 | check_types( | ||
166 | r#" | ||
167 | //- /main.rs crate:main deps:core | ||
168 | fn test() { | ||
169 | let r: Result<i32, u64> = Result::Ok(1); | ||
170 | let v = r?; | ||
171 | v; | ||
172 | } //^ i32 | ||
173 | |||
174 | //- /core.rs crate:core | ||
175 | #[prelude_import] use ops::*; | ||
176 | mod ops { | ||
177 | trait Try { | ||
178 | type Output; | ||
179 | type Residual; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | #[prelude_import] use result::*; | ||
184 | mod result { | ||
185 | enum Infallible {} | ||
186 | enum Result<O, E> { | ||
187 | Ok(O), | ||
188 | Err(E) | ||
189 | } | ||
190 | |||
191 | impl<O, E> crate::ops::Try for Result<O, E> { | ||
192 | type Output = O; | ||
193 | type Error = Result<Infallible, E>; | ||
194 | } | ||
195 | } | ||
196 | "#, | ||
197 | ); | ||
198 | } | ||
199 | |||
200 | #[test] | ||
164 | fn infer_for_loop() { | 201 | fn infer_for_loop() { |
165 | check_types( | 202 | check_types( |
166 | r#" | 203 | r#" |
@@ -3041,7 +3078,7 @@ fn infer_fn_trait_arg() { | |||
3041 | 3078 | ||
3042 | #[test] | 3079 | #[test] |
3043 | fn infer_box_fn_arg() { | 3080 | fn infer_box_fn_arg() { |
3044 | // The type mismatch is a bug | 3081 | // The type mismatch is because we don't define Unsize and CoerceUnsized |
3045 | check_infer_with_mismatches( | 3082 | check_infer_with_mismatches( |
3046 | r#" | 3083 | r#" |
3047 | //- /lib.rs deps:std | 3084 | //- /lib.rs deps:std |
@@ -3101,7 +3138,7 @@ fn foo() { | |||
3101 | 555..557 'ps': {unknown} | 3138 | 555..557 'ps': {unknown} |
3102 | 559..561 '{}': () | 3139 | 559..561 '{}': () |
3103 | 568..569 'f': Box<dyn FnOnce(&Option<i32>)> | 3140 | 568..569 'f': Box<dyn FnOnce(&Option<i32>)> |
3104 | 568..573 'f(&s)': FnOnce::Output<dyn FnOnce(&Option<i32>), (&Option<i32>,)> | 3141 | 568..573 'f(&s)': () |
3105 | 570..572 '&s': &Option<i32> | 3142 | 570..572 '&s': &Option<i32> |
3106 | 571..572 's': Option<i32> | 3143 | 571..572 's': Option<i32> |
3107 | 549..562: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|{unknown}| -> ()> | 3144 | 549..562: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|{unknown}| -> ()> |
@@ -3571,3 +3608,55 @@ fn main() { | |||
3571 | "#]], | 3608 | "#]], |
3572 | ) | 3609 | ) |
3573 | } | 3610 | } |
3611 | |||
3612 | #[test] | ||
3613 | fn fn_returning_unit() { | ||
3614 | check_infer_with_mismatches( | ||
3615 | r#" | ||
3616 | #[lang = "fn_once"] | ||
3617 | trait FnOnce<Args> { | ||
3618 | type Output; | ||
3619 | } | ||
3620 | |||
3621 | fn test<F: FnOnce()>(f: F) { | ||
3622 | let _: () = f(); | ||
3623 | }"#, | ||
3624 | expect![[r#" | ||
3625 | 82..83 'f': F | ||
3626 | 88..112 '{ ...f(); }': () | ||
3627 | 98..99 '_': () | ||
3628 | 106..107 'f': F | ||
3629 | 106..109 'f()': () | ||
3630 | "#]], | ||
3631 | ); | ||
3632 | } | ||
3633 | |||
3634 | #[test] | ||
3635 | fn trait_in_scope_of_trait_impl() { | ||
3636 | check_infer( | ||
3637 | r#" | ||
3638 | mod foo { | ||
3639 | pub trait Foo { | ||
3640 | fn foo(self); | ||
3641 | fn bar(self) -> usize { 0 } | ||
3642 | } | ||
3643 | } | ||
3644 | impl foo::Foo for u32 { | ||
3645 | fn foo(self) { | ||
3646 | let _x = self.bar(); | ||
3647 | } | ||
3648 | } | ||
3649 | "#, | ||
3650 | expect![[r#" | ||
3651 | 45..49 'self': Self | ||
3652 | 67..71 'self': Self | ||
3653 | 82..87 '{ 0 }': usize | ||
3654 | 84..85 '0': usize | ||
3655 | 131..135 'self': u32 | ||
3656 | 137..173 '{ ... }': () | ||
3657 | 151..153 '_x': usize | ||
3658 | 156..160 'self': u32 | ||
3659 | 156..166 'self.bar()': usize | ||
3660 | "#]], | ||
3661 | ); | ||
3662 | } | ||
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 27d347dbd..dcac7c76d 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -299,10 +299,10 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist { | |||
299 | 299 | ||
300 | #[cfg(test)] | 300 | #[cfg(test)] |
301 | mod tests { | 301 | mod tests { |
302 | use expect_test::{expect, Expect}; | 302 | use expect_test::Expect; |
303 | use ide_assists::AssistResolveStrategy; | 303 | use ide_assists::AssistResolveStrategy; |
304 | use stdx::trim_indent; | 304 | use stdx::trim_indent; |
305 | use test_utils::assert_eq_text; | 305 | use test_utils::{assert_eq_text, extract_annotations}; |
306 | 306 | ||
307 | use crate::{fixture, DiagnosticsConfig}; | 307 | use crate::{fixture, DiagnosticsConfig}; |
308 | 308 | ||
@@ -396,26 +396,51 @@ mod tests { | |||
396 | expect.assert_debug_eq(&diagnostics) | 396 | expect.assert_debug_eq(&diagnostics) |
397 | } | 397 | } |
398 | 398 | ||
399 | pub(crate) fn check_diagnostics(ra_fixture: &str) { | ||
400 | let (analysis, file_id) = fixture::file(ra_fixture); | ||
401 | let diagnostics = analysis | ||
402 | .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id) | ||
403 | .unwrap(); | ||
404 | |||
405 | let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); | ||
406 | let actual = diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>(); | ||
407 | assert_eq!(expected, actual); | ||
408 | } | ||
409 | |||
399 | #[test] | 410 | #[test] |
400 | fn test_unresolved_macro_range() { | 411 | fn test_unresolved_macro_range() { |
401 | check_expect( | 412 | check_diagnostics( |
402 | r#"foo::bar!(92);"#, | 413 | r#" |
403 | expect![[r#" | 414 | foo::bar!(92); |
404 | [ | 415 | //^^^ unresolved macro `foo::bar!` |
405 | Diagnostic { | 416 | "#, |
406 | message: "unresolved macro `foo::bar!`", | 417 | ); |
407 | range: 5..8, | 418 | } |
408 | severity: Error, | 419 | |
409 | fixes: None, | 420 | #[test] |
410 | unused: false, | 421 | fn unresolved_import_in_use_tree() { |
411 | code: Some( | 422 | // Only the relevant part of a nested `use` item should be highlighted. |
412 | DiagnosticCode( | 423 | check_diagnostics( |
413 | "unresolved-macro-call", | 424 | r#" |
414 | ), | 425 | use does_exist::{Exists, DoesntExist}; |
415 | ), | 426 | //^^^^^^^^^^^ unresolved import |
416 | }, | 427 | |
417 | ] | 428 | use {does_not_exist::*, does_exist}; |
418 | "#]], | 429 | //^^^^^^^^^^^^^^^^^ unresolved import |
430 | |||
431 | use does_not_exist::{ | ||
432 | a, | ||
433 | //^ unresolved import | ||
434 | b, | ||
435 | //^ unresolved import | ||
436 | c, | ||
437 | //^ unresolved import | ||
438 | }; | ||
439 | |||
440 | mod does_exist { | ||
441 | pub struct Exists; | ||
442 | } | ||
443 | "#, | ||
419 | ); | 444 | ); |
420 | } | 445 | } |
421 | 446 | ||
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 05130a237..43356a94e 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use hir::{Impl, Semantics}; | 1 | use hir::{AsAssocItem, Impl, Semantics}; |
2 | use ide_db::{ | 2 | use ide_db::{ |
3 | defs::{Definition, NameClass, NameRefClass}, | 3 | defs::{Definition, NameClass, NameRefClass}, |
4 | RootDatabase, | 4 | RootDatabase, |
@@ -36,6 +36,7 @@ pub(crate) fn goto_implementation( | |||
36 | } | 36 | } |
37 | ast::NameLike::Lifetime(_) => None, | 37 | ast::NameLike::Lifetime(_) => None, |
38 | }?; | 38 | }?; |
39 | |||
39 | let def = match def { | 40 | let def = match def { |
40 | Definition::ModuleDef(def) => def, | 41 | Definition::ModuleDef(def) => def, |
41 | _ => return None, | 42 | _ => return None, |
@@ -48,6 +49,18 @@ pub(crate) fn goto_implementation( | |||
48 | let module = sema.to_module_def(position.file_id)?; | 49 | let module = sema.to_module_def(position.file_id)?; |
49 | impls_for_ty(&sema, builtin.ty(sema.db, module)) | 50 | impls_for_ty(&sema, builtin.ty(sema.db, module)) |
50 | } | 51 | } |
52 | hir::ModuleDef::Function(f) => { | ||
53 | let assoc = f.as_assoc_item(sema.db)?; | ||
54 | let name = assoc.name(sema.db)?; | ||
55 | let trait_ = assoc.containing_trait(sema.db)?; | ||
56 | impls_for_trait_item(&sema, trait_, name) | ||
57 | } | ||
58 | hir::ModuleDef::Const(c) => { | ||
59 | let assoc = c.as_assoc_item(sema.db)?; | ||
60 | let name = assoc.name(sema.db)?; | ||
61 | let trait_ = assoc.containing_trait(sema.db)?; | ||
62 | impls_for_trait_item(&sema, trait_, name) | ||
63 | } | ||
51 | _ => return None, | 64 | _ => return None, |
52 | }; | 65 | }; |
53 | Some(RangeInfo { range: node.syntax().text_range(), info: navs }) | 66 | Some(RangeInfo { range: node.syntax().text_range(), info: navs }) |
@@ -64,6 +77,23 @@ fn impls_for_trait(sema: &Semantics<RootDatabase>, trait_: hir::Trait) -> Vec<Na | |||
64 | .collect() | 77 | .collect() |
65 | } | 78 | } |
66 | 79 | ||
80 | fn impls_for_trait_item( | ||
81 | sema: &Semantics<RootDatabase>, | ||
82 | trait_: hir::Trait, | ||
83 | fun_name: hir::Name, | ||
84 | ) -> Vec<NavigationTarget> { | ||
85 | Impl::all_for_trait(sema.db, trait_) | ||
86 | .into_iter() | ||
87 | .filter_map(|imp| { | ||
88 | let item = imp.items(sema.db).iter().find_map(|itm| { | ||
89 | let itm_name = itm.name(sema.db)?; | ||
90 | (itm_name == fun_name).then(|| itm.clone()) | ||
91 | })?; | ||
92 | item.try_to_nav(sema.db) | ||
93 | }) | ||
94 | .collect() | ||
95 | } | ||
96 | |||
67 | #[cfg(test)] | 97 | #[cfg(test)] |
68 | mod tests { | 98 | mod tests { |
69 | use ide_db::base_db::FileRange; | 99 | use ide_db::base_db::FileRange; |
@@ -262,4 +292,42 @@ impl bool {} | |||
262 | "#, | 292 | "#, |
263 | ); | 293 | ); |
264 | } | 294 | } |
295 | |||
296 | #[test] | ||
297 | fn goto_implementation_trait_functions() { | ||
298 | check( | ||
299 | r#" | ||
300 | trait Tr { | ||
301 | fn f$0(); | ||
302 | } | ||
303 | |||
304 | struct S; | ||
305 | |||
306 | impl Tr for S { | ||
307 | fn f() { | ||
308 | //^ | ||
309 | println!("Hello, world!"); | ||
310 | } | ||
311 | } | ||
312 | "#, | ||
313 | ); | ||
314 | } | ||
315 | |||
316 | #[test] | ||
317 | fn goto_implementation_trait_assoc_const() { | ||
318 | check( | ||
319 | r#" | ||
320 | trait Tr { | ||
321 | const C$0: usize; | ||
322 | } | ||
323 | |||
324 | struct S; | ||
325 | |||
326 | impl Tr for S { | ||
327 | const C: usize = 4; | ||
328 | //^ | ||
329 | } | ||
330 | "#, | ||
331 | ); | ||
332 | } | ||
265 | } | 333 | } |
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index f3284bb96..004d9cb68 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs | |||
@@ -1,3 +1,4 @@ | |||
1 | use ide_db::base_db::Upcast; | ||
1 | use ide_db::RootDatabase; | 2 | use ide_db::RootDatabase; |
2 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | 3 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; |
3 | 4 | ||
@@ -31,6 +32,7 @@ pub(crate) fn goto_type_definition( | |||
31 | ast::Pat(it) => sema.type_of_pat(&it)?, | 32 | ast::Pat(it) => sema.type_of_pat(&it)?, |
32 | ast::SelfParam(it) => sema.type_of_self(&it)?, | 33 | ast::SelfParam(it) => sema.type_of_self(&it)?, |
33 | ast::Type(it) => sema.resolve_type(&it)?, | 34 | ast::Type(it) => sema.resolve_type(&it)?, |
35 | ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?, | ||
34 | _ => return None, | 36 | _ => return None, |
35 | } | 37 | } |
36 | }; | 38 | }; |
@@ -161,4 +163,34 @@ impl Foo$0 {} | |||
161 | "#, | 163 | "#, |
162 | ) | 164 | ) |
163 | } | 165 | } |
166 | |||
167 | #[test] | ||
168 | fn goto_def_for_struct_field() { | ||
169 | check( | ||
170 | r#" | ||
171 | struct Bar; | ||
172 | //^^^ | ||
173 | |||
174 | struct Foo { | ||
175 | bar$0: Bar, | ||
176 | } | ||
177 | "#, | ||
178 | ); | ||
179 | } | ||
180 | |||
181 | #[test] | ||
182 | fn goto_def_for_enum_struct_field() { | ||
183 | check( | ||
184 | r#" | ||
185 | struct Bar; | ||
186 | //^^^ | ||
187 | |||
188 | enum Foo { | ||
189 | Bar { | ||
190 | bar$0: Bar | ||
191 | }, | ||
192 | } | ||
193 | "#, | ||
194 | ); | ||
195 | } | ||
164 | } | 196 | } |
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs index 2f95b8687..40006fb74 100644 --- a/crates/ide_completion/src/completions/record.rs +++ b/crates/ide_completion/src/completions/record.rs | |||
@@ -108,8 +108,6 @@ fn process(f: S) { | |||
108 | check_snippet( | 108 | check_snippet( |
109 | test_code, | 109 | test_code, |
110 | expect![[r#" | 110 | expect![[r#" |
111 | sn pd | ||
112 | sn ppd | ||
113 | fd ..Default::default() | 111 | fd ..Default::default() |
114 | "#]], | 112 | "#]], |
115 | ); | 113 | ); |
@@ -179,13 +177,7 @@ fn process(f: S) { | |||
179 | "#]], | 177 | "#]], |
180 | ); | 178 | ); |
181 | 179 | ||
182 | check_snippet( | 180 | check_snippet(test_code, expect![[r#""#]]); |
183 | test_code, | ||
184 | expect![[r#" | ||
185 | sn pd | ||
186 | sn ppd | ||
187 | "#]], | ||
188 | ); | ||
189 | } | 181 | } |
190 | 182 | ||
191 | #[test] | 183 | #[test] |
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs index 7f7830976..14cfb61de 100644 --- a/crates/ide_completion/src/completions/snippet.rs +++ b/crates/ide_completion/src/completions/snippet.rs | |||
@@ -22,8 +22,10 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte | |||
22 | None => return, | 22 | None => return, |
23 | }; | 23 | }; |
24 | 24 | ||
25 | snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); | 25 | if ctx.can_be_stmt { |
26 | snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); | 26 | snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); |
27 | snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); | ||
28 | } | ||
27 | } | 29 | } |
28 | 30 | ||
29 | pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) { | 31 | pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) { |
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index b2317618a..4d88932ca 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! Advertizes the capabilities of the LSP Server. | 1 | //! Advertises the capabilities of the LSP Server. |
2 | use std::env; | 2 | use std::env; |
3 | 3 | ||
4 | use lsp_types::{ | 4 | use lsp_types::{ |
@@ -54,7 +54,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti | |||
54 | code_action_provider: Some(code_action_capabilities(client_caps)), | 54 | code_action_provider: Some(code_action_capabilities(client_caps)), |
55 | code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), | 55 | code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), |
56 | document_formatting_provider: Some(OneOf::Left(true)), | 56 | document_formatting_provider: Some(OneOf::Left(true)), |
57 | document_range_formatting_provider: None, | 57 | document_range_formatting_provider: Some(OneOf::Left(true)), |
58 | document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { | 58 | document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { |
59 | first_trigger_character: "=".to_string(), | 59 | first_trigger_character: "=".to_string(), |
60 | more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]), | 60 | more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]), |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 7c02a507c..7620a2fe1 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -218,6 +218,10 @@ config_data! { | |||
218 | /// Advanced option, fully override the command rust-analyzer uses for | 218 | /// Advanced option, fully override the command rust-analyzer uses for |
219 | /// formatting. | 219 | /// formatting. |
220 | rustfmt_overrideCommand: Option<Vec<String>> = "null", | 220 | rustfmt_overrideCommand: Option<Vec<String>> = "null", |
221 | /// Enables the use of rustfmt's unstable range formatting command for the | ||
222 | /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only | ||
223 | /// available on a nightly build. | ||
224 | rustfmt_enableRangeFormatting: bool = "false", | ||
221 | 225 | ||
222 | /// Workspace symbol search scope. | 226 | /// Workspace symbol search scope. |
223 | workspace_symbol_search_scope: WorskpaceSymbolSearchScopeDef = "\"workspace\"", | 227 | workspace_symbol_search_scope: WorskpaceSymbolSearchScopeDef = "\"workspace\"", |
@@ -305,7 +309,7 @@ pub struct NotificationsConfig { | |||
305 | 309 | ||
306 | #[derive(Debug, Clone)] | 310 | #[derive(Debug, Clone)] |
307 | pub enum RustfmtConfig { | 311 | pub enum RustfmtConfig { |
308 | Rustfmt { extra_args: Vec<String> }, | 312 | Rustfmt { extra_args: Vec<String>, enable_range_formatting: bool }, |
309 | CustomCommand { command: String, args: Vec<String> }, | 313 | CustomCommand { command: String, args: Vec<String> }, |
310 | } | 314 | } |
311 | 315 | ||
@@ -584,9 +588,10 @@ impl Config { | |||
584 | let command = args.remove(0); | 588 | let command = args.remove(0); |
585 | RustfmtConfig::CustomCommand { command, args } | 589 | RustfmtConfig::CustomCommand { command, args } |
586 | } | 590 | } |
587 | Some(_) | None => { | 591 | Some(_) | None => RustfmtConfig::Rustfmt { |
588 | RustfmtConfig::Rustfmt { extra_args: self.data.rustfmt_extraArgs.clone() } | 592 | extra_args: self.data.rustfmt_extraArgs.clone(), |
589 | } | 593 | enable_range_formatting: self.data.rustfmt_enableRangeFormatting, |
594 | }, | ||
590 | } | 595 | } |
591 | } | 596 | } |
592 | pub fn flycheck(&self) -> Option<FlycheckConfig> { | 597 | pub fn flycheck(&self) -> Option<FlycheckConfig> { |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index f48210424..456744603 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -27,7 +27,7 @@ use lsp_types::{ | |||
27 | }; | 27 | }; |
28 | use project_model::TargetKind; | 28 | use project_model::TargetKind; |
29 | use serde::{Deserialize, Serialize}; | 29 | use serde::{Deserialize, Serialize}; |
30 | use serde_json::to_value; | 30 | use serde_json::{json, to_value}; |
31 | use stdx::format_to; | 31 | use stdx::format_to; |
32 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; | 32 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; |
33 | 33 | ||
@@ -955,104 +955,17 @@ pub(crate) fn handle_formatting( | |||
955 | params: DocumentFormattingParams, | 955 | params: DocumentFormattingParams, |
956 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { | 956 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { |
957 | let _p = profile::span("handle_formatting"); | 957 | let _p = profile::span("handle_formatting"); |
958 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | ||
959 | let file = snap.analysis.file_text(file_id)?; | ||
960 | let crate_ids = snap.analysis.crate_for(file_id)?; | ||
961 | |||
962 | let line_index = snap.file_line_index(file_id)?; | ||
963 | |||
964 | let mut rustfmt = match snap.config.rustfmt() { | ||
965 | RustfmtConfig::Rustfmt { extra_args } => { | ||
966 | let mut cmd = process::Command::new(toolchain::rustfmt()); | ||
967 | cmd.args(extra_args); | ||
968 | // try to chdir to the file so we can respect `rustfmt.toml` | ||
969 | // FIXME: use `rustfmt --config-path` once | ||
970 | // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed | ||
971 | match params.text_document.uri.to_file_path() { | ||
972 | Ok(mut path) => { | ||
973 | // pop off file name | ||
974 | if path.pop() && path.is_dir() { | ||
975 | cmd.current_dir(path); | ||
976 | } | ||
977 | } | ||
978 | Err(_) => { | ||
979 | log::error!( | ||
980 | "Unable to get file path for {}, rustfmt.toml might be ignored", | ||
981 | params.text_document.uri | ||
982 | ); | ||
983 | } | ||
984 | } | ||
985 | if let Some(&crate_id) = crate_ids.first() { | ||
986 | // Assume all crates are in the same edition | ||
987 | let edition = snap.analysis.crate_edition(crate_id)?; | ||
988 | cmd.arg("--edition"); | ||
989 | cmd.arg(edition.to_string()); | ||
990 | } | ||
991 | cmd | ||
992 | } | ||
993 | RustfmtConfig::CustomCommand { command, args } => { | ||
994 | let mut cmd = process::Command::new(command); | ||
995 | cmd.args(args); | ||
996 | cmd | ||
997 | } | ||
998 | }; | ||
999 | 958 | ||
1000 | let mut rustfmt = | 959 | run_rustfmt(&snap, params.text_document, None) |
1001 | rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?; | 960 | } |
1002 | |||
1003 | rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; | ||
1004 | |||
1005 | let output = rustfmt.wait_with_output()?; | ||
1006 | let captured_stdout = String::from_utf8(output.stdout)?; | ||
1007 | let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default(); | ||
1008 | |||
1009 | if !output.status.success() { | ||
1010 | let rustfmt_not_installed = | ||
1011 | captured_stderr.contains("not installed") || captured_stderr.contains("not available"); | ||
1012 | |||
1013 | return match output.status.code() { | ||
1014 | Some(1) if !rustfmt_not_installed => { | ||
1015 | // While `rustfmt` doesn't have a specific exit code for parse errors this is the | ||
1016 | // likely cause exiting with 1. Most Language Servers swallow parse errors on | ||
1017 | // formatting because otherwise an error is surfaced to the user on top of the | ||
1018 | // syntax error diagnostics they're already receiving. This is especially jarring | ||
1019 | // if they have format on save enabled. | ||
1020 | log::info!("rustfmt exited with status 1, assuming parse error and ignoring"); | ||
1021 | Ok(None) | ||
1022 | } | ||
1023 | _ => { | ||
1024 | // Something else happened - e.g. `rustfmt` is missing or caught a signal | ||
1025 | Err(LspError::new( | ||
1026 | -32900, | ||
1027 | format!( | ||
1028 | r#"rustfmt exited with: | ||
1029 | Status: {} | ||
1030 | stdout: {} | ||
1031 | stderr: {}"#, | ||
1032 | output.status, captured_stdout, captured_stderr, | ||
1033 | ), | ||
1034 | ) | ||
1035 | .into()) | ||
1036 | } | ||
1037 | }; | ||
1038 | } | ||
1039 | 961 | ||
1040 | let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout); | 962 | pub(crate) fn handle_range_formatting( |
963 | snap: GlobalStateSnapshot, | ||
964 | params: lsp_types::DocumentRangeFormattingParams, | ||
965 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { | ||
966 | let _p = profile::span("handle_range_formatting"); | ||
1041 | 967 | ||
1042 | if line_index.endings != new_line_endings { | 968 | run_rustfmt(&snap, params.text_document, Some(params.range)) |
1043 | // If line endings are different, send the entire file. | ||
1044 | // Diffing would not work here, as the line endings might be the only | ||
1045 | // difference. | ||
1046 | Ok(Some(to_proto::text_edit_vec( | ||
1047 | &line_index, | ||
1048 | TextEdit::replace(TextRange::up_to(TextSize::of(&*file)), new_text), | ||
1049 | ))) | ||
1050 | } else if *file == new_text { | ||
1051 | // The document is already formatted correctly -- no edits needed. | ||
1052 | Ok(None) | ||
1053 | } else { | ||
1054 | Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text)))) | ||
1055 | } | ||
1056 | } | 969 | } |
1057 | 970 | ||
1058 | pub(crate) fn handle_code_action( | 971 | pub(crate) fn handle_code_action( |
@@ -1675,6 +1588,140 @@ fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) | |||
1675 | } | 1588 | } |
1676 | } | 1589 | } |
1677 | 1590 | ||
1591 | fn run_rustfmt( | ||
1592 | snap: &GlobalStateSnapshot, | ||
1593 | text_document: TextDocumentIdentifier, | ||
1594 | range: Option<lsp_types::Range>, | ||
1595 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { | ||
1596 | let file_id = from_proto::file_id(&snap, &text_document.uri)?; | ||
1597 | let file = snap.analysis.file_text(file_id)?; | ||
1598 | let crate_ids = snap.analysis.crate_for(file_id)?; | ||
1599 | |||
1600 | let line_index = snap.file_line_index(file_id)?; | ||
1601 | |||
1602 | let mut rustfmt = match snap.config.rustfmt() { | ||
1603 | RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => { | ||
1604 | let mut cmd = process::Command::new(toolchain::rustfmt()); | ||
1605 | cmd.args(extra_args); | ||
1606 | // try to chdir to the file so we can respect `rustfmt.toml` | ||
1607 | // FIXME: use `rustfmt --config-path` once | ||
1608 | // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed | ||
1609 | match text_document.uri.to_file_path() { | ||
1610 | Ok(mut path) => { | ||
1611 | // pop off file name | ||
1612 | if path.pop() && path.is_dir() { | ||
1613 | cmd.current_dir(path); | ||
1614 | } | ||
1615 | } | ||
1616 | Err(_) => { | ||
1617 | log::error!( | ||
1618 | "Unable to get file path for {}, rustfmt.toml might be ignored", | ||
1619 | text_document.uri | ||
1620 | ); | ||
1621 | } | ||
1622 | } | ||
1623 | if let Some(&crate_id) = crate_ids.first() { | ||
1624 | // Assume all crates are in the same edition | ||
1625 | let edition = snap.analysis.crate_edition(crate_id)?; | ||
1626 | cmd.arg("--edition"); | ||
1627 | cmd.arg(edition.to_string()); | ||
1628 | } | ||
1629 | |||
1630 | if let Some(range) = range { | ||
1631 | if !enable_range_formatting { | ||
1632 | return Err(LspError::new( | ||
1633 | ErrorCode::InvalidRequest as i32, | ||
1634 | String::from( | ||
1635 | "rustfmt range formatting is unstable. \ | ||
1636 | Opt-in by using a nightly build of rustfmt and setting \ | ||
1637 | `rustfmt.enableRangeFormatting` to true in your LSP configuration", | ||
1638 | ), | ||
1639 | ) | ||
1640 | .into()); | ||
1641 | } | ||
1642 | |||
1643 | let frange = from_proto::file_range(&snap, text_document.clone(), range)?; | ||
1644 | let start_line = line_index.index.line_col(frange.range.start()).line; | ||
1645 | let end_line = line_index.index.line_col(frange.range.end()).line; | ||
1646 | |||
1647 | cmd.arg("--unstable-features"); | ||
1648 | cmd.arg("--file-lines"); | ||
1649 | cmd.arg( | ||
1650 | json!([{ | ||
1651 | "file": "stdin", | ||
1652 | "range": [start_line, end_line] | ||
1653 | }]) | ||
1654 | .to_string(), | ||
1655 | ); | ||
1656 | } | ||
1657 | |||
1658 | cmd | ||
1659 | } | ||
1660 | RustfmtConfig::CustomCommand { command, args } => { | ||
1661 | let mut cmd = process::Command::new(command); | ||
1662 | cmd.args(args); | ||
1663 | cmd | ||
1664 | } | ||
1665 | }; | ||
1666 | |||
1667 | let mut rustfmt = | ||
1668 | rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?; | ||
1669 | |||
1670 | rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; | ||
1671 | |||
1672 | let output = rustfmt.wait_with_output()?; | ||
1673 | let captured_stdout = String::from_utf8(output.stdout)?; | ||
1674 | let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default(); | ||
1675 | |||
1676 | if !output.status.success() { | ||
1677 | let rustfmt_not_installed = | ||
1678 | captured_stderr.contains("not installed") || captured_stderr.contains("not available"); | ||
1679 | |||
1680 | return match output.status.code() { | ||
1681 | Some(1) if !rustfmt_not_installed => { | ||
1682 | // While `rustfmt` doesn't have a specific exit code for parse errors this is the | ||
1683 | // likely cause exiting with 1. Most Language Servers swallow parse errors on | ||
1684 | // formatting because otherwise an error is surfaced to the user on top of the | ||
1685 | // syntax error diagnostics they're already receiving. This is especially jarring | ||
1686 | // if they have format on save enabled. | ||
1687 | log::info!("rustfmt exited with status 1, assuming parse error and ignoring"); | ||
1688 | Ok(None) | ||
1689 | } | ||
1690 | _ => { | ||
1691 | // Something else happened - e.g. `rustfmt` is missing or caught a signal | ||
1692 | Err(LspError::new( | ||
1693 | -32900, | ||
1694 | format!( | ||
1695 | r#"rustfmt exited with: | ||
1696 | Status: {} | ||
1697 | stdout: {} | ||
1698 | stderr: {}"#, | ||
1699 | output.status, captured_stdout, captured_stderr, | ||
1700 | ), | ||
1701 | ) | ||
1702 | .into()) | ||
1703 | } | ||
1704 | }; | ||
1705 | } | ||
1706 | |||
1707 | let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout); | ||
1708 | |||
1709 | if line_index.endings != new_line_endings { | ||
1710 | // If line endings are different, send the entire file. | ||
1711 | // Diffing would not work here, as the line endings might be the only | ||
1712 | // difference. | ||
1713 | Ok(Some(to_proto::text_edit_vec( | ||
1714 | &line_index, | ||
1715 | TextEdit::replace(TextRange::up_to(TextSize::of(&*file)), new_text), | ||
1716 | ))) | ||
1717 | } else if *file == new_text { | ||
1718 | // The document is already formatted correctly -- no edits needed. | ||
1719 | Ok(None) | ||
1720 | } else { | ||
1721 | Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text)))) | ||
1722 | } | ||
1723 | } | ||
1724 | |||
1678 | #[derive(Debug, Serialize, Deserialize)] | 1725 | #[derive(Debug, Serialize, Deserialize)] |
1679 | struct CompletionResolveData { | 1726 | struct CompletionResolveData { |
1680 | position: lsp_types::TextDocumentPositionParams, | 1727 | position: lsp_types::TextDocumentPositionParams, |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index cb002f700..008758ea0 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -543,6 +543,7 @@ impl GlobalState { | |||
543 | .on::<lsp_types::request::Rename>(handlers::handle_rename) | 543 | .on::<lsp_types::request::Rename>(handlers::handle_rename) |
544 | .on::<lsp_types::request::References>(handlers::handle_references) | 544 | .on::<lsp_types::request::References>(handlers::handle_references) |
545 | .on::<lsp_types::request::Formatting>(handlers::handle_formatting) | 545 | .on::<lsp_types::request::Formatting>(handlers::handle_formatting) |
546 | .on::<lsp_types::request::RangeFormatting>(handlers::handle_range_formatting) | ||
546 | .on::<lsp_types::request::DocumentHighlightRequest>(handlers::handle_document_highlight) | 547 | .on::<lsp_types::request::DocumentHighlightRequest>(handlers::handle_document_highlight) |
547 | .on::<lsp_types::request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare) | 548 | .on::<lsp_types::request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare) |
548 | .on::<lsp_types::request::CallHierarchyIncomingCalls>( | 549 | .on::<lsp_types::request::CallHierarchyIncomingCalls>( |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 7a53e4a8b..93b5ff55f 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -455,7 +455,11 @@ impl ProjectFolders { | |||
455 | dirs.include.extend(root.include); | 455 | dirs.include.extend(root.include); |
456 | dirs.exclude.extend(root.exclude); | 456 | dirs.exclude.extend(root.exclude); |
457 | for excl in global_excludes { | 457 | for excl in global_excludes { |
458 | if dirs.include.iter().any(|incl| incl.starts_with(excl)) { | 458 | if dirs |
459 | .include | ||
460 | .iter() | ||
461 | .any(|incl| incl.starts_with(excl) || excl.starts_with(incl)) | ||
462 | { | ||
459 | dirs.exclude.push(excl.clone()); | 463 | dirs.exclude.push(excl.clone()); |
460 | } | 464 | } |
461 | } | 465 | } |
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index c02bab7cc..f3da82feb 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc | |||
@@ -346,6 +346,13 @@ Additional arguments to `rustfmt`. | |||
346 | Advanced option, fully override the command rust-analyzer uses for | 346 | Advanced option, fully override the command rust-analyzer uses for |
347 | formatting. | 347 | formatting. |
348 | -- | 348 | -- |
349 | [[rust-analyzer.rustfmt.enableRangeFormatting]]rust-analyzer.rustfmt.enableRangeFormatting (default: `false`):: | ||
350 | + | ||
351 | -- | ||
352 | Enables the use of rustfmt's unstable range formatting command for the | ||
353 | `textDocument/rangeFormatting` request. The rustfmt option is unstable and only | ||
354 | available on a nightly build. | ||
355 | -- | ||
349 | [[rust-analyzer.workspace.symbol.search.scope]]rust-analyzer.workspace.symbol.search.scope (default: `"workspace"`):: | 356 | [[rust-analyzer.workspace.symbol.search.scope]]rust-analyzer.workspace.symbol.search.scope (default: `"workspace"`):: |
350 | + | 357 | + |
351 | -- | 358 | -- |
diff --git a/editors/code/package.json b/editors/code/package.json index 17d9281ff..05cbccf94 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -795,6 +795,11 @@ | |||
795 | "type": "string" | 795 | "type": "string" |
796 | } | 796 | } |
797 | }, | 797 | }, |
798 | "rust-analyzer.rustfmt.enableRangeFormatting": { | ||
799 | "markdownDescription": "Enables the use of rustfmt's unstable range formatting command for the\n`textDocument/rangeFormatting` request. The rustfmt option is unstable and only\navailable on a nightly build.", | ||
800 | "default": false, | ||
801 | "type": "boolean" | ||
802 | }, | ||
798 | "rust-analyzer.workspace.symbol.search.scope": { | 803 | "rust-analyzer.workspace.symbol.search.scope": { |
799 | "markdownDescription": "Workspace symbol search scope.", | 804 | "markdownDescription": "Workspace symbol search scope.", |
800 | "default": "workspace", | 805 | "default": "workspace", |