mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 12:29:21 +00:00
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},
|
ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId},
|
||||||
impl_block::ImplBlock,
|
impl_block::ImplBlock,
|
||||||
resolve::Resolver,
|
resolve::Resolver,
|
||||||
diagnostics::FunctionDiagnostic,
|
diagnostics::Diagnostics,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// hir::Crate describes a single crate. It's the main interface with which
|
/// hir::Crate describes a single crate. It's the main interface with which
|
||||||
|
@ -521,8 +521,10 @@ impl Function {
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diagnostics(&self, db: &impl HirDatabase) -> Vec<FunctionDiagnostic> {
|
pub fn diagnostics(&self, db: &impl HirDatabase) -> Diagnostics {
|
||||||
self.infer(db).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)]
|
use ra_syntax::{SyntaxNodePtr, AstPtr, ast};
|
||||||
pub enum FunctionDiagnostic {
|
|
||||||
NoSuchField { expr: ExprId, field: usize },
|
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()
|
self.pat_map.get(&SyntaxNodePtr::new(node.syntax())).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn field_syntax(&self, expr: ExprId, field: usize) -> Option<AstPtr<ast::NamedField>> {
|
pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::NamedField> {
|
||||||
self.field_map.get(&(expr, field)).cloned()
|
self.field_map[&(expr, field)].clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,8 @@ use crate::{
|
||||||
adt::VariantDef,
|
adt::VariantDef,
|
||||||
resolve::{Resolver, Resolution},
|
resolve::{Resolver, Resolution},
|
||||||
nameres::Namespace,
|
nameres::Namespace,
|
||||||
diagnostics::FunctionDiagnostic,
|
ty::infer::diagnostics::InferenceDiagnostic,
|
||||||
|
diagnostics::Diagnostics,
|
||||||
};
|
};
|
||||||
use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor};
|
use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor};
|
||||||
|
|
||||||
|
@ -97,7 +98,7 @@ pub struct InferenceResult {
|
||||||
field_resolutions: FxHashMap<ExprId, StructField>,
|
field_resolutions: FxHashMap<ExprId, StructField>,
|
||||||
/// For each associated item record what it resolves to
|
/// For each associated item record what it resolves to
|
||||||
assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>,
|
assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>,
|
||||||
diagnostics: Vec<FunctionDiagnostic>,
|
diagnostics: Vec<InferenceDiagnostic>,
|
||||||
pub(super) type_of_expr: ArenaMap<ExprId, Ty>,
|
pub(super) type_of_expr: ArenaMap<ExprId, Ty>,
|
||||||
pub(super) type_of_pat: ArenaMap<PatId, 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> {
|
pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> {
|
||||||
self.assoc_resolutions.get(&id.into()).map(|it| *it)
|
self.assoc_resolutions.get(&id.into()).map(|it| *it)
|
||||||
}
|
}
|
||||||
pub(crate) fn diagnostics(&self) -> Vec<FunctionDiagnostic> {
|
pub(crate) fn add_diagnostics(
|
||||||
self.diagnostics.clone()
|
&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>,
|
assoc_resolutions: FxHashMap<ExprOrPatId, ImplItem>,
|
||||||
type_of_expr: ArenaMap<ExprId, Ty>,
|
type_of_expr: ArenaMap<ExprId, Ty>,
|
||||||
type_of_pat: ArenaMap<PatId, Ty>,
|
type_of_pat: ArenaMap<PatId, Ty>,
|
||||||
diagnostics: Vec<FunctionDiagnostic>,
|
diagnostics: Vec<InferenceDiagnostic>,
|
||||||
/// The return type of the function being inferred.
|
/// The return type of the function being inferred.
|
||||||
return_ty: Ty,
|
return_ty: Ty,
|
||||||
}
|
}
|
||||||
|
@ -928,7 +934,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
.and_then(|it| match it.field(self.db, &field.name) {
|
.and_then(|it| match it.field(self.db, &field.name) {
|
||||||
Some(field) => Some(field),
|
Some(field) => Some(field),
|
||||||
None => {
|
None => {
|
||||||
self.diagnostics.push(FunctionDiagnostic::NoSuchField {
|
self.diagnostics.push(InferenceDiagnostic::NoSuchField {
|
||||||
expr: tgt_expr,
|
expr: tgt_expr,
|
||||||
field: field_idx,
|
field: field_idx,
|
||||||
});
|
});
|
||||||
|
@ -1261,3 +1267,24 @@ impl Expectation {
|
||||||
Expectation { ty: Ty::Unknown }
|
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_db::SourceDatabase;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
Location, SourceFile, SyntaxKind, TextRange, SyntaxNode,
|
Location, SourceFile, SyntaxKind, TextRange, SyntaxNode,
|
||||||
ast::{self, AstNode, NameOwner},
|
ast::{self, AstNode},
|
||||||
|
|
||||||
};
|
};
|
||||||
use ra_text_edit::{TextEdit, TextEditBuilder};
|
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) {
|
fn check_function(acc: &mut Vec<Diagnostic>, db: &RootDatabase, function: hir::Function) {
|
||||||
let (_file_id, fn_def) = function.source(db);
|
for d in function.diagnostics(db).iter() {
|
||||||
let source_file = fn_def.syntax().ancestors().find_map(ast::SourceFile::cast).unwrap();
|
acc.push(Diagnostic {
|
||||||
let source_map = function.body_source_map(db);
|
message: d.message(),
|
||||||
for d in function.diagnostics(db) {
|
range: d.syntax_node().range(),
|
||||||
match d {
|
severity: Severity::Error,
|
||||||
hir::diagnostics::FunctionDiagnostic::NoSuchField { expr, field } => {
|
fix: None,
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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]
|
#[test]
|
||||||
fn test_local_syntax_ptr() {
|
fn test_local_syntax_ptr() {
|
||||||
use crate::{ast, AstNode};
|
use crate::{ast, AstNode};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue