Move field_shorthand to a separate module
This commit is contained in:
parent
1022672af0
commit
3086fb2ef4
@ -5,6 +5,7 @@
|
||||
//! original files. So we need to map the ranges.
|
||||
|
||||
mod fixes;
|
||||
mod field_shorthand;
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
@ -15,7 +16,7 @@
|
||||
use rustc_hash::FxHashSet;
|
||||
use syntax::{
|
||||
ast::{self, AstNode},
|
||||
match_ast, SyntaxNode, TextRange, T,
|
||||
SyntaxNode, TextRange, T,
|
||||
};
|
||||
use text_edit::TextEdit;
|
||||
|
||||
@ -80,7 +81,7 @@ pub(crate) fn diagnostics(
|
||||
|
||||
for node in parse.tree().syntax().descendants() {
|
||||
check_unnecessary_braces_in_use_statement(&mut res, file_id, &node);
|
||||
check_field_shorthand(&mut res, file_id, &node);
|
||||
field_shorthand::check(&mut res, file_id, &node);
|
||||
}
|
||||
let res = RefCell::new(res);
|
||||
let sink_builder = DiagnosticSinkBuilder::new()
|
||||
@ -188,100 +189,6 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
|
||||
None
|
||||
}
|
||||
|
||||
fn check_field_shorthand(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) {
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::RecordExpr(it) => check_expr_field_shorthand(acc, file_id, it),
|
||||
ast::RecordPat(it) => check_pat_field_shorthand(acc, file_id, it),
|
||||
_ => ()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn check_expr_field_shorthand(
|
||||
acc: &mut Vec<Diagnostic>,
|
||||
file_id: FileId,
|
||||
record_expr: ast::RecordExpr,
|
||||
) {
|
||||
let record_field_list = match record_expr.record_expr_field_list() {
|
||||
Some(it) => it,
|
||||
None => return,
|
||||
};
|
||||
for record_field in record_field_list.fields() {
|
||||
let (name_ref, expr) = match record_field.name_ref().zip(record_field.expr()) {
|
||||
Some(it) => it,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let field_name = name_ref.syntax().text().to_string();
|
||||
let field_expr = expr.syntax().text().to_string();
|
||||
let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
|
||||
if field_name != field_expr || field_name_is_tup_index {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut edit_builder = TextEdit::builder();
|
||||
edit_builder.delete(record_field.syntax().text_range());
|
||||
edit_builder.insert(record_field.syntax().text_range().start(), field_name);
|
||||
let edit = edit_builder.finish();
|
||||
|
||||
let field_range = record_field.syntax().text_range();
|
||||
acc.push(Diagnostic {
|
||||
// name: None,
|
||||
range: field_range,
|
||||
message: "Shorthand struct initialization".to_string(),
|
||||
severity: Severity::WeakWarning,
|
||||
fix: Some(Fix::new(
|
||||
"Use struct shorthand initialization",
|
||||
SourceFileEdit { file_id, edit }.into(),
|
||||
field_range,
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn check_pat_field_shorthand(
|
||||
acc: &mut Vec<Diagnostic>,
|
||||
file_id: FileId,
|
||||
record_pat: ast::RecordPat,
|
||||
) {
|
||||
let record_pat_field_list = match record_pat.record_pat_field_list() {
|
||||
Some(it) => it,
|
||||
None => return,
|
||||
};
|
||||
for record_pat_field in record_pat_field_list.fields() {
|
||||
let (name_ref, pat) = match record_pat_field.name_ref().zip(record_pat_field.pat()) {
|
||||
Some(it) => it,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let field_name = name_ref.syntax().text().to_string();
|
||||
let field_pat = pat.syntax().text().to_string();
|
||||
let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
|
||||
if field_name != field_pat || field_name_is_tup_index {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut edit_builder = TextEdit::builder();
|
||||
edit_builder.delete(record_pat_field.syntax().text_range());
|
||||
edit_builder.insert(record_pat_field.syntax().text_range().start(), field_name);
|
||||
let edit = edit_builder.finish();
|
||||
|
||||
let field_range = record_pat_field.syntax().text_range();
|
||||
acc.push(Diagnostic {
|
||||
// name: None,
|
||||
range: field_range,
|
||||
message: "Shorthand struct pattern".to_string(),
|
||||
severity: Severity::WeakWarning,
|
||||
fix: Some(Fix::new(
|
||||
"Use struct field shorthand",
|
||||
SourceFileEdit { file_id, edit }.into(),
|
||||
field_range,
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use expect_test::{expect, Expect};
|
||||
@ -295,7 +202,7 @@ mod tests {
|
||||
/// * a diagnostic is produced
|
||||
/// * this diagnostic fix trigger range touches the input cursor position
|
||||
/// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
|
||||
fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
|
||||
pub(super) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
|
||||
let after = trim_indent(ra_fixture_after);
|
||||
|
||||
let (analysis, file_position) = fixture::position(ra_fixture_before);
|
||||
@ -377,7 +284,7 @@ fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_
|
||||
|
||||
/// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
|
||||
/// apply to the file containing the cursor.
|
||||
fn check_no_diagnostics(ra_fixture: &str) {
|
||||
pub(crate) fn check_no_diagnostics(ra_fixture: &str) {
|
||||
let (analysis, files) = fixture::files(ra_fixture);
|
||||
let diagnostics = files
|
||||
.into_iter()
|
||||
@ -777,104 +684,6 @@ mod a { mod c {} mod d { mod e {} } }
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_expr_field_shorthand() {
|
||||
check_no_diagnostics(
|
||||
r#"
|
||||
struct A { a: &'static str }
|
||||
fn main() { A { a: "hello" } }
|
||||
"#,
|
||||
);
|
||||
check_no_diagnostics(
|
||||
r#"
|
||||
struct A(usize);
|
||||
fn main() { A { 0: 0 } }
|
||||
"#,
|
||||
);
|
||||
|
||||
check_fix(
|
||||
r#"
|
||||
struct A { a: &'static str }
|
||||
fn main() {
|
||||
let a = "haha";
|
||||
A { a<|>: a }
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct A { a: &'static str }
|
||||
fn main() {
|
||||
let a = "haha";
|
||||
A { a }
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_fix(
|
||||
r#"
|
||||
struct A { a: &'static str, b: &'static str }
|
||||
fn main() {
|
||||
let a = "haha";
|
||||
let b = "bb";
|
||||
A { a<|>: a, b }
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct A { a: &'static str, b: &'static str }
|
||||
fn main() {
|
||||
let a = "haha";
|
||||
let b = "bb";
|
||||
A { a, b }
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_pat_field_shorthand() {
|
||||
check_no_diagnostics(
|
||||
r#"
|
||||
struct A { a: &'static str }
|
||||
fn f(a: A) { let A { a: hello } = a; }
|
||||
"#,
|
||||
);
|
||||
check_no_diagnostics(
|
||||
r#"
|
||||
struct A(usize);
|
||||
fn f(a: A) { let A { 0: 0 } = a; }
|
||||
"#,
|
||||
);
|
||||
|
||||
check_fix(
|
||||
r#"
|
||||
struct A { a: &'static str }
|
||||
fn f(a: A) {
|
||||
let A { a<|>: a } = a;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct A { a: &'static str }
|
||||
fn f(a: A) {
|
||||
let A { a } = a;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_fix(
|
||||
r#"
|
||||
struct A { a: &'static str, b: &'static str }
|
||||
fn f(a: A) {
|
||||
let A { a<|>: a, b } = a;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct A { a: &'static str, b: &'static str }
|
||||
fn f(a: A) {
|
||||
let A { a, b } = a;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_field_from_usage() {
|
||||
check_fix(
|
||||
|
206
crates/ide/src/diagnostics/field_shorthand.rs
Normal file
206
crates/ide/src/diagnostics/field_shorthand.rs
Normal file
@ -0,0 +1,206 @@
|
||||
//! Suggests shortening `Foo { field: field }` to `Foo { field }` in both
|
||||
//! expressions and patterns.
|
||||
|
||||
use base_db::FileId;
|
||||
use ide_db::source_change::SourceFileEdit;
|
||||
use syntax::{ast, match_ast, AstNode, SyntaxNode};
|
||||
use text_edit::TextEdit;
|
||||
|
||||
use crate::{Diagnostic, Fix, Severity};
|
||||
|
||||
pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) {
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::RecordExpr(it) => check_expr_field_shorthand(acc, file_id, it),
|
||||
ast::RecordPat(it) => check_pat_field_shorthand(acc, file_id, it),
|
||||
_ => ()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn check_expr_field_shorthand(
|
||||
acc: &mut Vec<Diagnostic>,
|
||||
file_id: FileId,
|
||||
record_expr: ast::RecordExpr,
|
||||
) {
|
||||
let record_field_list = match record_expr.record_expr_field_list() {
|
||||
Some(it) => it,
|
||||
None => return,
|
||||
};
|
||||
for record_field in record_field_list.fields() {
|
||||
let (name_ref, expr) = match record_field.name_ref().zip(record_field.expr()) {
|
||||
Some(it) => it,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let field_name = name_ref.syntax().text().to_string();
|
||||
let field_expr = expr.syntax().text().to_string();
|
||||
let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
|
||||
if field_name != field_expr || field_name_is_tup_index {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut edit_builder = TextEdit::builder();
|
||||
edit_builder.delete(record_field.syntax().text_range());
|
||||
edit_builder.insert(record_field.syntax().text_range().start(), field_name);
|
||||
let edit = edit_builder.finish();
|
||||
|
||||
let field_range = record_field.syntax().text_range();
|
||||
acc.push(Diagnostic {
|
||||
// name: None,
|
||||
range: field_range,
|
||||
message: "Shorthand struct initialization".to_string(),
|
||||
severity: Severity::WeakWarning,
|
||||
fix: Some(Fix::new(
|
||||
"Use struct shorthand initialization",
|
||||
SourceFileEdit { file_id, edit }.into(),
|
||||
field_range,
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn check_pat_field_shorthand(
|
||||
acc: &mut Vec<Diagnostic>,
|
||||
file_id: FileId,
|
||||
record_pat: ast::RecordPat,
|
||||
) {
|
||||
let record_pat_field_list = match record_pat.record_pat_field_list() {
|
||||
Some(it) => it,
|
||||
None => return,
|
||||
};
|
||||
for record_pat_field in record_pat_field_list.fields() {
|
||||
let (name_ref, pat) = match record_pat_field.name_ref().zip(record_pat_field.pat()) {
|
||||
Some(it) => it,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let field_name = name_ref.syntax().text().to_string();
|
||||
let field_pat = pat.syntax().text().to_string();
|
||||
let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
|
||||
if field_name != field_pat || field_name_is_tup_index {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut edit_builder = TextEdit::builder();
|
||||
edit_builder.delete(record_pat_field.syntax().text_range());
|
||||
edit_builder.insert(record_pat_field.syntax().text_range().start(), field_name);
|
||||
let edit = edit_builder.finish();
|
||||
|
||||
let field_range = record_pat_field.syntax().text_range();
|
||||
acc.push(Diagnostic {
|
||||
// name: None,
|
||||
range: field_range,
|
||||
message: "Shorthand struct pattern".to_string(),
|
||||
severity: Severity::WeakWarning,
|
||||
fix: Some(Fix::new(
|
||||
"Use struct field shorthand",
|
||||
SourceFileEdit { file_id, edit }.into(),
|
||||
field_range,
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::diagnostics::tests::{check_fix, check_no_diagnostics};
|
||||
|
||||
#[test]
|
||||
fn test_check_expr_field_shorthand() {
|
||||
check_no_diagnostics(
|
||||
r#"
|
||||
struct A { a: &'static str }
|
||||
fn main() { A { a: "hello" } }
|
||||
"#,
|
||||
);
|
||||
check_no_diagnostics(
|
||||
r#"
|
||||
struct A(usize);
|
||||
fn main() { A { 0: 0 } }
|
||||
"#,
|
||||
);
|
||||
|
||||
check_fix(
|
||||
r#"
|
||||
struct A { a: &'static str }
|
||||
fn main() {
|
||||
let a = "haha";
|
||||
A { a<|>: a }
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct A { a: &'static str }
|
||||
fn main() {
|
||||
let a = "haha";
|
||||
A { a }
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_fix(
|
||||
r#"
|
||||
struct A { a: &'static str, b: &'static str }
|
||||
fn main() {
|
||||
let a = "haha";
|
||||
let b = "bb";
|
||||
A { a<|>: a, b }
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct A { a: &'static str, b: &'static str }
|
||||
fn main() {
|
||||
let a = "haha";
|
||||
let b = "bb";
|
||||
A { a, b }
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_pat_field_shorthand() {
|
||||
check_no_diagnostics(
|
||||
r#"
|
||||
struct A { a: &'static str }
|
||||
fn f(a: A) { let A { a: hello } = a; }
|
||||
"#,
|
||||
);
|
||||
check_no_diagnostics(
|
||||
r#"
|
||||
struct A(usize);
|
||||
fn f(a: A) { let A { 0: 0 } = a; }
|
||||
"#,
|
||||
);
|
||||
|
||||
check_fix(
|
||||
r#"
|
||||
struct A { a: &'static str }
|
||||
fn f(a: A) {
|
||||
let A { a<|>: a } = a;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct A { a: &'static str }
|
||||
fn f(a: A) {
|
||||
let A { a } = a;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_fix(
|
||||
r#"
|
||||
struct A { a: &'static str, b: &'static str }
|
||||
fn f(a: A) {
|
||||
let A { a<|>: a, b } = a;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct A { a: &'static str, b: &'static str }
|
||||
fn f(a: A) {
|
||||
let A { a, b } = a;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user