internal: refactor incorrect case diagnostics

This commit is contained in:
Aleksey Kladov 2021-06-13 21:09:03 +03:00
parent 2ad7892462
commit fc30c5ccbe
6 changed files with 46 additions and 76 deletions

View File

@ -34,6 +34,7 @@ macro_rules! diagnostics {
diagnostics![
BreakOutsideOfLoop,
InactiveCode,
IncorrectCase,
MacroError,
MismatchedArgCount,
MissingFields,
@ -195,31 +196,3 @@ impl Diagnostic for InternalBailedOut {
}
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<SyntaxNodePtr> {
InFile::new(self.file, self.ident.clone().into())
}
fn as_any(&self) -> &(dyn Any + Send + 'static) {
self
}
fn is_experimental(&self) -> bool {
true
}
}

View File

@ -86,8 +86,8 @@ use crate::{
pub use crate::{
attrs::{HasAttrs, Namespace},
diagnostics::{
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, InternalBailedOut, MacroError,
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InternalBailedOut,
MacroError, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
UnresolvedModule, UnresolvedProcMacro,
@ -340,7 +340,7 @@ impl ModuleDef {
}
}
pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
pub fn diagnostics(self, db: &dyn HirDatabase) -> Vec<AnyDiagnostic> {
let id = match self {
ModuleDef::Adt(it) => match it {
Adt::Struct(it) => it.id.into(),
@ -353,17 +353,19 @@ impl ModuleDef {
ModuleDef::Module(it) => it.id.into(),
ModuleDef::Const(it) => it.id.into(),
ModuleDef::Static(it) => it.id.into(),
_ => return,
_ => return Vec::new(),
};
let module = match self.module(db) {
Some(it) => it,
None => return,
None => return Vec::new(),
};
let mut acc = Vec::new();
for diag in hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id) {
sink.push(diag)
acc.push(diag.into())
}
acc
}
}
@ -624,7 +626,7 @@ impl Module {
acc.extend(m.diagnostics(db, sink, internal_diagnostics))
}
}
_ => decl.diagnostics(db, sink),
_ => acc.extend(decl.diagnostics(db)),
}
}
@ -1234,7 +1236,7 @@ impl Function {
}
for diag in hir_ty::diagnostics::validate_module_item(db, krate, self.id.into()) {
sink.push(diag)
acc.push(diag.into())
}
acc
}

View File

@ -84,9 +84,6 @@ impl fmt::Display for IdentType {
}
}
// Diagnostic: incorrect-ident-case
//
// This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention].
#[derive(Debug)]
pub struct IncorrectCase {
pub file: HirFileId,

View File

@ -6,6 +6,7 @@
mod break_outside_of_loop;
mod inactive_code;
mod incorrect_case;
mod macro_error;
mod mismatched_arg_count;
mod missing_fields;
@ -135,7 +136,6 @@ pub struct DiagnosticsConfig {
struct DiagnosticsContext<'a> {
config: &'a DiagnosticsConfig,
sema: Semantics<'a, RootDatabase>,
#[allow(unused)]
resolve: &'a AssistResolveStrategy,
}
@ -165,9 +165,6 @@ pub(crate) fn diagnostics(
}
let res = RefCell::new(res);
let sink_builder = DiagnosticSinkBuilder::new()
.on::<hir::diagnostics::IncorrectCase, _>(|d| {
res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
})
.on::<UnlinkedFile, _>(|d| {
// Limit diagnostic to the first few characters in the file. This matches how VS Code
// renders it with the full span, but on other editors, and is less invasive.
@ -216,6 +213,7 @@ pub(crate) fn diagnostics(
#[rustfmt::skip]
let d = match diag {
AnyDiagnostic::BreakOutsideOfLoop(d) => break_outside_of_loop::break_outside_of_loop(&ctx, &d),
AnyDiagnostic::IncorrectCase(d) => incorrect_case::incorrect_case(&ctx, &d),
AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d),
AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d),
AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
@ -250,16 +248,6 @@ pub(crate) fn diagnostics(
res
}
fn warning_with_fix<D: DiagnosticWithFixes>(
d: &D,
sema: &Semantics<RootDatabase>,
resolve: &AssistResolveStrategy,
) -> Diagnostic {
Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message())
.with_fixes(d.fixes(sema, resolve))
.with_code(Some(d.code()))
}
fn check_unnecessary_braces_in_use_statement(
acc: &mut Vec<Diagnostic>,
file_id: FileId,

View File

@ -1,6 +1,5 @@
//! Provides a way to attach fixes to the diagnostics.
//! The same module also has all curret custom fixes for the diagnostics implemented.
mod change_case;
use hir::{diagnostics::Diagnostic, Semantics};
use ide_assists::AssistResolveStrategy;

View File

@ -1,35 +1,46 @@
use hir::{db::AstDatabase, diagnostics::IncorrectCase, InFile, Semantics};
use ide_assists::{Assist, AssistResolveStrategy};
use ide_db::{base_db::FilePosition, RootDatabase};
use hir::{db::AstDatabase, InFile};
use ide_assists::Assist;
use ide_db::base_db::FilePosition;
use syntax::AstNode;
use crate::{
diagnostics::{unresolved_fix, DiagnosticWithFixes},
diagnostics::{unresolved_fix, Diagnostic, DiagnosticsContext},
references::rename::rename_with_semantics,
Severity,
};
impl DiagnosticWithFixes for IncorrectCase {
fn fixes(
&self,
sema: &Semantics<RootDatabase>,
resolve: &AssistResolveStrategy,
) -> Option<Vec<Assist>> {
let root = sema.db.parse_or_expand(self.file)?;
let name_node = self.ident.to_node(&root);
// Diagnostic: incorrect-ident-case
//
// This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention].
pub(super) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic {
Diagnostic::new(
"incorrect-ident-case",
format!(
"{} `{}` should have {} name, e.g. `{}`",
d.ident_type, d.ident_text, d.expected_case, d.suggested_text
),
ctx.sema.diagnostics_display_range(InFile::new(d.file, d.ident.clone().into())).range,
)
.severity(Severity::WeakWarning)
.with_fixes(fixes(ctx, d))
}
let name_node = InFile::new(self.file, name_node.syntax());
let frange = name_node.original_file_range(sema.db);
let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> {
let root = ctx.sema.db.parse_or_expand(d.file)?;
let name_node = d.ident.to_node(&root);
let label = format!("Rename to {}", self.suggested_text);
let mut res = unresolved_fix("change_case", &label, frange.range);
if resolve.should_resolve(&res.id) {
let source_change = rename_with_semantics(sema, file_position, &self.suggested_text);
res.source_change = Some(source_change.ok().unwrap_or_default());
}
let name_node = InFile::new(d.file, name_node.syntax());
let frange = name_node.original_file_range(ctx.sema.db);
let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
Some(vec![res])
let label = format!("Rename to {}", d.suggested_text);
let mut res = unresolved_fix("change_case", &label, frange.range);
if ctx.resolve.should_resolve(&res.id) {
let source_change = rename_with_semantics(&ctx.sema, file_position, &d.suggested_text);
res.source_change = Some(source_change.ok().unwrap_or_default());
}
Some(vec![res])
}
#[cfg(test)]