Add a builder for DiagnosticSink

This commit is contained in:
Jonas Schievink 2020-07-24 16:30:12 +02:00
parent c3defe2532
commit 6f02befee4
4 changed files with 101 additions and 87 deletions

View file

@ -1,6 +1,8 @@
//! FIXME: write short doc here //! FIXME: write short doc here
pub use hir_def::diagnostics::UnresolvedModule; pub use hir_def::diagnostics::UnresolvedModule;
pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; pub use hir_expand::diagnostics::{
AstDiagnostic, Diagnostic, DiagnosticSink, DiagnosticSinkBuilder,
};
pub use hir_ty::diagnostics::{ pub use hir_ty::diagnostics::{
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField,
}; };

View file

@ -48,23 +48,6 @@ pub struct DiagnosticSink<'a> {
} }
impl<'a> DiagnosticSink<'a> { impl<'a> DiagnosticSink<'a> {
/// FIXME: split `new` and `on` into a separate builder type
pub fn new(cb: impl FnMut(&dyn Diagnostic) + 'a) -> DiagnosticSink<'a> {
DiagnosticSink { callbacks: Vec::new(), default_callback: Box::new(cb) }
}
pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> DiagnosticSink<'a> {
let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::<D>() {
Some(d) => {
cb(d);
Ok(())
}
None => Err(()),
};
self.callbacks.push(Box::new(cb));
self
}
pub fn push(&mut self, d: impl Diagnostic) { pub fn push(&mut self, d: impl Diagnostic) {
let d: &dyn Diagnostic = &d; let d: &dyn Diagnostic = &d;
self._push(d); self._push(d);
@ -80,3 +63,29 @@ impl<'a> DiagnosticSink<'a> {
(self.default_callback)(d) (self.default_callback)(d)
} }
} }
pub struct DiagnosticSinkBuilder<'a> {
callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
}
impl<'a> DiagnosticSinkBuilder<'a> {
pub fn new() -> Self {
Self { callbacks: Vec::new() }
}
pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> Self {
let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::<D>() {
Some(d) => {
cb(d);
Ok(())
}
None => Err(()),
};
self.callbacks.push(Box::new(cb));
self
}
pub fn build<F: FnMut(&dyn Diagnostic) + 'a>(self, default_callback: F) -> DiagnosticSink<'a> {
DiagnosticSink { callbacks: self.callbacks, default_callback: Box::new(default_callback) }
}
}

View file

@ -248,7 +248,7 @@ impl AstDiagnostic for MismatchedArgCount {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
use hir_expand::diagnostics::{Diagnostic, DiagnosticSink}; use hir_expand::diagnostics::{Diagnostic, DiagnosticSinkBuilder};
use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
use ra_syntax::{TextRange, TextSize}; use ra_syntax::{TextRange, TextSize};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@ -280,7 +280,7 @@ mod tests {
} }
for f in fns { for f in fns {
let mut sink = DiagnosticSink::new(&mut cb); let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
validate_body(self, f.into(), &mut sink); validate_body(self, f.into(), &mut sink);
} }
} }

View file

@ -7,7 +7,7 @@
use std::cell::RefCell; use std::cell::RefCell;
use hir::{ use hir::{
diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}, diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSinkBuilder},
HasSource, HirDisplay, Semantics, VariantDef, HasSource, HirDisplay, Semantics, VariantDef,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -48,19 +48,13 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
check_struct_shorthand_initialization(&mut res, file_id, &node); check_struct_shorthand_initialization(&mut res, file_id, &node);
} }
let res = RefCell::new(res); let res = RefCell::new(res);
let mut sink = DiagnosticSink::new(|d| { let mut sink = DiagnosticSinkBuilder::new()
res.borrow_mut().push(Diagnostic {
message: d.message(),
range: sema.diagnostics_range(d).range,
severity: Severity::Error,
fix: None,
})
})
.on::<hir::diagnostics::UnresolvedModule, _>(|d| { .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
let original_file = d.source().file_id.original_file(db); let original_file = d.source().file_id.original_file(db);
let fix = Fix::new( let fix = Fix::new(
"Create module", "Create module",
FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }.into(), FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }
.into(),
); );
res.borrow_mut().push(Diagnostic { res.borrow_mut().push(Diagnostic {
range: sema.diagnostics_range(d).range, range: sema.diagnostics_range(d).range,
@ -87,7 +81,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
let edit = { let edit = {
let mut builder = TextEditBuilder::default(); let mut builder = TextEditBuilder::default();
algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); algo::diff(&d.ast(db).syntax(), &field_list.syntax())
.into_text_edit(&mut builder);
builder.finish() builder.finish()
}; };
Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into())) Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
@ -120,6 +115,14 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
severity: Severity::Error, severity: Severity::Error,
fix: missing_struct_field_fix(&sema, file_id, d), fix: missing_struct_field_fix(&sema, file_id, d),
}) })
})
.build(|d| {
res.borrow_mut().push(Diagnostic {
message: d.message(),
range: sema.diagnostics_range(d).range,
severity: Severity::Error,
fix: None,
})
}); });
if let Some(m) = sema.to_module_def(file_id) { if let Some(m) = sema.to_module_def(file_id) {