diff options
author | Sergey Parilin <[email protected]> | 2019-04-10 22:00:56 +0100 |
---|---|---|
committer | Sergey Parilin <[email protected]> | 2019-05-06 15:16:11 +0100 |
commit | 26ed92568596ce45ad96c3e2ea5d54099702537f (patch) | |
tree | 29624826736c7287ac7dea635e639f20280d2533 /crates/ra_hir | |
parent | 32db5884ada59c72aa7ab9f88910ef7c8f882e7d (diff) |
fill struct fields diagnostic
Diffstat (limited to 'crates/ra_hir')
-rw-r--r-- | crates/ra_hir/src/code_model_api.rs | 11 | ||||
-rw-r--r-- | crates/ra_hir/src/diagnostics.rs | 24 | ||||
-rw-r--r-- | crates/ra_hir/src/expr.rs | 16 | ||||
-rw-r--r-- | crates/ra_hir/src/expr/validation.rs | 92 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 1 |
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)] |
432 | pub enum DefWithBody { | 432 | pub enum DefWithBody { |
433 | Function(Function), | 433 | Function(Function), |
434 | Const(Const), | ||
435 | Static(Static), | 434 | Static(Static), |
435 | Const(Const), | ||
436 | } | 436 | } |
437 | 437 | ||
438 | impl_froms!(DefWithBody: Function, Const, Static); | 438 | impl_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}; | |||
3 | use ra_syntax::{SyntaxNodePtr, TreeArc, AstPtr, TextRange, ast, SyntaxNode}; | 3 | use ra_syntax::{SyntaxNodePtr, TreeArc, AstPtr, TextRange, ast, SyntaxNode}; |
4 | use relative_path::RelativePathBuf; | 4 | use relative_path::RelativePathBuf; |
5 | 5 | ||
6 | use crate::{HirFileId, HirDatabase}; | 6 | use 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)] | ||
118 | pub struct MissingFields { | ||
119 | pub file: HirFileId, | ||
120 | pub field_list: AstPtr<ast::NamedFieldList>, | ||
121 | pub missed_fields: Vec<Name>, | ||
122 | } | ||
123 | |||
124 | impl 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 | |||
19 | pub use self::scope::ExprScopes; | 19 | pub use self::scope::ExprScopes; |
20 | 20 | ||
21 | pub(crate) mod scope; | 21 | pub(crate) mod scope; |
22 | pub(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)] |
24 | pub struct ExprId(RawId); | 25 | pub 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 @@ | |||
1 | use std::sync::Arc; | ||
2 | use rustc_hash::FxHashSet; | ||
3 | |||
4 | use ra_syntax::ast::{AstNode, StructLit}; | ||
5 | |||
6 | use crate::{ | ||
7 | expr::AstPtr, | ||
8 | HirDatabase, | ||
9 | Function, | ||
10 | Name, | ||
11 | diagnostics::{DiagnosticSink, MissingFields}, | ||
12 | adt::AdtDef, | ||
13 | Path, | ||
14 | ty::InferenceResult | ||
15 | }; | ||
16 | use super::{Expr, StructLitField, ExprId}; | ||
17 | |||
18 | pub(crate) struct ExprValidator<'a, 'b: 'a> { | ||
19 | func: Function, | ||
20 | infer: Arc<InferenceResult>, | ||
21 | sink: &'a mut DiagnosticSink<'b>, | ||
22 | } | ||
23 | |||
24 | impl<'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 | } |