aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-03-21 19:13:11 +0000
committerAleksey Kladov <[email protected]>2019-03-25 07:52:12 +0000
commit7e8f17188efcecfdfd1afbbc894a53c65985f836 (patch)
treeaab311a7646f9880adc82607abd227ef07e35d71
parent4132fbf3a08c9de2e28a50bc29a2c37a7c1a42fc (diff)
diagnostics
-rw-r--r--crates/ra_hir/src/code_model_api.rs5
-rw-r--r--crates/ra_hir/src/diagnostics.rs6
-rw-r--r--crates/ra_hir/src/expr.rs15
-rw-r--r--crates/ra_hir/src/lib.rs1
-rw-r--r--crates/ra_hir/src/ty/infer.rs23
-rw-r--r--crates/ra_ide_api/src/diagnostics.rs30
6 files changed, 74 insertions, 6 deletions
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs
index 45fa4cd11..58481e715 100644
--- a/crates/ra_hir/src/code_model_api.rs
+++ b/crates/ra_hir/src/code_model_api.rs
@@ -17,6 +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}; 21};
21 22
22/// 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
@@ -519,6 +520,10 @@ impl Function {
519 let r = if !p.params.is_empty() { r.push_generic_params_scope(p) } else { r }; 520 let r = if !p.params.is_empty() { r.push_generic_params_scope(p) } else { r };
520 r 521 r
521 } 522 }
523
524 pub fn diagnostics(&self, db: &impl HirDatabase) -> Vec<FunctionDiagnostic> {
525 self.infer(db).diagnostics()
526 }
522} 527}
523 528
524impl Docs for Function { 529impl Docs for Function {
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs
new file mode 100644
index 000000000..82aff9cee
--- /dev/null
+++ b/crates/ra_hir/src/diagnostics.rs
@@ -0,0 +1,6 @@
1use crate::{expr::ExprId};
2
3#[derive(Clone, Debug, PartialEq, Eq)]
4pub enum FunctionDiagnostic {
5 NoSuchField { expr: ExprId, field: usize },
6}
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index c37fd0454..31af5d241 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -5,7 +5,7 @@ use rustc_hash::FxHashMap;
5 5
6use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; 6use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
7use ra_syntax::{ 7use ra_syntax::{
8 SyntaxNodePtr, AstNode, 8 SyntaxNodePtr, AstPtr, AstNode,
9 ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralFlavor, TypeAscriptionOwner} 9 ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralFlavor, TypeAscriptionOwner}
10}; 10};
11 11
@@ -54,6 +54,7 @@ pub struct BodySourceMap {
54 expr_map_back: ArenaMap<ExprId, SyntaxNodePtr>, 54 expr_map_back: ArenaMap<ExprId, SyntaxNodePtr>,
55 pat_map: FxHashMap<SyntaxNodePtr, PatId>, 55 pat_map: FxHashMap<SyntaxNodePtr, PatId>,
56 pat_map_back: ArenaMap<PatId, SyntaxNodePtr>, 56 pat_map_back: ArenaMap<PatId, SyntaxNodePtr>,
57 field_map: FxHashMap<(ExprId, usize), AstPtr<ast::NamedField>>,
57} 58}
58 59
59impl Body { 60impl Body {
@@ -138,6 +139,10 @@ impl BodySourceMap {
138 pub fn node_pat(&self, node: &ast::Pat) -> Option<PatId> { 139 pub fn node_pat(&self, node: &ast::Pat) -> Option<PatId> {
139 self.pat_map.get(&SyntaxNodePtr::new(node.syntax())).cloned() 140 self.pat_map.get(&SyntaxNodePtr::new(node.syntax())).cloned()
140 } 141 }
142
143 pub fn field_syntax(&self, expr: ExprId, field: usize) -> Option<AstPtr<ast::NamedField>> {
144 self.field_map.get(&(expr, field)).cloned()
145 }
141} 146}
142 147
143#[derive(Debug, Clone, Eq, PartialEq)] 148#[derive(Debug, Clone, Eq, PartialEq)]
@@ -629,8 +634,10 @@ impl ExprCollector {
629 } 634 }
630 ast::ExprKind::StructLit(e) => { 635 ast::ExprKind::StructLit(e) => {
631 let path = e.path().and_then(Path::from_ast); 636 let path = e.path().and_then(Path::from_ast);
637 let mut field_ptrs = Vec::new();
632 let fields = if let Some(nfl) = e.named_field_list() { 638 let fields = if let Some(nfl) = e.named_field_list() {
633 nfl.fields() 639 nfl.fields()
640 .inspect(|field| field_ptrs.push(AstPtr::new(*field)))
634 .map(|field| StructLitField { 641 .map(|field| StructLitField {
635 name: field 642 name: field
636 .name_ref() 643 .name_ref()
@@ -657,7 +664,11 @@ impl ExprCollector {
657 Vec::new() 664 Vec::new()
658 }; 665 };
659 let spread = e.spread().map(|s| self.collect_expr(s)); 666 let spread = e.spread().map(|s| self.collect_expr(s));
660 self.alloc_expr(Expr::StructLit { path, fields, spread }, syntax_ptr) 667 let res = self.alloc_expr(Expr::StructLit { path, fields, spread }, syntax_ptr);
668 for (i, ptr) in field_ptrs.into_iter().enumerate() {
669 self.source_map.field_map.insert((res, i), ptr);
670 }
671 res
661 } 672 }
662 ast::ExprKind::FieldExpr(e) => { 673 ast::ExprKind::FieldExpr(e) => {
663 let expr = self.collect_expr_opt(e.expr()); 674 let expr = self.collect_expr_opt(e.expr());
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index a89c916f8..390aef0a9 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -35,6 +35,7 @@ mod expr;
35mod generics; 35mod generics;
36mod docs; 36mod docs;
37mod resolve; 37mod resolve;
38pub mod diagnostics;
38 39
39mod code_model_api; 40mod code_model_api;
40mod code_model_impl; 41mod code_model_impl;
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs
index cff7e7481..269b5162e 100644
--- a/crates/ra_hir/src/ty/infer.rs
+++ b/crates/ra_hir/src/ty/infer.rs
@@ -36,7 +36,8 @@ use crate::{
36 path::{GenericArgs, GenericArg}, 36 path::{GenericArgs, GenericArg},
37 adt::VariantDef, 37 adt::VariantDef,
38 resolve::{Resolver, Resolution}, 38 resolve::{Resolver, Resolution},
39 nameres::Namespace 39 nameres::Namespace,
40 diagnostics::FunctionDiagnostic,
40}; 41};
41use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; 42use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor};
42 43
@@ -96,6 +97,7 @@ pub struct InferenceResult {
96 field_resolutions: FxHashMap<ExprId, StructField>, 97 field_resolutions: FxHashMap<ExprId, StructField>,
97 /// For each associated item record what it resolves to 98 /// For each associated item record what it resolves to
98 assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, 99 assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>,
100 diagnostics: Vec<FunctionDiagnostic>,
99 pub(super) type_of_expr: ArenaMap<ExprId, Ty>, 101 pub(super) type_of_expr: ArenaMap<ExprId, Ty>,
100 pub(super) type_of_pat: ArenaMap<PatId, Ty>, 102 pub(super) type_of_pat: ArenaMap<PatId, Ty>,
101} 103}
@@ -113,6 +115,9 @@ impl InferenceResult {
113 pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> { 115 pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> {
114 self.assoc_resolutions.get(&id.into()).map(|it| *it) 116 self.assoc_resolutions.get(&id.into()).map(|it| *it)
115 } 117 }
118 pub(crate) fn diagnostics(&self) -> Vec<FunctionDiagnostic> {
119 self.diagnostics.clone()
120 }
116} 121}
117 122
118impl Index<ExprId> for InferenceResult { 123impl Index<ExprId> for InferenceResult {
@@ -143,6 +148,7 @@ struct InferenceContext<'a, D: HirDatabase> {
143 assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>, 148 assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>,
144 type_of_expr: ArenaMap<ExprId, Ty>, 149 type_of_expr: ArenaMap<ExprId, Ty>,
145 type_of_pat: ArenaMap<PatId, Ty>, 150 type_of_pat: ArenaMap<PatId, Ty>,
151 diagnostics: Vec<FunctionDiagnostic>,
146 /// The return type of the function being inferred. 152 /// The return type of the function being inferred.
147 return_ty: Ty, 153 return_ty: Ty,
148} 154}
@@ -155,6 +161,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
155 assoc_resolutions: FxHashMap::default(), 161 assoc_resolutions: FxHashMap::default(),
156 type_of_expr: ArenaMap::default(), 162 type_of_expr: ArenaMap::default(),
157 type_of_pat: ArenaMap::default(), 163 type_of_pat: ArenaMap::default(),
164 diagnostics: Vec::default(),
158 var_unification_table: InPlaceUnificationTable::new(), 165 var_unification_table: InPlaceUnificationTable::new(),
159 return_ty: Ty::Unknown, // set in collect_fn_signature 166 return_ty: Ty::Unknown, // set in collect_fn_signature
160 db, 167 db,
@@ -181,6 +188,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
181 assoc_resolutions: self.assoc_resolutions, 188 assoc_resolutions: self.assoc_resolutions,
182 type_of_expr: expr_types, 189 type_of_expr: expr_types,
183 type_of_pat: pat_types, 190 type_of_pat: pat_types,
191 diagnostics: self.diagnostics,
184 } 192 }
185 } 193 }
186 194
@@ -915,9 +923,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
915 Expr::StructLit { path, fields, spread } => { 923 Expr::StructLit { path, fields, spread } => {
916 let (ty, def_id) = self.resolve_variant(path.as_ref()); 924 let (ty, def_id) = self.resolve_variant(path.as_ref());
917 let substs = ty.substs().unwrap_or_else(Substs::empty); 925 let substs = ty.substs().unwrap_or_else(Substs::empty);
918 for field in fields { 926 for (field_idx, field) in fields.into_iter().enumerate() {
919 let field_ty = def_id 927 let field_ty = def_id
920 .and_then(|it| it.field(self.db, &field.name)) 928 .and_then(|it| match it.field(self.db, &field.name) {
929 Some(field) => Some(field),
930 None => {
931 self.diagnostics.push(FunctionDiagnostic::NoSuchField {
932 expr: tgt_expr,
933 field: field_idx,
934 });
935 None
936 }
937 })
921 .map_or(Ty::Unknown, |field| field.ty(self.db)) 938 .map_or(Ty::Unknown, |field| field.ty(self.db))
922 .subst(&substs); 939 .subst(&substs);
923 self.infer_expr(field.expr, &Expectation::has_type(field_ty)); 940 self.infer_expr(field.expr, &Expectation::has_type(field_ty));
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs
index 156f28ca3..f662f7e2f 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}, 6 ast::{self, AstNode, NameOwner},
7 7
8}; 8};
9use ra_text_edit::{TextEdit, TextEditBuilder}; 9use ra_text_edit::{TextEdit, TextEditBuilder};
@@ -134,6 +134,13 @@ fn check_module(
134 file_id: FileId, 134 file_id: FileId,
135 module: hir::Module, 135 module: hir::Module,
136) { 136) {
137 for decl in module.declarations(db) {
138 match decl {
139 hir::ModuleDef::Function(f) => check_function(acc, db, f),
140 _ => (),
141 }
142 }
143
137 let source_root = db.file_source_root(file_id); 144 let source_root = db.file_source_root(file_id);
138 for (name_node, problem) in module.problems(db) { 145 for (name_node, problem) in module.problems(db) {
139 let diag = match problem { 146 let diag = match problem {
@@ -153,6 +160,27 @@ fn check_module(
153 } 160 }
154} 161}
155 162
163fn check_function(acc: &mut Vec<Diagnostic>, db: &RootDatabase, function: hir::Function) {
164 let (_file_id, fn_def) = function.source(db);
165 let source_file = fn_def.syntax().ancestors().find_map(ast::SourceFile::cast).unwrap();
166 let source_map = function.body_source_map(db);
167 for d in function.diagnostics(db) {
168 match d {
169 hir::diagnostics::FunctionDiagnostic::NoSuchField { expr, field } => {
170 if let Some(field) = source_map.field_syntax(expr, field) {
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 }
182}
183
156#[cfg(test)] 184#[cfg(test)]
157mod tests { 185mod tests {
158 use test_utils::assert_eq_text; 186 use test_utils::assert_eq_text;