From 7731714578d4ae6eb7a54ead2e51ae032e9a700a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 12 Jun 2021 22:05:23 +0300 Subject: internal: move diagnostics infra to hir --- crates/hir/src/diagnostics.rs | 35 +++++++++++- crates/hir/src/diagnostics_sink.rs | 109 +++++++++++++++++++++++++++++++++++++ crates/hir/src/lib.rs | 32 ++++++----- 3 files changed, 159 insertions(+), 17 deletions(-) create mode 100644 crates/hir/src/diagnostics_sink.rs (limited to 'crates/hir') diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 26dbcd86a..8a7c3a4fd 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -11,9 +11,8 @@ use hir_expand::{name::Name, HirFileId, InFile}; use stdx::format_to; use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; -pub use hir_ty::{ - diagnostics::IncorrectCase, - diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder}, +pub use crate::diagnostics_sink::{ + Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, }; // Diagnostic: unresolved-module @@ -578,3 +577,33 @@ impl Diagnostic for InternalBailedOut { self } } + +pub use hir_ty::diagnostics::IncorrectCase; + +impl Diagnostic for IncorrectCase { + fn code(&self) -> DiagnosticCode { + DiagnosticCode("incorrect-ident-case") + } + + fn message(&self) -> String { + format!( + "{} `{}` should have {} name, e.g. `{}`", + self.ident_type, + self.ident_text, + self.expected_case.to_string(), + self.suggested_text + ) + } + + fn display_source(&self) -> InFile { + InFile::new(self.file, self.ident.clone().into()) + } + + fn as_any(&self) -> &(dyn Any + Send + 'static) { + self + } + + fn is_experimental(&self) -> bool { + true + } +} diff --git a/crates/hir/src/diagnostics_sink.rs b/crates/hir/src/diagnostics_sink.rs new file mode 100644 index 000000000..084fa8b06 --- /dev/null +++ b/crates/hir/src/diagnostics_sink.rs @@ -0,0 +1,109 @@ +//! Semantic errors and warnings. +//! +//! The `Diagnostic` trait defines a trait object which can represent any +//! diagnostic. +//! +//! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating +//! a `DiagnosticSink`, you supply a callback which can react to a `dyn +//! Diagnostic` or to any concrete diagnostic (downcasting is used internally). +//! +//! Because diagnostics store file offsets, it's a bad idea to store them +//! directly in salsa. For this reason, every hir subsytem defines it's own +//! strongly-typed closed set of diagnostics which use hir ids internally, are +//! stored in salsa and do *not* implement the `Diagnostic` trait. Instead, a +//! subsystem provides a separate, non-query-based API which can walk all stored +//! values and transform them into instances of `Diagnostic`. + +use std::{any::Any, fmt}; + +use hir_expand::InFile; +use syntax::SyntaxNodePtr; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct DiagnosticCode(pub &'static str); + +impl DiagnosticCode { + pub fn as_str(&self) -> &str { + self.0 + } +} + +pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { + fn code(&self) -> DiagnosticCode; + fn message(&self) -> String; + /// Source element that triggered the diagnostics. + /// + /// Note that this should reflect "semantics", rather than specific span we + /// want to highlight. When rendering the diagnostics into an error message, + /// the IDE will fetch the `SyntaxNode` and will narrow the span + /// appropriately. + fn display_source(&self) -> InFile; + fn as_any(&self) -> &(dyn Any + Send + 'static); + fn is_experimental(&self) -> bool { + false + } +} + +pub struct DiagnosticSink<'a> { + callbacks: Vec Result<(), ()> + 'a>>, + filters: Vec bool + 'a>>, + default_callback: Box, +} + +impl<'a> DiagnosticSink<'a> { + pub fn push(&mut self, d: impl Diagnostic) { + let d: &dyn Diagnostic = &d; + self._push(d); + } + + fn _push(&mut self, d: &dyn Diagnostic) { + for filter in &mut self.filters { + if !filter(d) { + return; + } + } + for cb in &mut self.callbacks { + match cb(d) { + Ok(()) => return, + Err(()) => (), + } + } + (self.default_callback)(d) + } +} + +pub struct DiagnosticSinkBuilder<'a> { + callbacks: Vec Result<(), ()> + 'a>>, + filters: Vec bool + 'a>>, +} + +impl<'a> DiagnosticSinkBuilder<'a> { + pub fn new() -> Self { + Self { callbacks: Vec::new(), filters: Vec::new() } + } + + pub fn filter bool + 'a>(mut self, cb: F) -> Self { + self.filters.push(Box::new(cb)); + self + } + + pub fn on(mut self, mut cb: F) -> Self { + let cb = move |diag: &dyn Diagnostic| match diag.as_any().downcast_ref::() { + Some(d) => { + cb(d); + Ok(()) + } + None => Err(()), + }; + self.callbacks.push(Box::new(cb)); + self + } + + pub fn build(self, default_callback: F) -> DiagnosticSink<'a> { + DiagnosticSink { + callbacks: self.callbacks, + filters: self.filters, + default_callback: Box::new(default_callback), + } + } +} diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index dd5515c2b..2468c0dc6 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -27,6 +27,7 @@ mod attrs; mod has_source; pub mod diagnostics; +pub mod diagnostics_sink; pub mod db; mod display; @@ -35,13 +36,6 @@ use std::{iter, sync::Arc}; use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, Edition, FileId}; -use diagnostics::{ - BreakOutsideOfLoop, InactiveCode, InternalBailedOut, MacroError, MismatchedArgCount, - MissingFields, MissingOkOrSomeInTailExpr, MissingPatFields, MissingUnsafe, NoSuchField, - RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro, - UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule, - UnresolvedProcMacro, -}; use either::Either; use hir_def::{ adt::{ReprKind, VariantData}, @@ -64,8 +58,7 @@ use hir_ty::{ consteval::ConstExt, could_unify, diagnostics::BodyValidationDiagnostic, - diagnostics_sink::DiagnosticSink, - method_resolution::{self, def_crates, TyFingerprint}, + method_resolution::{self, TyFingerprint}, primitive::UintTy, subst_prefix, traits::FnTrait, @@ -87,7 +80,14 @@ use tt::{Ident, Leaf, Literal, TokenTree}; use crate::{ db::{DefDatabase, HirDatabase}, - diagnostics::MissingMatchArms, + diagnostics::{ + BreakOutsideOfLoop, InactiveCode, InternalBailedOut, MacroError, MismatchedArgCount, + MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, MissingPatFields, + MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, + UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, + UnresolvedModule, UnresolvedProcMacro, + }, + diagnostics_sink::DiagnosticSink, }; pub use crate::{ @@ -361,7 +361,9 @@ impl ModuleDef { None => return, }; - hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink) + for diag in hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id) { + sink.push(diag) + } } } @@ -1225,7 +1227,9 @@ impl Function { } } - hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); + for diag in hir_ty::diagnostics::validate_module_item(db, krate, self.id.into()) { + sink.push(diag) + } } /// Whether this function declaration has a definition. @@ -1944,7 +1948,7 @@ impl Impl { } pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec { - let def_crates = match def_crates(db, &ty, krate) { + let def_crates = match method_resolution::def_crates(db, &ty, krate) { Some(def_crates) => def_crates, None => return Vec::new(), }; @@ -2350,7 +2354,7 @@ impl Type { krate: Crate, mut callback: impl FnMut(AssocItem) -> Option, ) -> Option { - for krate in def_crates(db, &self.ty, krate.id)? { + for krate in method_resolution::def_crates(db, &self.ty, krate.id)? { let impls = db.inherent_impls_in_crate(krate); for impl_def in impls.for_self_ty(&self.ty) { -- cgit v1.2.3