From c8cbd398e4e54059b1594ef934ce96d6fc0c8130 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 15 Oct 2020 16:48:25 +0200 Subject: Prepare for pat_field_shorthand --- crates/ide/src/diagnostics.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'crates/ide') diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index b30cdb6ed..fb2a2fb28 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -15,7 +15,7 @@ use itertools::Itertools; use rustc_hash::FxHashSet; use syntax::{ ast::{self, AstNode}, - SyntaxNode, TextRange, T, + match_ast, SyntaxNode, TextRange, T, }; use text_edit::TextEdit; @@ -80,7 +80,7 @@ pub(crate) fn diagnostics( for node in parse.tree().syntax().descendants() { check_unnecessary_braces_in_use_statement(&mut res, file_id, &node); - check_struct_shorthand_initialization(&mut res, file_id, &node); + check_field_shorthand(&mut res, file_id, &node); } let res = RefCell::new(res); let sink_builder = DiagnosticSinkBuilder::new() @@ -188,12 +188,20 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement( None } -fn check_struct_shorthand_initialization( +fn check_field_shorthand(acc: &mut Vec, file_id: FileId, node: &SyntaxNode) { + match_ast! { + match node { + ast::RecordExpr(it) => check_expr_field_shorthand(acc, file_id, it), + _ => None + } + }; +} + +fn check_expr_field_shorthand( acc: &mut Vec, file_id: FileId, - node: &SyntaxNode, + record_lit: ast::RecordExpr, ) -> Option<()> { - let record_lit = ast::RecordExpr::cast(node.clone())?; let record_field_list = record_lit.record_expr_field_list()?; for record_field in record_field_list.fields() { if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) { -- cgit v1.2.3 From e9903a91cd2135d2102cfd8547576331bbc85857 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 15 Oct 2020 16:51:55 +0200 Subject: flatten --- crates/ide/src/diagnostics.rs | 59 ++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 26 deletions(-) (limited to 'crates/ide') diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index fb2a2fb28..d3d8c86b5 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -201,35 +201,42 @@ fn check_expr_field_shorthand( acc: &mut Vec, file_id: FileId, record_lit: ast::RecordExpr, -) -> Option<()> { - let record_field_list = record_lit.record_expr_field_list()?; +) { + let record_field_list = match record_lit.record_expr_field_list() { + Some(it) => it, + None => (), + }; for record_field in record_field_list.fields() { - if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) { - 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 { - 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, - )), - }); - } + 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, + )), + }); } - Some(()) } #[cfg(test)] -- cgit v1.2.3 From 1022672af0467edf480f50398989c3f8016ccd10 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 15 Oct 2020 17:03:52 +0200 Subject: Diagnose shorthand in patterns as well --- crates/ide/src/diagnostics.rs | 99 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 5 deletions(-) (limited to 'crates/ide') diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index d3d8c86b5..c36c321f2 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -192,7 +192,8 @@ fn check_field_shorthand(acc: &mut Vec, file_id: FileId, node: &Synt match_ast! { match node { ast::RecordExpr(it) => check_expr_field_shorthand(acc, file_id, it), - _ => None + ast::RecordPat(it) => check_pat_field_shorthand(acc, file_id, it), + _ => () } }; } @@ -200,11 +201,11 @@ fn check_field_shorthand(acc: &mut Vec, file_id: FileId, node: &Synt fn check_expr_field_shorthand( acc: &mut Vec, file_id: FileId, - record_lit: ast::RecordExpr, + record_expr: ast::RecordExpr, ) { - let record_field_list = match record_lit.record_expr_field_list() { + let record_field_list = match record_expr.record_expr_field_list() { Some(it) => it, - None => (), + None => return, }; for record_field in record_field_list.fields() { let (name_ref, expr) = match record_field.name_ref().zip(record_field.expr()) { @@ -239,6 +240,48 @@ fn check_expr_field_shorthand( } } +fn check_pat_field_shorthand( + acc: &mut Vec, + 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}; @@ -735,7 +778,7 @@ mod a { } #[test] - fn test_check_struct_shorthand_initialization() { + fn test_check_expr_field_shorthand() { check_no_diagnostics( r#" struct A { a: &'static str } @@ -786,6 +829,52 @@ fn main() { ); } + #[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( -- cgit v1.2.3 From 3086fb2ef4064ab7daa8194a8d57c742849ca900 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 15 Oct 2020 17:07:53 +0200 Subject: Move field_shorthand to a separate module --- crates/ide/src/diagnostics.rs | 201 +------------------------ crates/ide/src/diagnostics/field_shorthand.rs | 206 ++++++++++++++++++++++++++ 2 files changed, 211 insertions(+), 196 deletions(-) create mode 100644 crates/ide/src/diagnostics/field_shorthand.rs (limited to 'crates/ide') diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index c36c321f2..1e5ea4617 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -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 itertools::Itertools; 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, 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, - 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, - 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 @@ mod tests { /// 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 { ); } - #[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( diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide/src/diagnostics/field_shorthand.rs new file mode 100644 index 000000000..2c4acd783 --- /dev/null +++ b/crates/ide/src/diagnostics/field_shorthand.rs @@ -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, 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, + 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, + 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; +} +"#, + ); + } +} -- cgit v1.2.3