mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 07:04:49 +00:00
internal: refactor incorrect case diagnostics
This commit is contained in:
parent
2ad7892462
commit
fc30c5ccbe
6 changed files with 46 additions and 76 deletions
|
@ -34,6 +34,7 @@ macro_rules! diagnostics {
|
||||||
diagnostics![
|
diagnostics![
|
||||||
BreakOutsideOfLoop,
|
BreakOutsideOfLoop,
|
||||||
InactiveCode,
|
InactiveCode,
|
||||||
|
IncorrectCase,
|
||||||
MacroError,
|
MacroError,
|
||||||
MismatchedArgCount,
|
MismatchedArgCount,
|
||||||
MissingFields,
|
MissingFields,
|
||||||
|
@ -195,31 +196,3 @@ impl Diagnostic for InternalBailedOut {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use hir_ty::diagnostics::IncorrectCase;
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -86,8 +86,8 @@ use crate::{
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
attrs::{HasAttrs, Namespace},
|
attrs::{HasAttrs, Namespace},
|
||||||
diagnostics::{
|
diagnostics::{
|
||||||
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, InternalBailedOut, MacroError,
|
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InternalBailedOut,
|
||||||
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
|
MacroError, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
|
||||||
MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
|
MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
|
||||||
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
|
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
|
||||||
UnresolvedModule, UnresolvedProcMacro,
|
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 {
|
let id = match self {
|
||||||
ModuleDef::Adt(it) => match it {
|
ModuleDef::Adt(it) => match it {
|
||||||
Adt::Struct(it) => it.id.into(),
|
Adt::Struct(it) => it.id.into(),
|
||||||
|
@ -353,17 +353,19 @@ impl ModuleDef {
|
||||||
ModuleDef::Module(it) => it.id.into(),
|
ModuleDef::Module(it) => it.id.into(),
|
||||||
ModuleDef::Const(it) => it.id.into(),
|
ModuleDef::Const(it) => it.id.into(),
|
||||||
ModuleDef::Static(it) => it.id.into(),
|
ModuleDef::Static(it) => it.id.into(),
|
||||||
_ => return,
|
_ => return Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let module = match self.module(db) {
|
let module = match self.module(db) {
|
||||||
Some(it) => it,
|
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) {
|
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))
|
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()) {
|
for diag in hir_ty::diagnostics::validate_module_item(db, krate, self.id.into()) {
|
||||||
sink.push(diag)
|
acc.push(diag.into())
|
||||||
}
|
}
|
||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)]
|
#[derive(Debug)]
|
||||||
pub struct IncorrectCase {
|
pub struct IncorrectCase {
|
||||||
pub file: HirFileId,
|
pub file: HirFileId,
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
mod break_outside_of_loop;
|
mod break_outside_of_loop;
|
||||||
mod inactive_code;
|
mod inactive_code;
|
||||||
|
mod incorrect_case;
|
||||||
mod macro_error;
|
mod macro_error;
|
||||||
mod mismatched_arg_count;
|
mod mismatched_arg_count;
|
||||||
mod missing_fields;
|
mod missing_fields;
|
||||||
|
@ -135,7 +136,6 @@ pub struct DiagnosticsConfig {
|
||||||
struct DiagnosticsContext<'a> {
|
struct DiagnosticsContext<'a> {
|
||||||
config: &'a DiagnosticsConfig,
|
config: &'a DiagnosticsConfig,
|
||||||
sema: Semantics<'a, RootDatabase>,
|
sema: Semantics<'a, RootDatabase>,
|
||||||
#[allow(unused)]
|
|
||||||
resolve: &'a AssistResolveStrategy,
|
resolve: &'a AssistResolveStrategy,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,9 +165,6 @@ pub(crate) fn diagnostics(
|
||||||
}
|
}
|
||||||
let res = RefCell::new(res);
|
let res = RefCell::new(res);
|
||||||
let sink_builder = DiagnosticSinkBuilder::new()
|
let sink_builder = DiagnosticSinkBuilder::new()
|
||||||
.on::<hir::diagnostics::IncorrectCase, _>(|d| {
|
|
||||||
res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
|
|
||||||
})
|
|
||||||
.on::<UnlinkedFile, _>(|d| {
|
.on::<UnlinkedFile, _>(|d| {
|
||||||
// Limit diagnostic to the first few characters in the file. This matches how VS Code
|
// 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.
|
// renders it with the full span, but on other editors, and is less invasive.
|
||||||
|
@ -216,6 +213,7 @@ pub(crate) fn diagnostics(
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let d = match diag {
|
let d = match diag {
|
||||||
AnyDiagnostic::BreakOutsideOfLoop(d) => break_outside_of_loop::break_outside_of_loop(&ctx, &d),
|
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::MacroError(d) => macro_error::macro_error(&ctx, &d),
|
||||||
AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d),
|
AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d),
|
||||||
AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
|
AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
|
||||||
|
@ -250,16 +248,6 @@ pub(crate) fn diagnostics(
|
||||||
res
|
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(
|
fn check_unnecessary_braces_in_use_statement(
|
||||||
acc: &mut Vec<Diagnostic>,
|
acc: &mut Vec<Diagnostic>,
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Provides a way to attach fixes to the diagnostics.
|
//! Provides a way to attach fixes to the diagnostics.
|
||||||
//! The same module also has all curret custom fixes for the diagnostics implemented.
|
//! The same module also has all curret custom fixes for the diagnostics implemented.
|
||||||
mod change_case;
|
|
||||||
|
|
||||||
use hir::{diagnostics::Diagnostic, Semantics};
|
use hir::{diagnostics::Diagnostic, Semantics};
|
||||||
use ide_assists::AssistResolveStrategy;
|
use ide_assists::AssistResolveStrategy;
|
||||||
|
|
|
@ -1,35 +1,46 @@
|
||||||
use hir::{db::AstDatabase, diagnostics::IncorrectCase, InFile, Semantics};
|
use hir::{db::AstDatabase, InFile};
|
||||||
use ide_assists::{Assist, AssistResolveStrategy};
|
use ide_assists::Assist;
|
||||||
use ide_db::{base_db::FilePosition, RootDatabase};
|
use ide_db::base_db::FilePosition;
|
||||||
use syntax::AstNode;
|
use syntax::AstNode;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diagnostics::{unresolved_fix, DiagnosticWithFixes},
|
diagnostics::{unresolved_fix, Diagnostic, DiagnosticsContext},
|
||||||
references::rename::rename_with_semantics,
|
references::rename::rename_with_semantics,
|
||||||
|
Severity,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl DiagnosticWithFixes for IncorrectCase {
|
// Diagnostic: incorrect-ident-case
|
||||||
fn fixes(
|
//
|
||||||
&self,
|
// 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].
|
||||||
sema: &Semantics<RootDatabase>,
|
pub(super) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic {
|
||||||
resolve: &AssistResolveStrategy,
|
Diagnostic::new(
|
||||||
) -> Option<Vec<Assist>> {
|
"incorrect-ident-case",
|
||||||
let root = sema.db.parse_or_expand(self.file)?;
|
format!(
|
||||||
let name_node = self.ident.to_node(&root);
|
"{} `{}` 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());
|
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> {
|
||||||
let frange = name_node.original_file_range(sema.db);
|
let root = ctx.sema.db.parse_or_expand(d.file)?;
|
||||||
let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
|
let name_node = d.ident.to_node(&root);
|
||||||
|
|
||||||
let label = format!("Rename to {}", self.suggested_text);
|
let name_node = InFile::new(d.file, name_node.syntax());
|
||||||
let mut res = unresolved_fix("change_case", &label, frange.range);
|
let frange = name_node.original_file_range(ctx.sema.db);
|
||||||
if resolve.should_resolve(&res.id) {
|
let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
|
||||||
let source_change = rename_with_semantics(sema, file_position, &self.suggested_text);
|
|
||||||
res.source_change = Some(source_change.ok().unwrap_or_default());
|
|
||||||
}
|
|
||||||
|
|
||||||
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)]
|
#[cfg(test)]
|
Loading…
Add table
Add a link
Reference in a new issue