Skip to content

Commit e24e22f

Browse files
committed
Add fix for incorrect case diagnostic
1 parent 17f1026 commit e24e22f

File tree

8 files changed

+112
-30
lines changed

8 files changed

+112
-30
lines changed

crates/hir/src/code_model.rs

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -257,34 +257,22 @@ impl ModuleDef {
257257
}
258258

259259
pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
260-
match self {
260+
let id = match self {
261261
ModuleDef::Adt(it) => match it {
262-
Adt::Struct(it) => {
263-
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
264-
}
265-
Adt::Enum(it) => hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink),
266-
Adt::Union(it) => hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink),
262+
Adt::Struct(it) => it.id.into(),
263+
Adt::Enum(it) => it.id.into(),
264+
Adt::Union(it) => it.id.into(),
267265
},
268-
ModuleDef::Trait(it) => {
269-
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
270-
}
271-
ModuleDef::Function(it) => {
272-
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
273-
}
274-
ModuleDef::TypeAlias(it) => {
275-
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
276-
}
277-
ModuleDef::Module(it) => {
278-
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
279-
}
280-
ModuleDef::Const(it) => {
281-
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
282-
}
283-
ModuleDef::Static(it) => {
284-
hir_ty::diagnostics::validate_module_item(db, it.id.into(), sink)
285-
}
266+
ModuleDef::Trait(it) => it.id.into(),
267+
ModuleDef::Function(it) => it.id.into(),
268+
ModuleDef::TypeAlias(it) => it.id.into(),
269+
ModuleDef::Module(it) => it.id.into(),
270+
ModuleDef::Const(it) => it.id.into(),
271+
ModuleDef::Static(it) => it.id.into(),
286272
_ => return,
287-
}
273+
};
274+
275+
hir_ty::diagnostics::validate_module_item(db, id, sink)
288276
}
289277
}
290278

@@ -389,6 +377,8 @@ impl Module {
389377
let crate_def_map = db.crate_def_map(self.id.krate);
390378
crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink);
391379
for decl in self.declarations(db) {
380+
decl.diagnostics(db, sink);
381+
392382
match decl {
393383
crate::ModuleDef::Function(f) => f.diagnostics(db, sink),
394384
crate::ModuleDef::Module(m) => {

crates/hir/src/diagnostics.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
pub use hir_def::diagnostics::UnresolvedModule;
33
pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder};
44
pub use hir_ty::diagnostics::{
5-
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField,
5+
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr,
6+
NoSuchField,
67
};

crates/hir_ty/src/diagnostics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ impl Diagnostic for IncorrectCase {
298298
}
299299

300300
fn is_experimental(&self) -> bool {
301-
true
301+
false
302302
}
303303
}
304304

crates/ide/src/diagnostics.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ pub(crate) fn diagnostics(
9696
.on::<hir::diagnostics::NoSuchField, _>(|d| {
9797
res.borrow_mut().push(diagnostic_with_fix(d, &sema));
9898
})
99+
.on::<hir::diagnostics::IncorrectCase, _>(|d| {
100+
res.borrow_mut().push(warning_with_fix(d, &sema));
101+
})
99102
// Only collect experimental diagnostics when they're enabled.
100103
.filter(|diag| !(diag.is_experimental() && config.disable_experimental))
101104
.filter(|diag| !config.disabled.contains(diag.code().as_str()));
@@ -130,6 +133,16 @@ fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabas
130133
}
131134
}
132135

136+
fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
137+
Diagnostic {
138+
// name: Some(d.name().into()),
139+
range: sema.diagnostics_display_range(d).range,
140+
message: d.message(),
141+
severity: Severity::WeakWarning,
142+
fix: d.fix(&sema),
143+
}
144+
}
145+
133146
fn check_unnecessary_braces_in_use_statement(
134147
acc: &mut Vec<Diagnostic>,
135148
file_id: FileId,
@@ -253,6 +266,37 @@ mod tests {
253266
);
254267
}
255268

269+
/// Similar to `check_fix`, but applies all the available fixes.
270+
fn check_fixes(ra_fixture_before: &str, ra_fixture_after: &str) {
271+
let after = trim_indent(ra_fixture_after);
272+
273+
let (analysis, file_position) = fixture::position(ra_fixture_before);
274+
let diagnostic = analysis
275+
.diagnostics(&DiagnosticsConfig::default(), file_position.file_id)
276+
.unwrap()
277+
.pop()
278+
.unwrap();
279+
let fix = diagnostic.fix.unwrap();
280+
let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
281+
let actual = {
282+
let mut actual = target_file_contents.to_string();
283+
// Go from the last one to the first one, so that ranges won't be affected by previous edits.
284+
for edit in fix.source_change.source_file_edits.iter().rev() {
285+
edit.edit.apply(&mut actual);
286+
}
287+
actual
288+
};
289+
290+
assert_eq_text!(&after, &actual);
291+
assert!(
292+
fix.fix_trigger_range.start() <= file_position.offset
293+
&& fix.fix_trigger_range.end() >= file_position.offset,
294+
"diagnostic fix range {:?} does not touch cursor position {:?}",
295+
fix.fix_trigger_range,
296+
file_position.offset
297+
);
298+
}
299+
256300
/// Checks that a diagnostic applies to the file containing the `<|>` cursor marker
257301
/// which has a fix that can apply to other files.
258302
fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) {
@@ -790,4 +834,24 @@ struct Foo {
790834
let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap();
791835
assert!(!diagnostics.is_empty());
792836
}
837+
838+
#[test]
839+
fn test_rename_incorrect_case() {
840+
check_fixes(
841+
r#"
842+
pub struct test_struct<|> { one: i32 }
843+
844+
pub fn some_fn(val: test_struct) -> test_struct {
845+
test_struct { one: val.one + 1 }
846+
}
847+
"#,
848+
r#"
849+
pub struct TestStruct { one: i32 }
850+
851+
pub fn some_fn(val: TestStruct) -> TestStruct {
852+
TestStruct { one: val.one + 1 }
853+
}
854+
"#,
855+
);
856+
}
793857
}

crates/ide/src/diagnostics/fixes.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
use base_db::FileId;
44
use hir::{
55
db::AstDatabase,
6-
diagnostics::{Diagnostic, MissingFields, MissingOkInTailExpr, NoSuchField, UnresolvedModule},
6+
diagnostics::{
7+
Diagnostic, IncorrectCase, MissingFields, MissingOkInTailExpr, NoSuchField,
8+
UnresolvedModule,
9+
},
710
HasSource, HirDisplay, Semantics, VariantDef,
811
};
912
use ide_db::{
@@ -17,7 +20,7 @@ use syntax::{
1720
};
1821
use text_edit::TextEdit;
1922

20-
use crate::diagnostics::Fix;
23+
use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition};
2124

2225
/// A [Diagnostic] that potentially has a fix available.
2326
///
@@ -99,6 +102,19 @@ impl DiagnosticWithFix for MissingOkInTailExpr {
99102
}
100103
}
101104

105+
impl DiagnosticWithFix for IncorrectCase {
106+
fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
107+
let file_id = self.file.original_file(sema.db);
108+
let offset = self.ident.text_range().start();
109+
let file_position = FilePosition { file_id, offset };
110+
111+
let rename_changes = rename_with_semantics(sema, file_position, &self.suggested_text)?;
112+
113+
let label = format!("Rename to {}", self.suggested_text);
114+
Some(Fix::new(&label, rename_changes.info, rename_changes.range))
115+
}
116+
}
117+
102118
fn missing_record_expr_field_fix(
103119
sema: &Semantics<RootDatabase>,
104120
usage_file_id: FileId,

crates/ide/src/references.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
//! at the index that the match starts at and its tree parent is
1010
//! resolved to the search element definition, we get a reference.
1111
12-
mod rename;
12+
pub(crate) mod rename;
1313

1414
use hir::Semantics;
1515
use ide_db::{

crates/ide/src/references/rename.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,14 @@ pub(crate) fn rename(
4242
new_name: &str,
4343
) -> Result<RangeInfo<SourceChange>, RenameError> {
4444
let sema = Semantics::new(db);
45+
rename_with_semantics(&sema, position, new_name)
46+
}
4547

48+
pub(crate) fn rename_with_semantics(
49+
sema: &Semantics<RootDatabase>,
50+
position: FilePosition,
51+
new_name: &str,
52+
) -> Result<RangeInfo<SourceChange>, RenameError> {
4653
match lex_single_syntax_kind(new_name) {
4754
Some(res) => match res {
4855
(SyntaxKind::IDENT, _) => (),

crates/syntax/src/ptr.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ impl SyntaxNodePtr {
2323
SyntaxNodePtr { range: node.text_range(), kind: node.kind() }
2424
}
2525

26+
pub fn text_range(&self) -> TextRange {
27+
self.range.clone()
28+
}
29+
2630
pub fn to_node(&self, root: &SyntaxNode) -> SyntaxNode {
2731
assert!(root.parent().is_none());
2832
successors(Some(root.clone()), |node| {

0 commit comments

Comments
 (0)