From e373ce81fe1cee1d6dbe70fffeb0f21b4013031d Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Mon, 28 Nov 2022 11:09:16 +0000 Subject: [PATCH] fix(rome_js_analyze): false positives for `noUselessFragments` #3668 (#3858) --- crates/rome_js_analyze/src/lib.rs | 15 +++----- .../correctness/no_useless_fragments.rs | 37 ++++++++++++------- .../noUselessFragments/issue_3668.jsx | 9 +++++ .../noUselessFragments/issue_3668.jsx.snap | 19 ++++++++++ 4 files changed, 57 insertions(+), 23 deletions(-) create mode 100644 crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/issue_3668.jsx create mode 100644 crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/issue_3668.jsx.snap diff --git a/crates/rome_js_analyze/src/lib.rs b/crates/rome_js_analyze/src/lib.rs index 3ff5eb964e1..322a0b0401b 100644 --- a/crates/rome_js_analyze/src/lib.rs +++ b/crates/rome_js_analyze/src/lib.rs @@ -134,6 +134,7 @@ mod tests { use rome_diagnostics::{Diagnostic, DiagnosticExt, PrintDiagnostic, Severity}; use rome_js_parser::parse; use rome_js_syntax::{SourceType, TextRange, TextSize}; + use std::slice; use crate::{analyze, AnalysisFilter, ControlFlow}; @@ -149,25 +150,21 @@ mod tests { String::from_utf8(buffer).unwrap() } - const SOURCE: &str = r#"function f() { - return ( -
- ) - }; - f(); + const SOURCE: &str = r#"const obj = { + element: <>test +}; "#; let parsed = parse(SOURCE, FileId::zero(), SourceType::jsx()); let mut error_ranges: Vec = Vec::new(); let options = AnalyzerOptions::default(); - let _rule_filter = RuleFilter::Rule("correctness", "flipBinExp"); + let rule_filter = RuleFilter::Rule("correctness", "noUselessFragments"); analyze( FileId::zero(), &parsed.tree(), AnalysisFilter { - // enabled_rules: Some(slice::from_ref(&rule_filter)), + enabled_rules: Some(slice::from_ref(&rule_filter)), ..AnalysisFilter::default() }, &options, diff --git a/crates/rome_js_analyze/src/semantic_analyzers/correctness/no_useless_fragments.rs b/crates/rome_js_analyze/src/semantic_analyzers/correctness/no_useless_fragments.rs index 465f6ece9e0..15bc8c22e0f 100644 --- a/crates/rome_js_analyze/src/semantic_analyzers/correctness/no_useless_fragments.rs +++ b/crates/rome_js_analyze/src/semantic_analyzers/correctness/no_useless_fragments.rs @@ -9,12 +9,10 @@ use rome_js_factory::make::{ ident, js_expression_statement, js_string_literal_expression, jsx_tag_expression, }; use rome_js_syntax::{ - JsLanguage, JsSyntaxKind, JsxAnyChild, JsxAnyElementName, JsxAnyTag, JsxChildList, JsxElement, - JsxFragment, JsxTagExpression, -}; -use rome_rowan::{ - declare_node_union, AstNode, AstNodeList, BatchMutation, BatchMutationExt, SyntaxNodeOptionExt, + JsLanguage, JsParenthesizedExpression, JsSyntaxKind, JsxAnyChild, JsxAnyElementName, JsxAnyTag, + JsxChildList, JsxElement, JsxFragment, JsxTagExpression, }; +use rome_rowan::{declare_node_union, AstNode, AstNodeList, BatchMutation, BatchMutationExt}; declare_rule! { /// Disallow unnecessary fragments @@ -108,31 +106,42 @@ impl Rule for NoUselessFragments { let model = ctx.model(); match node { NoUselessFragmentsQuery::JsxFragment(fragment) => { - let matches_allowed_parents = node + let parents_where_fragments_must_be_preserved = node .syntax() .parent() .map(|parent| match JsxTagExpression::try_cast(parent) { - Ok(parent) => { - let parent_kind = parent.syntax().parent().kind(); - matches!( - parent_kind, - Some( + Ok(parent) => parent + .syntax() + .parent() + .and_then(|parent| { + if let Some(parenthesized_expression) = + JsParenthesizedExpression::cast_ref(&parent) + { + parenthesized_expression.syntax().parent() + } else { + Some(parent) + } + }) + .map(|parent| { + matches!( + parent.kind(), JsSyntaxKind::JS_RETURN_STATEMENT | JsSyntaxKind::JS_INITIALIZER_CLAUSE | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION | JsSyntaxKind::JS_ARROW_FUNCTION_EXPRESSION | JsSyntaxKind::JS_FUNCTION_EXPRESSION | JsSyntaxKind::JS_FUNCTION_DECLARATION + | JsSyntaxKind::JS_PROPERTY_OBJECT_MEMBER ) - ) - } + }) + .unwrap_or(false), Err(_) => false, }) .unwrap_or(false); let child_list = fragment.children(); - if !matches_allowed_parents { + if !parents_where_fragments_must_be_preserved { match child_list.first() { Some(first) if child_list.len() == 1 => { Some(NoUselessFragmentsState::Child(first)) diff --git a/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/issue_3668.jsx b/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/issue_3668.jsx new file mode 100644 index 00000000000..edd82e59902 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/issue_3668.jsx @@ -0,0 +1,9 @@ +// should not trigger +function Component2() { + const str = 'str'; + return (<>{str}); +} + +const obj = { + element: <>test +}; diff --git a/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/issue_3668.jsx.snap b/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/issue_3668.jsx.snap new file mode 100644 index 00000000000..667a8bc7b0c --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/issue_3668.jsx.snap @@ -0,0 +1,19 @@ +--- +source: crates/rome_js_analyze/tests/spec_tests.rs +expression: issue_3668.jsx +--- +# Input +```js +// should not trigger +function Component2() { + const str = 'str'; + return (<>{str}); +} + +const obj = { + element: <>test +}; + +``` + +