mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 07:04:49 +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
|
//! 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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,79 +48,82 @@ 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 {
|
.on::<hir::diagnostics::UnresolvedModule, _>(|d| {
|
||||||
message: d.message(),
|
let original_file = d.source().file_id.original_file(db);
|
||||||
range: sema.diagnostics_range(d).range,
|
let fix = Fix::new(
|
||||||
severity: Severity::Error,
|
"Create module",
|
||||||
fix: None,
|
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| {
|
||||||
.on::<hir::diagnostics::UnresolvedModule, _>(|d| {
|
// Note that although we could add a diagnostics to
|
||||||
let original_file = d.source().file_id.original_file(db);
|
// fill the missing tuple field, e.g :
|
||||||
let fix = Fix::new(
|
// `struct A(usize);`
|
||||||
"Create module",
|
// `let a = A { 0: () }`
|
||||||
FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }.into(),
|
// 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()) {
|
||||||
res.borrow_mut().push(Diagnostic {
|
None
|
||||||
range: sema.diagnostics_range(d).range,
|
} else {
|
||||||
message: d.message(),
|
let mut field_list = d.ast(db);
|
||||||
severity: Severity::Error,
|
for f in d.missed_fields.iter() {
|
||||||
fix: Some(fix),
|
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 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())
|
||||||
builder.finish()
|
.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 {
|
res.borrow_mut().push(Diagnostic {
|
||||||
range: sema.diagnostics_range(d).range,
|
range: sema.diagnostics_range(d).range,
|
||||||
message: d.message(),
|
message: d.message(),
|
||||||
severity: Severity::Error,
|
severity: Severity::Error,
|
||||||
fix,
|
fix,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
.on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
|
||||||
.on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
|
let node = d.ast(db);
|
||||||
let node = d.ast(db);
|
let replacement = format!("Ok({})", node.syntax());
|
||||||
let replacement = format!("Ok({})", node.syntax());
|
let edit = TextEdit::replace(node.syntax().text_range(), replacement);
|
||||||
let edit = TextEdit::replace(node.syntax().text_range(), replacement);
|
let source_change = SourceFileEdit { file_id, edit }.into();
|
||||||
let source_change = SourceFileEdit { file_id, edit }.into();
|
let fix = Fix::new("Wrap with ok", source_change);
|
||||||
let fix = Fix::new("Wrap with ok", source_change);
|
res.borrow_mut().push(Diagnostic {
|
||||||
res.borrow_mut().push(Diagnostic {
|
range: sema.diagnostics_range(d).range,
|
||||||
range: sema.diagnostics_range(d).range,
|
message: d.message(),
|
||||||
message: d.message(),
|
severity: Severity::Error,
|
||||||
severity: Severity::Error,
|
fix: Some(fix),
|
||||||
fix: Some(fix),
|
})
|
||||||
})
|
})
|
||||||
})
|
.on::<hir::diagnostics::NoSuchField, _>(|d| {
|
||||||
.on::<hir::diagnostics::NoSuchField, _>(|d| {
|
res.borrow_mut().push(Diagnostic {
|
||||||
res.borrow_mut().push(Diagnostic {
|
range: sema.diagnostics_range(d).range,
|
||||||
range: sema.diagnostics_range(d).range,
|
message: d.message(),
|
||||||
message: d.message(),
|
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) {
|
||||||
m.diagnostics(db, &mut sink);
|
m.diagnostics(db, &mut sink);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue