allow dyn diagnostics

This commit is contained in:
Aleksey Kladov 2019-03-23 16:28:47 +03:00
parent 7e8f17188e
commit fcca35969d
6 changed files with 112 additions and 33 deletions

View file

@ -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<FunctionDiagnostic> {
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
}
}

View file

@ -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<D: Diagnostic>(&self) -> Option<&D> {
self.as_any().downcast_ref()
}
}
#[derive(Debug, Default)]
pub struct Diagnostics {
data: Vec<Box<dyn Diagnostic>>,
}
impl Diagnostics {
pub fn push(&mut self, d: impl Diagnostic) {
self.data.push(Box::new(d))
}
pub fn iter<'a>(&'a self) -> impl Iterator<Item = &'a dyn Diagnostic> + 'a {
self.data.iter().map(|it| it.as_ref())
}
}
#[derive(Debug)]
pub struct NoSuchField {
pub(crate) file: HirFileId,
pub(crate) field: AstPtr<ast::NamedField>,
}
impl NoSuchField {
pub fn field(&self) -> AstPtr<ast::NamedField> {
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
}
}

View file

@ -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<AstPtr<ast::NamedField>> {
self.field_map.get(&(expr, field)).cloned()
pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::NamedField> {
self.field_map[&(expr, field)].clone()
}
}

View file

@ -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<ExprId, StructField>,
/// For each associated item record what it resolves to
assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>,
diagnostics: Vec<FunctionDiagnostic>,
diagnostics: Vec<InferenceDiagnostic>,
pub(super) type_of_expr: ArenaMap<ExprId, Ty>,
pub(super) type_of_pat: ArenaMap<PatId, Ty>,
}
@ -115,8 +116,13 @@ impl InferenceResult {
pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> {
self.assoc_resolutions.get(&id.into()).map(|it| *it)
}
pub(crate) fn diagnostics(&self) -> Vec<FunctionDiagnostic> {
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<ExprOrPatId, ImplItem>,
type_of_expr: ArenaMap<ExprId, Ty>,
type_of_pat: ArenaMap<PatId, Ty>,
diagnostics: Vec<FunctionDiagnostic>,
diagnostics: Vec<InferenceDiagnostic>,
/// 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 })
}
}
}
}
}

View file

@ -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<Diagnostic>, 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,
})
}
}

View file

@ -64,6 +64,12 @@ impl<N: AstNode> AstPtr<N> {
}
}
impl<N: AstNode> From<AstPtr<N>> for SyntaxNodePtr {
fn from(ptr: AstPtr<N>) -> SyntaxNodePtr {
ptr.raw
}
}
#[test]
fn test_local_syntax_ptr() {
use crate::{ast, AstNode};