allow dyn diagnostics
This commit is contained in:
parent
7e8f17188e
commit
fcca35969d
6 changed files with 112 additions and 33 deletions
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
Loading…
Add table
Reference in a new issue