aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/code_model_api.rs8
-rw-r--r--crates/ra_hir/src/diagnostics.rs62
-rw-r--r--crates/ra_hir/src/expr.rs4
-rw-r--r--crates/ra_hir/src/ty/infer.rs39
-rw-r--r--crates/ra_ide_api/src/diagnostics.rs26
-rw-r--r--crates/ra_syntax/src/ptr.rs6
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 @@
1use crate::{expr::ExprId}; 1use std::{fmt, any::Any};
2 2
3#[derive(Clone, Debug, PartialEq, Eq)] 3use ra_syntax::{SyntaxNodePtr, AstPtr, ast};
4pub enum FunctionDiagnostic { 4
5 NoSuchField { expr: ExprId, field: usize }, 5use crate::HirFileId;
6
7pub 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
14impl 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)]
21pub struct Diagnostics {
22 data: Vec<Box<dyn Diagnostic>>,
23}
24
25impl 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)]
36pub struct NoSuchField {
37 pub(crate) file: HirFileId,
38 pub(crate) field: AstPtr<ast::NamedField>,
39}
40
41impl NoSuchField {
42 pub fn field(&self) -> AstPtr<ast::NamedField> {
43 self.field
44 }
45}
46
47impl 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};
42use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; 43use 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
1271mod 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};
3use ra_db::SourceDatabase; 3use ra_db::SourceDatabase;
4use ra_syntax::{ 4use 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};
9use ra_text_edit::{TextEdit, TextEditBuilder}; 9use ra_text_edit::{TextEdit, TextEditBuilder};
@@ -161,23 +161,13 @@ fn check_module(
161} 161}
162 162
163fn check_function(acc: &mut Vec<Diagnostic>, db: &RootDatabase, function: hir::Function) { 163fn 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
67impl<N: AstNode> From<AstPtr<N>> for SyntaxNodePtr {
68 fn from(ptr: AstPtr<N>) -> SyntaxNodePtr {
69 ptr.raw
70 }
71}
72
67#[test] 73#[test]
68fn test_local_syntax_ptr() { 74fn test_local_syntax_ptr() {
69 use crate::{ast, AstNode}; 75 use crate::{ast, AstNode};