diff options
-rw-r--r-- | crates/ra_hir/src/code_model_api.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir/src/diagnostics.rs | 62 | ||||
-rw-r--r-- | crates/ra_hir/src/expr.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/infer.rs | 39 | ||||
-rw-r--r-- | crates/ra_ide_api/src/diagnostics.rs | 26 | ||||
-rw-r--r-- | crates/ra_syntax/src/ptr.rs | 6 |
6 files changed, 112 insertions, 33 deletions
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 58481e715..a37d960a1 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs | |||
@@ -17,7 +17,7 @@ use crate::{ | |||
17 | ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, | 17 | ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, |
18 | impl_block::ImplBlock, | 18 | impl_block::ImplBlock, |
19 | resolve::Resolver, | 19 | resolve::Resolver, |
20 | diagnostics::FunctionDiagnostic, | 20 | diagnostics::Diagnostics, |
21 | }; | 21 | }; |
22 | 22 | ||
23 | /// hir::Crate describes a single crate. It's the main interface with which | 23 | /// hir::Crate describes a single crate. It's the main interface with which |
@@ -521,8 +521,10 @@ impl Function { | |||
521 | r | 521 | r |
522 | } | 522 | } |
523 | 523 | ||
524 | pub fn diagnostics(&self, db: &impl HirDatabase) -> Vec<FunctionDiagnostic> { | 524 | pub fn diagnostics(&self, db: &impl HirDatabase) -> Diagnostics { |
525 | self.infer(db).diagnostics() | 525 | let mut res = Diagnostics::default(); |
526 | self.infer(db).add_diagnostics(db, *self, &mut res); | ||
527 | res | ||
526 | } | 528 | } |
527 | } | 529 | } |
528 | 530 | ||
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index 82aff9cee..46a3fdd47 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs | |||
@@ -1,6 +1,60 @@ | |||
1 | use crate::{expr::ExprId}; | 1 | use std::{fmt, any::Any}; |
2 | 2 | ||
3 | #[derive(Clone, Debug, PartialEq, Eq)] | 3 | use ra_syntax::{SyntaxNodePtr, AstPtr, ast}; |
4 | pub enum FunctionDiagnostic { | 4 | |
5 | NoSuchField { expr: ExprId, field: usize }, | 5 | use crate::HirFileId; |
6 | |||
7 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | ||
8 | fn file(&self) -> HirFileId; | ||
9 | fn syntax_node(&self) -> SyntaxNodePtr; | ||
10 | fn message(&self) -> String; | ||
11 | fn as_any(&self) -> &(Any + Send + 'static); | ||
12 | } | ||
13 | |||
14 | impl dyn Diagnostic { | ||
15 | pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> { | ||
16 | self.as_any().downcast_ref() | ||
17 | } | ||
18 | } | ||
19 | |||
20 | #[derive(Debug, Default)] | ||
21 | pub struct Diagnostics { | ||
22 | data: Vec<Box<dyn Diagnostic>>, | ||
23 | } | ||
24 | |||
25 | impl Diagnostics { | ||
26 | pub fn push(&mut self, d: impl Diagnostic) { | ||
27 | self.data.push(Box::new(d)) | ||
28 | } | ||
29 | |||
30 | pub fn iter<'a>(&'a self) -> impl Iterator<Item = &'a dyn Diagnostic> + 'a { | ||
31 | self.data.iter().map(|it| it.as_ref()) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | #[derive(Debug)] | ||
36 | pub struct NoSuchField { | ||
37 | pub(crate) file: HirFileId, | ||
38 | pub(crate) field: AstPtr<ast::NamedField>, | ||
39 | } | ||
40 | |||
41 | impl NoSuchField { | ||
42 | pub fn field(&self) -> AstPtr<ast::NamedField> { | ||
43 | self.field | ||
44 | } | ||
45 | } | ||
46 | |||
47 | impl Diagnostic for NoSuchField { | ||
48 | fn file(&self) -> HirFileId { | ||
49 | self.file | ||
50 | } | ||
51 | fn syntax_node(&self) -> SyntaxNodePtr { | ||
52 | self.field.into() | ||
53 | } | ||
54 | fn message(&self) -> String { | ||
55 | "no such field".to_string() | ||
56 | } | ||
57 | fn as_any(&self) -> &(Any + Send + 'static) { | ||
58 | self | ||
59 | } | ||
6 | } | 60 | } |
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 31af5d241..a85422955 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs | |||
@@ -140,8 +140,8 @@ impl BodySourceMap { | |||
140 | self.pat_map.get(&SyntaxNodePtr::new(node.syntax())).cloned() | 140 | self.pat_map.get(&SyntaxNodePtr::new(node.syntax())).cloned() |
141 | } | 141 | } |
142 | 142 | ||
143 | pub fn field_syntax(&self, expr: ExprId, field: usize) -> Option<AstPtr<ast::NamedField>> { | 143 | pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::NamedField> { |
144 | self.field_map.get(&(expr, field)).cloned() | 144 | self.field_map[&(expr, field)].clone() |
145 | } | 145 | } |
146 | } | 146 | } |
147 | 147 | ||
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 269b5162e..02708ba0f 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs | |||
@@ -37,7 +37,8 @@ use crate::{ | |||
37 | adt::VariantDef, | 37 | adt::VariantDef, |
38 | resolve::{Resolver, Resolution}, | 38 | resolve::{Resolver, Resolution}, |
39 | nameres::Namespace, | 39 | nameres::Namespace, |
40 | diagnostics::FunctionDiagnostic, | 40 | ty::infer::diagnostics::InferenceDiagnostic, |
41 | diagnostics::Diagnostics, | ||
41 | }; | 42 | }; |
42 | use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; | 43 | use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; |
43 | 44 | ||
@@ -97,7 +98,7 @@ pub struct InferenceResult { | |||
97 | field_resolutions: FxHashMap<ExprId, StructField>, | 98 | field_resolutions: FxHashMap<ExprId, StructField>, |
98 | /// For each associated item record what it resolves to | 99 | /// For each associated item record what it resolves to |
99 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, | 100 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, |
100 | diagnostics: Vec<FunctionDiagnostic>, | 101 | diagnostics: Vec<InferenceDiagnostic>, |
101 | pub(super) type_of_expr: ArenaMap<ExprId, Ty>, | 102 | pub(super) type_of_expr: ArenaMap<ExprId, Ty>, |
102 | pub(super) type_of_pat: ArenaMap<PatId, Ty>, | 103 | pub(super) type_of_pat: ArenaMap<PatId, Ty>, |
103 | } | 104 | } |
@@ -115,8 +116,13 @@ impl InferenceResult { | |||
115 | pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> { | 116 | pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> { |
116 | self.assoc_resolutions.get(&id.into()).map(|it| *it) | 117 | self.assoc_resolutions.get(&id.into()).map(|it| *it) |
117 | } | 118 | } |
118 | pub(crate) fn diagnostics(&self) -> Vec<FunctionDiagnostic> { | 119 | pub(crate) fn add_diagnostics( |
119 | self.diagnostics.clone() | 120 | &self, |
121 | db: &impl HirDatabase, | ||
122 | owner: Function, | ||
123 | diagnostics: &mut Diagnostics, | ||
124 | ) { | ||
125 | self.diagnostics.iter().for_each(|it| it.add_to(db, owner, diagnostics)) | ||
120 | } | 126 | } |
121 | } | 127 | } |
122 | 128 | ||
@@ -148,7 +154,7 @@ struct InferenceContext<'a, D: HirDatabase> { | |||
148 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, | 154 | assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, |
149 | type_of_expr: ArenaMap<ExprId, Ty>, | 155 | type_of_expr: ArenaMap<ExprId, Ty>, |
150 | type_of_pat: ArenaMap<PatId, Ty>, | 156 | type_of_pat: ArenaMap<PatId, Ty>, |
151 | diagnostics: Vec<FunctionDiagnostic>, | 157 | diagnostics: Vec<InferenceDiagnostic>, |
152 | /// The return type of the function being inferred. | 158 | /// The return type of the function being inferred. |
153 | return_ty: Ty, | 159 | return_ty: Ty, |
154 | } | 160 | } |
@@ -928,7 +934,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
928 | .and_then(|it| match it.field(self.db, &field.name) { | 934 | .and_then(|it| match it.field(self.db, &field.name) { |
929 | Some(field) => Some(field), | 935 | Some(field) => Some(field), |
930 | None => { | 936 | None => { |
931 | self.diagnostics.push(FunctionDiagnostic::NoSuchField { | 937 | self.diagnostics.push(InferenceDiagnostic::NoSuchField { |
932 | expr: tgt_expr, | 938 | expr: tgt_expr, |
933 | field: field_idx, | 939 | field: field_idx, |
934 | }); | 940 | }); |
@@ -1261,3 +1267,24 @@ impl Expectation { | |||
1261 | Expectation { ty: Ty::Unknown } | 1267 | Expectation { ty: Ty::Unknown } |
1262 | } | 1268 | } |
1263 | } | 1269 | } |
1270 | |||
1271 | mod diagnostics { | ||
1272 | use crate::{expr::ExprId, diagnostics::{Diagnostics, NoSuchField}, HirDatabase, Function}; | ||
1273 | |||
1274 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
1275 | pub(super) enum InferenceDiagnostic { | ||
1276 | NoSuchField { expr: ExprId, field: usize }, | ||
1277 | } | ||
1278 | |||
1279 | impl InferenceDiagnostic { | ||
1280 | pub(super) fn add_to(&self, db: &impl HirDatabase, owner: Function, acc: &mut Diagnostics) { | ||
1281 | match self { | ||
1282 | InferenceDiagnostic::NoSuchField { expr, field } => { | ||
1283 | let (file, _) = owner.source(db); | ||
1284 | let field = owner.body_source_map(db).field_syntax(*expr, *field); | ||
1285 | acc.push(NoSuchField { file, field }) | ||
1286 | } | ||
1287 | } | ||
1288 | } | ||
1289 | } | ||
1290 | } | ||
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index f662f7e2f..943fd2f53 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs | |||
@@ -3,7 +3,7 @@ use hir::{Problem, source_binder}; | |||
3 | use ra_db::SourceDatabase; | 3 | use ra_db::SourceDatabase; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, | 5 | Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, |
6 | ast::{self, AstNode, NameOwner}, | 6 | ast::{self, AstNode}, |
7 | 7 | ||
8 | }; | 8 | }; |
9 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 9 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
@@ -161,23 +161,13 @@ fn check_module( | |||
161 | } | 161 | } |
162 | 162 | ||
163 | fn check_function(acc: &mut Vec<Diagnostic>, db: &RootDatabase, function: hir::Function) { | 163 | fn check_function(acc: &mut Vec<Diagnostic>, db: &RootDatabase, function: hir::Function) { |
164 | let (_file_id, fn_def) = function.source(db); | 164 | for d in function.diagnostics(db).iter() { |
165 | let source_file = fn_def.syntax().ancestors().find_map(ast::SourceFile::cast).unwrap(); | 165 | acc.push(Diagnostic { |
166 | let source_map = function.body_source_map(db); | 166 | message: d.message(), |
167 | for d in function.diagnostics(db) { | 167 | range: d.syntax_node().range(), |
168 | match d { | 168 | severity: Severity::Error, |
169 | hir::diagnostics::FunctionDiagnostic::NoSuchField { expr, field } => { | 169 | fix: None, |
170 | if let Some(field) = source_map.field_syntax(expr, field) { | 170 | }) |
171 | let field = field.to_node(&source_file); | ||
172 | acc.push(Diagnostic { | ||
173 | message: "no such field".into(), | ||
174 | range: field.syntax().range(), | ||
175 | severity: Severity::Error, | ||
176 | fix: None, | ||
177 | }) | ||
178 | } | ||
179 | } | ||
180 | } | ||
181 | } | 171 | } |
182 | } | 172 | } |
183 | 173 | ||
diff --git a/crates/ra_syntax/src/ptr.rs b/crates/ra_syntax/src/ptr.rs index aae590cb6..d8de1c4c1 100644 --- a/crates/ra_syntax/src/ptr.rs +++ b/crates/ra_syntax/src/ptr.rs | |||
@@ -64,6 +64,12 @@ impl<N: AstNode> AstPtr<N> { | |||
64 | } | 64 | } |
65 | } | 65 | } |
66 | 66 | ||
67 | impl<N: AstNode> From<AstPtr<N>> for SyntaxNodePtr { | ||
68 | fn from(ptr: AstPtr<N>) -> SyntaxNodePtr { | ||
69 | ptr.raw | ||
70 | } | ||
71 | } | ||
72 | |||
67 | #[test] | 73 | #[test] |
68 | fn test_local_syntax_ptr() { | 74 | fn test_local_syntax_ptr() { |
69 | use crate::{ast, AstNode}; | 75 | use crate::{ast, AstNode}; |