Add fix for incorrect case diagnostic
This commit is contained in:
parent
17f1026c46
commit
e24e22f288
@ -257,34 +257,22 @@ impl ModuleDef {
|
||||
}
|
||||
|
||||
pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
|
||||
match self {
|
||||
let id = match self {
|
||||
ModuleDef::Adt(it) => match it {
|
||||
Adt::Struct(it) => {
|
||||
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
|
||||
}
|
||||
Adt::Enum(it) => hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink),
|
||||
Adt::Union(it) => hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink),
|
||||
Adt::Struct(it) => it.id.into(),
|
||||
Adt::Enum(it) => it.id.into(),
|
||||
Adt::Union(it) => it.id.into(),
|
||||
},
|
||||
ModuleDef::Trait(it) => {
|
||||
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
|
||||
}
|
||||
ModuleDef::Function(it) => {
|
||||
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
|
||||
}
|
||||
ModuleDef::TypeAlias(it) => {
|
||||
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
|
||||
}
|
||||
ModuleDef::Module(it) => {
|
||||
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
|
||||
}
|
||||
ModuleDef::Const(it) => {
|
||||
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
|
||||
}
|
||||
ModuleDef::Static(it) => {
|
||||
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
|
||||
}
|
||||
ModuleDef::Trait(it) => it.id.into(),
|
||||
ModuleDef::Function(it) => it.id.into(),
|
||||
ModuleDef::TypeAlias(it) => it.id.into(),
|
||||
ModuleDef::Module(it) => it.id.into(),
|
||||
ModuleDef::Const(it) => it.id.into(),
|
||||
ModuleDef::Static(it) => it.id.into(),
|
||||
_ => return,
|
||||
}
|
||||
};
|
||||
|
||||
hir_ty::diagnostics::validate_module_item(db, id, sink)
|
||||
}
|
||||
}
|
||||
|
||||
@ -389,6 +377,8 @@ impl Module {
|
||||
let crate_def_map = db.crate_def_map(self.id.krate);
|
||||
crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink);
|
||||
for decl in self.declarations(db) {
|
||||
decl.diagnostics(db, sink);
|
||||
|
||||
match decl {
|
||||
crate::ModuleDef::Function(f) => f.diagnostics(db, sink),
|
||||
crate::ModuleDef::Module(m) => {
|
||||
|
@ -2,5 +2,6 @@
|
||||
pub use hir_def::diagnostics::UnresolvedModule;
|
||||
pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder};
|
||||
pub use hir_ty::diagnostics::{
|
||||
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField,
|
||||
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr,
|
||||
NoSuchField,
|
||||
};
|
||||
|
@ -298,7 +298,7 @@ impl Diagnostic for IncorrectCase {
|
||||
}
|
||||
|
||||
fn is_experimental(&self) -> bool {
|
||||
true
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,6 +96,9 @@ pub(crate) fn diagnostics(
|
||||
.on::<hir::diagnostics::NoSuchField, _>(|d| {
|
||||
res.borrow_mut().push(diagnostic_with_fix(d, &sema));
|
||||
})
|
||||
.on::<hir::diagnostics::IncorrectCase, _>(|d| {
|
||||
res.borrow_mut().push(warning_with_fix(d, &sema));
|
||||
})
|
||||
// Only collect experimental diagnostics when they're enabled.
|
||||
.filter(|diag| !(diag.is_experimental() && config.disable_experimental))
|
||||
.filter(|diag| !config.disabled.contains(diag.code().as_str()));
|
||||
@ -130,6 +133,16 @@ fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabas
|
||||
}
|
||||
}
|
||||
|
||||
fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
|
||||
Diagnostic {
|
||||
// name: Some(d.name().into()),
|
||||
range: sema.diagnostics_display_range(d).range,
|
||||
message: d.message(),
|
||||
severity: Severity::WeakWarning,
|
||||
fix: d.fix(&sema),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_unnecessary_braces_in_use_statement(
|
||||
acc: &mut Vec<Diagnostic>,
|
||||
file_id: FileId,
|
||||
@ -253,6 +266,37 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// Similar to `check_fix`, but applies all the available fixes.
|
||||
fn check_fixes(ra_fixture_before: &str, ra_fixture_after: &str) {
|
||||
let after = trim_indent(ra_fixture_after);
|
||||
|
||||
let (analysis, file_position) = fixture::position(ra_fixture_before);
|
||||
let diagnostic = analysis
|
||||
.diagnostics(&DiagnosticsConfig::default(), file_position.file_id)
|
||||
.unwrap()
|
||||
.pop()
|
||||
.unwrap();
|
||||
let fix = diagnostic.fix.unwrap();
|
||||
let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
|
||||
let actual = {
|
||||
let mut actual = target_file_contents.to_string();
|
||||
// Go from the last one to the first one, so that ranges won't be affected by previous edits.
|
||||
for edit in fix.source_change.source_file_edits.iter().rev() {
|
||||
edit.edit.apply(&mut actual);
|
||||
}
|
||||
actual
|
||||
};
|
||||
|
||||
assert_eq_text!(&after, &actual);
|
||||
assert!(
|
||||
fix.fix_trigger_range.start() <= file_position.offset
|
||||
&& fix.fix_trigger_range.end() >= file_position.offset,
|
||||
"diagnostic fix range {:?} does not touch cursor position {:?}",
|
||||
fix.fix_trigger_range,
|
||||
file_position.offset
|
||||
);
|
||||
}
|
||||
|
||||
/// Checks that a diagnostic applies to the file containing the `<|>` cursor marker
|
||||
/// which has a fix that can apply to other files.
|
||||
fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) {
|
||||
@ -790,4 +834,24 @@ struct Foo {
|
||||
let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap();
|
||||
assert!(!diagnostics.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_incorrect_case() {
|
||||
check_fixes(
|
||||
r#"
|
||||
pub struct test_struct<|> { one: i32 }
|
||||
|
||||
pub fn some_fn(val: test_struct) -> test_struct {
|
||||
test_struct { one: val.one + 1 }
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
pub struct TestStruct { one: i32 }
|
||||
|
||||
pub fn some_fn(val: TestStruct) -> TestStruct {
|
||||
TestStruct { one: val.one + 1 }
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,10 @@
|
||||
use base_db::FileId;
|
||||
use hir::{
|
||||
db::AstDatabase,
|
||||
diagnostics::{Diagnostic, MissingFields, MissingOkInTailExpr, NoSuchField, UnresolvedModule},
|
||||
diagnostics::{
|
||||
Diagnostic, IncorrectCase, MissingFields, MissingOkInTailExpr, NoSuchField,
|
||||
UnresolvedModule,
|
||||
},
|
||||
HasSource, HirDisplay, Semantics, VariantDef,
|
||||
};
|
||||
use ide_db::{
|
||||
@ -17,7 +20,7 @@ use syntax::{
|
||||
};
|
||||
use text_edit::TextEdit;
|
||||
|
||||
use crate::diagnostics::Fix;
|
||||
use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition};
|
||||
|
||||
/// A [Diagnostic] that potentially has a fix available.
|
||||
///
|
||||
@ -99,6 +102,19 @@ impl DiagnosticWithFix for MissingOkInTailExpr {
|
||||
}
|
||||
}
|
||||
|
||||
impl DiagnosticWithFix for IncorrectCase {
|
||||
fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
|
||||
let file_id = self.file.original_file(sema.db);
|
||||
let offset = self.ident.text_range().start();
|
||||
let file_position = FilePosition { file_id, offset };
|
||||
|
||||
let rename_changes = rename_with_semantics(sema, file_position, &self.suggested_text)?;
|
||||
|
||||
let label = format!("Rename to {}", self.suggested_text);
|
||||
Some(Fix::new(&label, rename_changes.info, rename_changes.range))
|
||||
}
|
||||
}
|
||||
|
||||
fn missing_record_expr_field_fix(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
usage_file_id: FileId,
|
||||
|
@ -9,7 +9,7 @@
|
||||
//! at the index that the match starts at and its tree parent is
|
||||
//! resolved to the search element definition, we get a reference.
|
||||
|
||||
mod rename;
|
||||
pub(crate) mod rename;
|
||||
|
||||
use hir::Semantics;
|
||||
use ide_db::{
|
||||
|
@ -42,7 +42,14 @@ pub(crate) fn rename(
|
||||
new_name: &str,
|
||||
) -> Result<RangeInfo<SourceChange>, RenameError> {
|
||||
let sema = Semantics::new(db);
|
||||
rename_with_semantics(&sema, position, new_name)
|
||||
}
|
||||
|
||||
pub(crate) fn rename_with_semantics(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
position: FilePosition,
|
||||
new_name: &str,
|
||||
) -> Result<RangeInfo<SourceChange>, RenameError> {
|
||||
match lex_single_syntax_kind(new_name) {
|
||||
Some(res) => match res {
|
||||
(SyntaxKind::IDENT, _) => (),
|
||||
|
@ -23,6 +23,10 @@ impl SyntaxNodePtr {
|
||||
SyntaxNodePtr { range: node.text_range(), kind: node.kind() }
|
||||
}
|
||||
|
||||
pub fn text_range(&self) -> TextRange {
|
||||
self.range.clone()
|
||||
}
|
||||
|
||||
pub fn to_node(&self, root: &SyntaxNode) -> SyntaxNode {
|
||||
assert!(root.parent().is_none());
|
||||
successors(Some(root.clone()), |node| {
|
||||
|
Loading…
x
Reference in New Issue
Block a user