From fcca35969dd7c63a83ee34c4ce7d54cefdb72bbe Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 16:28:47 +0300 Subject: [PATCH] allow dyn diagnostics --- crates/ra_hir/src/code_model_api.rs | 8 ++-- crates/ra_hir/src/diagnostics.rs | 62 ++++++++++++++++++++++++++-- crates/ra_hir/src/expr.rs | 4 +- crates/ra_hir/src/ty/infer.rs | 39 ++++++++++++++--- crates/ra_ide_api/src/diagnostics.rs | 26 ++++-------- crates/ra_syntax/src/ptr.rs | 6 +++ 6 files changed, 112 insertions(+), 33 deletions(-) diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 58481e715e7..a37d960a148 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -17,7 +17,7 @@ use crate::{ ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, impl_block::ImplBlock, resolve::Resolver, - diagnostics::FunctionDiagnostic, + diagnostics::Diagnostics, }; /// hir::Crate describes a single crate. It's the main interface with which @@ -521,8 +521,10 @@ impl Function { r } - pub fn diagnostics(&self, db: &impl HirDatabase) -> Vec { - self.infer(db).diagnostics() + pub fn diagnostics(&self, db: &impl HirDatabase) -> Diagnostics { + let mut res = Diagnostics::default(); + self.infer(db).add_diagnostics(db, *self, &mut res); + res } } diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index 82aff9cee0d..46a3fdd4794 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs @@ -1,6 +1,60 @@ -use crate::{expr::ExprId}; +use std::{fmt, any::Any}; -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum FunctionDiagnostic { - NoSuchField { expr: ExprId, field: usize }, +use ra_syntax::{SyntaxNodePtr, AstPtr, ast}; + +use crate::HirFileId; + +pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { + fn file(&self) -> HirFileId; + fn syntax_node(&self) -> SyntaxNodePtr; + fn message(&self) -> String; + fn as_any(&self) -> &(Any + Send + 'static); +} + +impl dyn Diagnostic { + pub fn downcast_ref(&self) -> Option<&D> { + self.as_any().downcast_ref() + } +} + +#[derive(Debug, Default)] +pub struct Diagnostics { + data: Vec>, +} + +impl Diagnostics { + pub fn push(&mut self, d: impl Diagnostic) { + self.data.push(Box::new(d)) + } + + pub fn iter<'a>(&'a self) -> impl Iterator + 'a { + self.data.iter().map(|it| it.as_ref()) + } +} + +#[derive(Debug)] +pub struct NoSuchField { + pub(crate) file: HirFileId, + pub(crate) field: AstPtr, +} + +impl NoSuchField { + pub fn field(&self) -> AstPtr { + self.field + } +} + +impl Diagnostic for NoSuchField { + fn file(&self) -> HirFileId { + self.file + } + fn syntax_node(&self) -> SyntaxNodePtr { + self.field.into() + } + fn message(&self) -> String { + "no such field".to_string() + } + fn as_any(&self) -> &(Any + Send + 'static) { + self + } } diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 31af5d24108..a85422955b9 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -140,8 +140,8 @@ impl BodySourceMap { 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() + pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr { + self.field_map[&(expr, field)].clone() } } diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 269b5162e31..02708ba0f4a 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -37,7 +37,8 @@ use crate::{ adt::VariantDef, resolve::{Resolver, Resolution}, nameres::Namespace, - diagnostics::FunctionDiagnostic, + ty::infer::diagnostics::InferenceDiagnostic, + diagnostics::Diagnostics, }; use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; @@ -97,7 +98,7 @@ pub struct InferenceResult { field_resolutions: FxHashMap, /// For each associated item record what it resolves to assoc_resolutions: FxHashMap, - diagnostics: Vec, + diagnostics: Vec, pub(super) type_of_expr: ArenaMap, pub(super) type_of_pat: ArenaMap, } @@ -115,8 +116,13 @@ 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() + pub(crate) fn add_diagnostics( + &self, + db: &impl HirDatabase, + owner: Function, + diagnostics: &mut Diagnostics, + ) { + self.diagnostics.iter().for_each(|it| it.add_to(db, owner, diagnostics)) } } @@ -148,7 +154,7 @@ struct InferenceContext<'a, D: HirDatabase> { assoc_resolutions: FxHashMap, type_of_expr: ArenaMap, type_of_pat: ArenaMap, - diagnostics: Vec, + diagnostics: Vec, /// The return type of the function being inferred. return_ty: Ty, } @@ -928,7 +934,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { .and_then(|it| match it.field(self.db, &field.name) { Some(field) => Some(field), None => { - self.diagnostics.push(FunctionDiagnostic::NoSuchField { + self.diagnostics.push(InferenceDiagnostic::NoSuchField { expr: tgt_expr, field: field_idx, }); @@ -1261,3 +1267,24 @@ impl Expectation { Expectation { ty: Ty::Unknown } } } + +mod diagnostics { + use crate::{expr::ExprId, diagnostics::{Diagnostics, NoSuchField}, HirDatabase, Function}; + + #[derive(Debug, PartialEq, Eq, Clone)] + pub(super) enum InferenceDiagnostic { + NoSuchField { expr: ExprId, field: usize }, + } + + impl InferenceDiagnostic { + pub(super) fn add_to(&self, db: &impl HirDatabase, owner: Function, acc: &mut Diagnostics) { + match self { + InferenceDiagnostic::NoSuchField { expr, field } => { + let (file, _) = owner.source(db); + let field = owner.body_source_map(db).field_syntax(*expr, *field); + acc.push(NoSuchField { file, field }) + } + } + } + } +} diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index f662f7e2f1b..943fd2f539a 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}; use ra_db::SourceDatabase; use ra_syntax::{ Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, - ast::{self, AstNode, NameOwner}, + ast::{self, AstNode}, }; use ra_text_edit::{TextEdit, TextEditBuilder}; @@ -161,23 +161,13 @@ fn check_module( } fn check_function(acc: &mut Vec, db: &RootDatabase, function: hir::Function) { - let (_file_id, fn_def) = function.source(db); - let source_file = fn_def.syntax().ancestors().find_map(ast::SourceFile::cast).unwrap(); - let source_map = function.body_source_map(db); - for d in function.diagnostics(db) { - match d { - hir::diagnostics::FunctionDiagnostic::NoSuchField { expr, field } => { - if let Some(field) = source_map.field_syntax(expr, field) { - let field = field.to_node(&source_file); - acc.push(Diagnostic { - message: "no such field".into(), - range: field.syntax().range(), - severity: Severity::Error, - fix: None, - }) - } - } - } + for d in function.diagnostics(db).iter() { + acc.push(Diagnostic { + message: d.message(), + range: d.syntax_node().range(), + severity: Severity::Error, + fix: None, + }) } } diff --git a/crates/ra_syntax/src/ptr.rs b/crates/ra_syntax/src/ptr.rs index aae590cb62a..d8de1c4c1b5 100644 --- a/crates/ra_syntax/src/ptr.rs +++ b/crates/ra_syntax/src/ptr.rs @@ -64,6 +64,12 @@ impl AstPtr { } } +impl From> for SyntaxNodePtr { + fn from(ptr: AstPtr) -> SyntaxNodePtr { + ptr.raw + } +} + #[test] fn test_local_syntax_ptr() { use crate::{ast, AstNode};