From 26ed92568596ce45ad96c3e2ea5d54099702537f Mon Sep 17 00:00:00 2001 From: Sergey Parilin Date: Thu, 11 Apr 2019 00:00:56 +0300 Subject: fill struct fields diagnostic --- crates/ra_hir/src/code_model_api.rs | 11 +++-- crates/ra_hir/src/diagnostics.rs | 24 +++++++++- crates/ra_hir/src/expr.rs | 16 ++++--- crates/ra_hir/src/expr/validation.rs | 92 ++++++++++++++++++++++++++++++++++++ crates/ra_hir/src/ty/tests.rs | 1 + 5 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 crates/ra_hir/src/expr/validation.rs (limited to 'crates/ra_hir/src') 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::{ HirDatabase, DefDatabase, type_ref::TypeRef, nameres::{ModuleScope, Namespace, ImportId, CrateModuleId}, - expr::{Body, BodySourceMap}, + expr::{Body, BodySourceMap, validation::ExprValidator}, ty::{ TraitRef, InferenceResult}, adt::{EnumVariantId, StructFieldId, VariantDef}, generics::HasGenericParams, @@ -16,7 +16,7 @@ use crate::{ ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeAliasId}, impl_block::ImplBlock, resolve::Resolver, - diagnostics::DiagnosticSink, + diagnostics::{DiagnosticSink}, traits::{TraitItem, TraitData}, }; @@ -431,8 +431,8 @@ impl Docs for EnumVariant { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum DefWithBody { Function(Function), - Const(Const), Static(Static), + Const(Const), } impl_froms!(DefWithBody: Function, Const, Static); @@ -562,7 +562,10 @@ impl Function { } pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { - self.infer(db).add_diagnostics(db, *self, sink); + let infer = self.infer(db); + infer.add_diagnostics(db, *self, sink); + let mut validator = ExprValidator::new(*self, infer, sink); + validator.validate_body(db); } } 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}; use ra_syntax::{SyntaxNodePtr, TreeArc, AstPtr, TextRange, ast, SyntaxNode}; use relative_path::RelativePathBuf; -use crate::{HirFileId, HirDatabase}; +use crate::{HirFileId, HirDatabase, Name}; /// Diagnostic defines hir API for errors and warnings. /// @@ -113,3 +113,25 @@ impl Diagnostic for UnresolvedModule { self } } + +#[derive(Debug)] +pub struct MissingFields { + pub file: HirFileId, + pub field_list: AstPtr, + pub missed_fields: Vec, +} + +impl Diagnostic for MissingFields { + fn message(&self) -> String { + "fill structure fields".to_string() + } + fn file(&self) -> HirFileId { + self.file + } + fn syntax_node_ptr(&self) -> SyntaxNodePtr { + self.field_list.into() + } + fn as_any(&self) -> &(dyn Any + Send + 'static) { + self + } +} 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 pub use self::scope::ExprScopes; pub(crate) mod scope; +pub(crate) mod validation; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ExprId(RawId); @@ -670,8 +671,9 @@ where ast::ExprKind::StructLit(e) => { let path = e.path().and_then(Path::from_ast); let mut field_ptrs = Vec::new(); - let fields = if let Some(nfl) = e.named_field_list() { - nfl.fields() + let struct_lit = if let Some(nfl) = e.named_field_list() { + let fields = nfl + .fields() .inspect(|field| field_ptrs.push(AstPtr::new(*field))) .map(|field| StructLitField { name: field @@ -694,12 +696,14 @@ where self.exprs.alloc(Expr::Missing) }, }) - .collect() + .collect(); + let spread = nfl.spread().map(|s| self.collect_expr(s)); + Expr::StructLit { path, fields, spread } } else { - Vec::new() + Expr::StructLit { path, fields: Vec::new(), spread: None } }; - let spread = e.spread().map(|s| self.collect_expr(s)); - let res = self.alloc_expr(Expr::StructLit { path, fields, spread }, syntax_ptr); + + let res = self.alloc_expr(struct_lit, syntax_ptr); for (i, ptr) in field_ptrs.into_iter().enumerate() { self.source_map.field_map.insert((res, i), ptr); } 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 @@ +use std::sync::Arc; +use rustc_hash::FxHashSet; + +use ra_syntax::ast::{AstNode, StructLit}; + +use crate::{ + expr::AstPtr, + HirDatabase, + Function, + Name, + diagnostics::{DiagnosticSink, MissingFields}, + adt::AdtDef, + Path, + ty::InferenceResult +}; +use super::{Expr, StructLitField, ExprId}; + +pub(crate) struct ExprValidator<'a, 'b: 'a> { + func: Function, + infer: Arc, + sink: &'a mut DiagnosticSink<'b>, +} + +impl<'a, 'b> ExprValidator<'a, 'b> { + pub(crate) fn new( + func: Function, + infer: Arc, + sink: &'a mut DiagnosticSink<'b>, + ) -> ExprValidator<'a, 'b> { + ExprValidator { func, infer, sink } + } + + pub(crate) fn validate_body(&mut self, db: &impl HirDatabase) { + let body = self.func.body(db); + for e in body.exprs() { + match e { + (id, Expr::StructLit { path, fields, spread }) => { + self.validate_struct_literal(id, path, fields, spread, db) + } + _ => (), + } + } + } + + fn validate_struct_literal( + &mut self, + id: ExprId, + _path: &Option, + fields: &Vec, + spread: &Option, + db: &impl HirDatabase, + ) { + if let Some(_) = spread { + return; + } + let lit_fields: FxHashSet<_> = fields.into_iter().map(|f| &f.name).collect(); + let struct_ty = &self.infer[id]; + if let Some((AdtDef::Struct(s), _)) = struct_ty.as_adt() { + let missed_fields: Vec = s + .fields(db) + .iter() + .filter_map(|f| { + let name = f.name(db); + if lit_fields.contains(&name) { + None + } else { + Some(name) + } + }) + .collect(); + if missed_fields.is_empty() { + return; + } + let source_map = self.func.body_source_map(db); + let file_id = self.func.source(db).0; + let source_file = db.parse(file_id.original_file(db)); + if let Some(field_list_node) = source_map + .expr_syntax(id) + .map(|ptr| ptr.to_node(&source_file)) + .and_then(StructLit::cast) + .and_then(|lit| lit.named_field_list()) + { + let field_list_ptr = AstPtr::new(field_list_node); + self.sink.push(MissingFields { + file: file_id, + field_list: field_list_ptr, + missed_fields, + }) + } + } + } +} 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() { assert_snapshot_matches!(diagnostics, @r###" "baz: 62": no such field +"{\n foo: 92,\n baz: 62,\n }": fill structure fields "### ); } -- cgit v1.2.3