aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir
diff options
context:
space:
mode:
authorSergey Parilin <[email protected]>2019-04-10 22:00:56 +0100
committerSergey Parilin <[email protected]>2019-05-06 15:16:11 +0100
commit26ed92568596ce45ad96c3e2ea5d54099702537f (patch)
tree29624826736c7287ac7dea635e639f20280d2533 /crates/ra_hir
parent32db5884ada59c72aa7ab9f88910ef7c8f882e7d (diff)
fill struct fields diagnostic
Diffstat (limited to 'crates/ra_hir')
-rw-r--r--crates/ra_hir/src/code_model_api.rs11
-rw-r--r--crates/ra_hir/src/diagnostics.rs24
-rw-r--r--crates/ra_hir/src/expr.rs16
-rw-r--r--crates/ra_hir/src/expr/validation.rs92
-rw-r--r--crates/ra_hir/src/ty/tests.rs1
5 files changed, 133 insertions, 11 deletions
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs
index 9dcae50a5..55e1793c5 100644
--- a/crates/ra_hir/src/code_model_api.rs
+++ b/crates/ra_hir/src/code_model_api.rs
@@ -8,7 +8,7 @@ use crate::{
8 HirDatabase, DefDatabase, 8 HirDatabase, DefDatabase,
9 type_ref::TypeRef, 9 type_ref::TypeRef,
10 nameres::{ModuleScope, Namespace, ImportId, CrateModuleId}, 10 nameres::{ModuleScope, Namespace, ImportId, CrateModuleId},
11 expr::{Body, BodySourceMap}, 11 expr::{Body, BodySourceMap, validation::ExprValidator},
12 ty::{ TraitRef, InferenceResult}, 12 ty::{ TraitRef, InferenceResult},
13 adt::{EnumVariantId, StructFieldId, VariantDef}, 13 adt::{EnumVariantId, StructFieldId, VariantDef},
14 generics::HasGenericParams, 14 generics::HasGenericParams,
@@ -16,7 +16,7 @@ use crate::{
16 ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeAliasId}, 16 ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeAliasId},
17 impl_block::ImplBlock, 17 impl_block::ImplBlock,
18 resolve::Resolver, 18 resolve::Resolver,
19 diagnostics::DiagnosticSink, 19 diagnostics::{DiagnosticSink},
20 traits::{TraitItem, TraitData}, 20 traits::{TraitItem, TraitData},
21}; 21};
22 22
@@ -431,8 +431,8 @@ impl Docs for EnumVariant {
431#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 431#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
432pub enum DefWithBody { 432pub enum DefWithBody {
433 Function(Function), 433 Function(Function),
434 Const(Const),
435 Static(Static), 434 Static(Static),
435 Const(Const),
436} 436}
437 437
438impl_froms!(DefWithBody: Function, Const, Static); 438impl_froms!(DefWithBody: Function, Const, Static);
@@ -562,7 +562,10 @@ impl Function {
562 } 562 }
563 563
564 pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { 564 pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut DiagnosticSink) {
565 self.infer(db).add_diagnostics(db, *self, sink); 565 let infer = self.infer(db);
566 infer.add_diagnostics(db, *self, sink);
567 let mut validator = ExprValidator::new(*self, infer, sink);
568 validator.validate_body(db);
566 } 569 }
567} 570}
568 571
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs
index d6a51b833..61cd9d6b1 100644
--- a/crates/ra_hir/src/diagnostics.rs
+++ b/crates/ra_hir/src/diagnostics.rs
@@ -3,7 +3,7 @@ use std::{fmt, any::Any};
3use ra_syntax::{SyntaxNodePtr, TreeArc, AstPtr, TextRange, ast, SyntaxNode}; 3use ra_syntax::{SyntaxNodePtr, TreeArc, AstPtr, TextRange, ast, SyntaxNode};
4use relative_path::RelativePathBuf; 4use relative_path::RelativePathBuf;
5 5
6use crate::{HirFileId, HirDatabase}; 6use crate::{HirFileId, HirDatabase, Name};
7 7
8/// Diagnostic defines hir API for errors and warnings. 8/// Diagnostic defines hir API for errors and warnings.
9/// 9///
@@ -113,3 +113,25 @@ impl Diagnostic for UnresolvedModule {
113 self 113 self
114 } 114 }
115} 115}
116
117#[derive(Debug)]
118pub struct MissingFields {
119 pub file: HirFileId,
120 pub field_list: AstPtr<ast::NamedFieldList>,
121 pub missed_fields: Vec<Name>,
122}
123
124impl Diagnostic for MissingFields {
125 fn message(&self) -> String {
126 "fill structure fields".to_string()
127 }
128 fn file(&self) -> HirFileId {
129 self.file
130 }
131 fn syntax_node_ptr(&self) -> SyntaxNodePtr {
132 self.field_list.into()
133 }
134 fn as_any(&self) -> &(dyn Any + Send + 'static) {
135 self
136 }
137}
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index 692da2895..480eaf171 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -19,6 +19,7 @@ use crate::{path::GenericArgs, ty::primitive::{IntTy, UncertainIntTy, FloatTy, U
19pub use self::scope::ExprScopes; 19pub use self::scope::ExprScopes;
20 20
21pub(crate) mod scope; 21pub(crate) mod scope;
22pub(crate) mod validation;
22 23
23#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 24#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub struct ExprId(RawId); 25pub struct ExprId(RawId);
@@ -670,8 +671,9 @@ where
670 ast::ExprKind::StructLit(e) => { 671 ast::ExprKind::StructLit(e) => {
671 let path = e.path().and_then(Path::from_ast); 672 let path = e.path().and_then(Path::from_ast);
672 let mut field_ptrs = Vec::new(); 673 let mut field_ptrs = Vec::new();
673 let fields = if let Some(nfl) = e.named_field_list() { 674 let struct_lit = if let Some(nfl) = e.named_field_list() {
674 nfl.fields() 675 let fields = nfl
676 .fields()
675 .inspect(|field| field_ptrs.push(AstPtr::new(*field))) 677 .inspect(|field| field_ptrs.push(AstPtr::new(*field)))
676 .map(|field| StructLitField { 678 .map(|field| StructLitField {
677 name: field 679 name: field
@@ -694,12 +696,14 @@ where
694 self.exprs.alloc(Expr::Missing) 696 self.exprs.alloc(Expr::Missing)
695 }, 697 },
696 }) 698 })
697 .collect() 699 .collect();
700 let spread = nfl.spread().map(|s| self.collect_expr(s));
701 Expr::StructLit { path, fields, spread }
698 } else { 702 } else {
699 Vec::new() 703 Expr::StructLit { path, fields: Vec::new(), spread: None }
700 }; 704 };
701 let spread = e.spread().map(|s| self.collect_expr(s)); 705
702 let res = self.alloc_expr(Expr::StructLit { path, fields, spread }, syntax_ptr); 706 let res = self.alloc_expr(struct_lit, syntax_ptr);
703 for (i, ptr) in field_ptrs.into_iter().enumerate() { 707 for (i, ptr) in field_ptrs.into_iter().enumerate() {
704 self.source_map.field_map.insert((res, i), ptr); 708 self.source_map.field_map.insert((res, i), ptr);
705 } 709 }
diff --git a/crates/ra_hir/src/expr/validation.rs b/crates/ra_hir/src/expr/validation.rs
new file mode 100644
index 000000000..fd4907313
--- /dev/null
+++ b/crates/ra_hir/src/expr/validation.rs
@@ -0,0 +1,92 @@
1use std::sync::Arc;
2use rustc_hash::FxHashSet;
3
4use ra_syntax::ast::{AstNode, StructLit};
5
6use crate::{
7 expr::AstPtr,
8 HirDatabase,
9 Function,
10 Name,
11 diagnostics::{DiagnosticSink, MissingFields},
12 adt::AdtDef,
13 Path,
14 ty::InferenceResult
15};
16use super::{Expr, StructLitField, ExprId};
17
18pub(crate) struct ExprValidator<'a, 'b: 'a> {
19 func: Function,
20 infer: Arc<InferenceResult>,
21 sink: &'a mut DiagnosticSink<'b>,
22}
23
24impl<'a, 'b> ExprValidator<'a, 'b> {
25 pub(crate) fn new(
26 func: Function,
27 infer: Arc<InferenceResult>,
28 sink: &'a mut DiagnosticSink<'b>,
29 ) -> ExprValidator<'a, 'b> {
30 ExprValidator { func, infer, sink }
31 }
32
33 pub(crate) fn validate_body(&mut self, db: &impl HirDatabase) {
34 let body = self.func.body(db);
35 for e in body.exprs() {
36 match e {
37 (id, Expr::StructLit { path, fields, spread }) => {
38 self.validate_struct_literal(id, path, fields, spread, db)
39 }
40 _ => (),
41 }
42 }
43 }
44
45 fn validate_struct_literal(
46 &mut self,
47 id: ExprId,
48 _path: &Option<Path>,
49 fields: &Vec<StructLitField>,
50 spread: &Option<ExprId>,
51 db: &impl HirDatabase,
52 ) {
53 if let Some(_) = spread {
54 return;
55 }
56 let lit_fields: FxHashSet<_> = fields.into_iter().map(|f| &f.name).collect();
57 let struct_ty = &self.infer[id];
58 if let Some((AdtDef::Struct(s), _)) = struct_ty.as_adt() {
59 let missed_fields: Vec<Name> = s
60 .fields(db)
61 .iter()
62 .filter_map(|f| {
63 let name = f.name(db);
64 if lit_fields.contains(&name) {
65 None
66 } else {
67 Some(name)
68 }
69 })
70 .collect();
71 if missed_fields.is_empty() {
72 return;
73 }
74 let source_map = self.func.body_source_map(db);
75 let file_id = self.func.source(db).0;
76 let source_file = db.parse(file_id.original_file(db));
77 if let Some(field_list_node) = source_map
78 .expr_syntax(id)
79 .map(|ptr| ptr.to_node(&source_file))
80 .and_then(StructLit::cast)
81 .and_then(|lit| lit.named_field_list())
82 {
83 let field_list_ptr = AstPtr::new(field_list_node);
84 self.sink.push(MissingFields {
85 file: file_id,
86 field_list: field_list_ptr,
87 missed_fields,
88 })
89 }
90 }
91 }
92}
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 0aecde37c..a38fe35c7 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -2662,6 +2662,7 @@ fn no_such_field_diagnostics() {
2662 2662
2663 assert_snapshot_matches!(diagnostics, @r###" 2663 assert_snapshot_matches!(diagnostics, @r###"
2664"baz: 62": no such field 2664"baz: 62": no such field
2665"{\n foo: 92,\n baz: 62,\n }": fill structure fields
2665"### 2666"###
2666 ); 2667 );
2667} 2668}