mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 01:17:27 +00:00
Add a builder for DiagnosticSink
This commit is contained in:
parent
c3defe2532
commit
6f02befee4
4 changed files with 101 additions and 87 deletions
|
@ -1,6 +1,8 @@
|
|||
//! FIXME: write short doc here
|
||||
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::{
|
||||
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField,
|
||||
};
|
||||
|
|
|
@ -48,23 +48,6 @@ pub struct 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) {
|
||||
let d: &dyn Diagnostic = &d;
|
||||
self._push(d);
|
||||
|
@ -80,3 +63,29 @@ impl<'a> DiagnosticSink<'a> {
|
|||
(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) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -248,7 +248,7 @@ impl AstDiagnostic for MismatchedArgCount {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
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_syntax::{TextRange, TextSize};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
@ -280,7 +280,7 @@ mod tests {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
use std::cell::RefCell;
|
||||
|
||||
use hir::{
|
||||
diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink},
|
||||
diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSinkBuilder},
|
||||
HasSource, HirDisplay, Semantics, VariantDef,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
@ -48,79 +48,82 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
|||
check_struct_shorthand_initialization(&mut res, file_id, &node);
|
||||
}
|
||||
let res = RefCell::new(res);
|
||||
let mut sink = DiagnosticSink::new(|d| {
|
||||
res.borrow_mut().push(Diagnostic {
|
||||
message: d.message(),
|
||||
range: sema.diagnostics_range(d).range,
|
||||
severity: Severity::Error,
|
||||
fix: None,
|
||||
let mut sink = DiagnosticSinkBuilder::new()
|
||||
.on::<hir::diagnostics::UnresolvedModule, _>(|d| {
|
||||
let original_file = d.source().file_id.original_file(db);
|
||||
let fix = Fix::new(
|
||||
"Create module",
|
||||
FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }
|
||||
.into(),
|
||||
);
|
||||
res.borrow_mut().push(Diagnostic {
|
||||
range: sema.diagnostics_range(d).range,
|
||||
message: d.message(),
|
||||
severity: Severity::Error,
|
||||
fix: Some(fix),
|
||||
})
|
||||
})
|
||||
})
|
||||
.on::<hir::diagnostics::UnresolvedModule, _>(|d| {
|
||||
let original_file = d.source().file_id.original_file(db);
|
||||
let fix = Fix::new(
|
||||
"Create module",
|
||||
FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }.into(),
|
||||
);
|
||||
res.borrow_mut().push(Diagnostic {
|
||||
range: sema.diagnostics_range(d).range,
|
||||
message: d.message(),
|
||||
severity: Severity::Error,
|
||||
fix: Some(fix),
|
||||
})
|
||||
})
|
||||
.on::<hir::diagnostics::MissingFields, _>(|d| {
|
||||
// Note that although we could add a diagnostics to
|
||||
// fill the missing tuple field, e.g :
|
||||
// `struct A(usize);`
|
||||
// `let a = A { 0: () }`
|
||||
// but it is uncommon usage and it should not be encouraged.
|
||||
let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
|
||||
None
|
||||
} else {
|
||||
let mut field_list = d.ast(db);
|
||||
for f in d.missed_fields.iter() {
|
||||
let field =
|
||||
make::record_field(make::name_ref(&f.to_string()), Some(make::expr_unit()));
|
||||
field_list = field_list.append_field(&field);
|
||||
}
|
||||
.on::<hir::diagnostics::MissingFields, _>(|d| {
|
||||
// Note that although we could add a diagnostics to
|
||||
// fill the missing tuple field, e.g :
|
||||
// `struct A(usize);`
|
||||
// `let a = A { 0: () }`
|
||||
// but it is uncommon usage and it should not be encouraged.
|
||||
let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
|
||||
None
|
||||
} else {
|
||||
let mut field_list = d.ast(db);
|
||||
for f in d.missed_fields.iter() {
|
||||
let field =
|
||||
make::record_field(make::name_ref(&f.to_string()), Some(make::expr_unit()));
|
||||
field_list = field_list.append_field(&field);
|
||||
}
|
||||
|
||||
let edit = {
|
||||
let mut builder = TextEditBuilder::default();
|
||||
algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder);
|
||||
builder.finish()
|
||||
let edit = {
|
||||
let mut builder = TextEditBuilder::default();
|
||||
algo::diff(&d.ast(db).syntax(), &field_list.syntax())
|
||||
.into_text_edit(&mut builder);
|
||||
builder.finish()
|
||||
};
|
||||
Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
|
||||
};
|
||||
Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
|
||||
};
|
||||
|
||||
res.borrow_mut().push(Diagnostic {
|
||||
range: sema.diagnostics_range(d).range,
|
||||
message: d.message(),
|
||||
severity: Severity::Error,
|
||||
fix,
|
||||
res.borrow_mut().push(Diagnostic {
|
||||
range: sema.diagnostics_range(d).range,
|
||||
message: d.message(),
|
||||
severity: Severity::Error,
|
||||
fix,
|
||||
})
|
||||
})
|
||||
})
|
||||
.on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
|
||||
let node = d.ast(db);
|
||||
let replacement = format!("Ok({})", node.syntax());
|
||||
let edit = TextEdit::replace(node.syntax().text_range(), replacement);
|
||||
let source_change = SourceFileEdit { file_id, edit }.into();
|
||||
let fix = Fix::new("Wrap with ok", source_change);
|
||||
res.borrow_mut().push(Diagnostic {
|
||||
range: sema.diagnostics_range(d).range,
|
||||
message: d.message(),
|
||||
severity: Severity::Error,
|
||||
fix: Some(fix),
|
||||
.on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
|
||||
let node = d.ast(db);
|
||||
let replacement = format!("Ok({})", node.syntax());
|
||||
let edit = TextEdit::replace(node.syntax().text_range(), replacement);
|
||||
let source_change = SourceFileEdit { file_id, edit }.into();
|
||||
let fix = Fix::new("Wrap with ok", source_change);
|
||||
res.borrow_mut().push(Diagnostic {
|
||||
range: sema.diagnostics_range(d).range,
|
||||
message: d.message(),
|
||||
severity: Severity::Error,
|
||||
fix: Some(fix),
|
||||
})
|
||||
})
|
||||
})
|
||||
.on::<hir::diagnostics::NoSuchField, _>(|d| {
|
||||
res.borrow_mut().push(Diagnostic {
|
||||
range: sema.diagnostics_range(d).range,
|
||||
message: d.message(),
|
||||
severity: Severity::Error,
|
||||
fix: missing_struct_field_fix(&sema, file_id, d),
|
||||
.on::<hir::diagnostics::NoSuchField, _>(|d| {
|
||||
res.borrow_mut().push(Diagnostic {
|
||||
range: sema.diagnostics_range(d).range,
|
||||
message: d.message(),
|
||||
severity: Severity::Error,
|
||||
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) {
|
||||
m.diagnostics(db, &mut sink);
|
||||
|
|
Loading…
Reference in a new issue