From 7e8f17188efcecfdfd1afbbc894a53c65985f836 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 21 Mar 2019 22:13:11 +0300 Subject: diagnostics --- crates/ra_hir/src/code_model_api.rs | 5 +++++ crates/ra_hir/src/diagnostics.rs | 6 ++++++ crates/ra_hir/src/expr.rs | 15 +++++++++++++-- crates/ra_hir/src/lib.rs | 1 + crates/ra_hir/src/ty/infer.rs | 23 ++++++++++++++++++++--- 5 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 crates/ra_hir/src/diagnostics.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 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::{ ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, impl_block::ImplBlock, resolve::Resolver, + diagnostics::FunctionDiagnostic, }; /// hir::Crate describes a single crate. It's the main interface with which @@ -519,6 +520,10 @@ impl Function { let r = if !p.params.is_empty() { r.push_generic_params_scope(p) } else { r }; r } + + pub fn diagnostics(&self, db: &impl HirDatabase) -> Vec { + self.infer(db).diagnostics() + } } impl 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 @@ +use crate::{expr::ExprId}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum FunctionDiagnostic { + NoSuchField { expr: ExprId, field: usize }, +} 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; use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; use ra_syntax::{ - SyntaxNodePtr, AstNode, + SyntaxNodePtr, AstPtr, AstNode, ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralFlavor, TypeAscriptionOwner} }; @@ -54,6 +54,7 @@ pub struct BodySourceMap { expr_map_back: ArenaMap, pat_map: FxHashMap, pat_map_back: ArenaMap, + field_map: FxHashMap<(ExprId, usize), AstPtr>, } impl Body { @@ -138,6 +139,10 @@ impl BodySourceMap { pub fn node_pat(&self, node: &ast::Pat) -> Option { self.pat_map.get(&SyntaxNodePtr::new(node.syntax())).cloned() } + + pub fn field_syntax(&self, expr: ExprId, field: usize) -> Option> { + self.field_map.get(&(expr, field)).cloned() + } } #[derive(Debug, Clone, Eq, PartialEq)] @@ -629,8 +634,10 @@ impl ExprCollector { } 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() + .inspect(|field| field_ptrs.push(AstPtr::new(*field))) .map(|field| StructLitField { name: field .name_ref() @@ -657,7 +664,11 @@ impl ExprCollector { Vec::new() }; let spread = e.spread().map(|s| self.collect_expr(s)); - self.alloc_expr(Expr::StructLit { path, fields, spread }, syntax_ptr) + let res = self.alloc_expr(Expr::StructLit { path, fields, spread }, syntax_ptr); + for (i, ptr) in field_ptrs.into_iter().enumerate() { + self.source_map.field_map.insert((res, i), ptr); + } + res } ast::ExprKind::FieldExpr(e) => { 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; mod generics; mod docs; mod resolve; +pub mod diagnostics; mod code_model_api; mod 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::{ path::{GenericArgs, GenericArg}, adt::VariantDef, resolve::{Resolver, Resolution}, - nameres::Namespace + nameres::Namespace, + diagnostics::FunctionDiagnostic, }; use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; @@ -96,6 +97,7 @@ pub struct InferenceResult { field_resolutions: FxHashMap, /// For each associated item record what it resolves to assoc_resolutions: FxHashMap, + diagnostics: Vec, pub(super) type_of_expr: ArenaMap, pub(super) type_of_pat: ArenaMap, } @@ -113,6 +115,9 @@ impl InferenceResult { pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option { self.assoc_resolutions.get(&id.into()).map(|it| *it) } + pub(crate) fn diagnostics(&self) -> Vec { + self.diagnostics.clone() + } } impl Index for InferenceResult { @@ -143,6 +148,7 @@ struct InferenceContext<'a, D: HirDatabase> { assoc_resolutions: FxHashMap, type_of_expr: ArenaMap, type_of_pat: ArenaMap, + diagnostics: Vec, /// The return type of the function being inferred. return_ty: Ty, } @@ -155,6 +161,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { assoc_resolutions: FxHashMap::default(), type_of_expr: ArenaMap::default(), type_of_pat: ArenaMap::default(), + diagnostics: Vec::default(), var_unification_table: InPlaceUnificationTable::new(), return_ty: Ty::Unknown, // set in collect_fn_signature db, @@ -181,6 +188,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { assoc_resolutions: self.assoc_resolutions, type_of_expr: expr_types, type_of_pat: pat_types, + diagnostics: self.diagnostics, } } @@ -915,9 +923,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Expr::StructLit { path, fields, spread } => { let (ty, def_id) = self.resolve_variant(path.as_ref()); let substs = ty.substs().unwrap_or_else(Substs::empty); - for field in fields { + for (field_idx, field) in fields.into_iter().enumerate() { let field_ty = def_id - .and_then(|it| it.field(self.db, &field.name)) + .and_then(|it| match it.field(self.db, &field.name) { + Some(field) => Some(field), + None => { + self.diagnostics.push(FunctionDiagnostic::NoSuchField { + expr: tgt_expr, + field: field_idx, + }); + None + } + }) .map_or(Ty::Unknown, |field| field.ty(self.db)) .subst(&substs); self.infer_expr(field.expr, &Expectation::has_type(field_ty)); -- cgit v1.2.3