diff --git a/.changeset/red-buckets-sing.md b/.changeset/red-buckets-sing.md new file mode 100644 index 000000000000..e1a097916da0 --- /dev/null +++ b/.changeset/red-buckets-sing.md @@ -0,0 +1,22 @@ +--- +'@biomejs/biome': patch +--- + +Fixed [#8288](https://github.com/biomejs/biome/issues/8288): Fixed the issue with false positive errors + +This new change will ignore attribute and only show diagnostics for JSX Expressions + +For example + +Valid: + +```jsx + +``` + +Invalid: +```jsx +const Component = () =>{ + return isOpen && items.length +} +``` diff --git a/crates/biome_js_analyze/src/lint/nursery/no_leaked_render.rs b/crates/biome_js_analyze/src/lint/nursery/no_leaked_render.rs index 1312ef0fe5c6..5ed35547c4fd 100644 --- a/crates/biome_js_analyze/src/lint/nursery/no_leaked_render.rs +++ b/crates/biome_js_analyze/src/lint/nursery/no_leaked_render.rs @@ -4,8 +4,8 @@ use biome_analyze::{ use biome_console::markup; use biome_js_syntax::{ AnyJsExpression, JsConditionalExpression, JsLogicalExpression, JsLogicalOperator, JsSyntaxNode, - JsxExpressionAttributeValue, JsxExpressionChild, JsxTagExpression, - binding_ext::AnyJsBindingDeclaration, + JsxExpressionChild, JsxTagExpression, binding_ext::AnyJsBindingDeclaration, + jsx_ext::AnyJsxElement, }; use biome_rowan::{AstNode, declare_node_union}; use biome_rule_options::no_leaked_render::NoLeakedRenderOptions; @@ -106,7 +106,7 @@ impl Rule for NoLeakedRender { let query = ctx.query(); let model = ctx.model(); - if !is_inside_jsx_expression(query.syntax()) { + if !is_inside_jsx_expression(query.syntax()).unwrap_or_default() { return None; } @@ -267,10 +267,12 @@ declare_node_union! { pub NoLeakedRenderQuery = JsLogicalExpression | JsConditionalExpression } -fn is_inside_jsx_expression(node: &JsSyntaxNode) -> bool { - node.ancestors().any(|ancestor| { - JsxExpressionChild::can_cast(ancestor.kind()) - || JsxExpressionAttributeValue::can_cast(ancestor.kind()) - || JsxTagExpression::can_cast(ancestor.kind()) - }) +fn is_inside_jsx_expression(node: &JsSyntaxNode) -> Option { + let parent = node.parent()?; + + Some( + JsxExpressionChild::can_cast(parent.kind()) + || JsxTagExpression::can_cast(parent.kind()) + || AnyJsxElement::can_cast(parent.kind()), + ) } diff --git a/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/invalid.jsx b/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/invalid.jsx index acca87e420b2..e9d248104325 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/invalid.jsx +++ b/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/invalid.jsx @@ -56,28 +56,14 @@ const MyComponent3 = () => { return
{maybeObject && (isFoo ? : )}
; }; -const MyComponent4 = () => { - return ; -}; - -const MyComponent5 = () => { - return ; -}; - -const isOpen1 = 0; -const Component7 = () => { - return 0} />; -}; - -const Component8 = ({ count, title }) => { +const MyComponent4 = ({ count, title }) => { return
{(((((count))))) && ((title))}
; }; -const Component9 = ({ data }) => { +const MyComponent5 = ({ data }) => { return
{(((((data)))) && (((((data.value))))))}
; -}; +} -const Component = ({ value }) => { +const MyComponent6 = ({ value }) => { return
{(((value))) && }
; }; - diff --git a/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/invalid.jsx.snap b/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/invalid.jsx.snap index c07711cd5ef3..eba5ab0fde20 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/invalid.jsx.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/invalid.jsx.snap @@ -62,32 +62,18 @@ const MyComponent3 = () => { return
{maybeObject && (isFoo ? : )}
; }; -const MyComponent4 = () => { - return ; -}; - -const MyComponent5 = () => { - return ; -}; - -const isOpen1 = 0; -const Component7 = () => { - return 0} />; -}; - -const Component8 = ({ count, title }) => { +const MyComponent4 = ({ count, title }) => { return
{(((((count))))) && ((title))}
; }; -const Component9 = ({ data }) => { +const MyComponent5 = ({ data }) => { return
{(((((data)))) && (((((data.value))))))}
; -}; +} -const Component = ({ value }) => { +const MyComponent6 = ({ value }) => { return
{(((value))) && }
; }; - ``` # Diagnostics @@ -318,107 +304,16 @@ invalid.jsx:56:15 lint/nursery/noLeakedRender ━━━━━━━━━━━ ``` ``` -invalid.jsx:60:29 lint/nursery/noLeakedRender ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:60:15 lint/nursery/noLeakedRender ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ i Potential leaked value that might cause unintended rendering. - 59 │ const MyComponent4 = () => { - > 60 │ return ; - │ ^^^^^^^^^^^^^^^^^^^^^ + 59 │ const MyComponent4 = ({ count, title }) => { + > 60 │ return
{(((((count))))) && ((title))}
; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 61 │ }; 62 │ - i This happens when you use ternary operators in JSX with alternate values that could be variables. - - i Replace with a safe alternate value like an empty string , null or another JSX element. - - -``` - -``` -invalid.jsx:64:29 lint/nursery/noLeakedRender ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - i Potential leaked value that might cause unintended rendering. - - 63 │ const MyComponent5 = () => { - > 64 │ return ; - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 65 │ }; - 66 │ - - i This happens when you use ternary operators in JSX with alternate values that could be variables. - - i Replace with a safe alternate value like an empty string , null or another JSX element. - - -``` - -``` -invalid.jsx:64:29 lint/nursery/noLeakedRender ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - i Potential leaked value that might cause unintended rendering. - - 63 │ const MyComponent5 = () => { - > 64 │ return ; - │ ^^^^^^^^^^^^^^^^^^^^^^^ - 65 │ }; - 66 │ - - i JavaScript's && operator returns the left value when it's falsy (e.g., 0, NaN, ''). React will render that value, causing unexpected UI output. - - i Make sure the condition is explicitly boolean.Use !!value, value > 0, or a ternary expression. - - -``` - -``` -invalid.jsx:69:24 lint/nursery/noLeakedRender ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - i Potential leaked value that might cause unintended rendering. - - 67 │ const isOpen1 = 0; - 68 │ const Component7 = () => { - > 69 │ return 0} />; - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 70 │ }; - 71 │ - - i JavaScript's && operator returns the left value when it's falsy (e.g., 0, NaN, ''). React will render that value, causing unexpected UI output. - - i Make sure the condition is explicitly boolean.Use !!value, value > 0, or a ternary expression. - - -``` - -``` -invalid.jsx:73:15 lint/nursery/noLeakedRender ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - i Potential leaked value that might cause unintended rendering. - - 72 │ const Component8 = ({ count, title }) => { - > 73 │ return
{(((((count))))) && ((title))}
; - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 74 │ }; - 75 │ - - i JavaScript's && operator returns the left value when it's falsy (e.g., 0, NaN, ''). React will render that value, causing unexpected UI output. - - i Make sure the condition is explicitly boolean.Use !!value, value > 0, or a ternary expression. - - -``` - -``` -invalid.jsx:77:16 lint/nursery/noLeakedRender ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - i Potential leaked value that might cause unintended rendering. - - 76 │ const Component9 = ({ data }) => { - > 77 │ return
{(((((data)))) && (((((data.value))))))}
; - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 78 │ }; - 79 │ - i JavaScript's && operator returns the left value when it's falsy (e.g., 0, NaN, ''). React will render that value, causing unexpected UI output. i Make sure the condition is explicitly boolean.Use !!value, value > 0, or a ternary expression. @@ -427,15 +322,15 @@ invalid.jsx:77:16 lint/nursery/noLeakedRender ━━━━━━━━━━━ ``` ``` -invalid.jsx:81:15 lint/nursery/noLeakedRender ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.jsx:68:15 lint/nursery/noLeakedRender ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ i Potential leaked value that might cause unintended rendering. - 80 │ const Component = ({ value }) => { - > 81 │ return
{(((value))) && }
; + 67 │ const MyComponent6 = ({ value }) => { + > 68 │ return
{(((value))) && }
; │ ^^^^^^^^^^^^^^^^^^^^^^^ - 82 │ }; - 83 │ + 69 │ }; + 70 │ i JavaScript's && operator returns the left value when it's falsy (e.g., 0, NaN, ''). React will render that value, causing unexpected UI output. diff --git a/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/issue8288.invalid.jsx b/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/issue8288.invalid.jsx new file mode 100644 index 000000000000..7d955fb9a559 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/issue8288.invalid.jsx @@ -0,0 +1,8 @@ +/* should generate diagnostics */ +const Component1 = () => { + return ( + + {userId ? 1 : undefined} + + ); +}; diff --git a/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/issue8288.invalid.jsx.snap b/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/issue8288.invalid.jsx.snap new file mode 100644 index 000000000000..e308e7d6672a --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/issue8288.invalid.jsx.snap @@ -0,0 +1,36 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: issue8288.invalid.jsx +--- +# Input +```jsx +/* should generate diagnostics */ +const Component1 = () => { + return ( + + {userId ? 1 : undefined} + + ); +}; + +``` + +# Diagnostics +``` +issue8288.invalid.jsx:5:19 lint/nursery/noLeakedRender ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + i Potential leaked value that might cause unintended rendering. + + 3 │ return ( + 4 │ + > 5 │ {userId ? 1 : undefined} + │ ^^^^^^^^^^^^^^^^^^^^^^ + 6 │ + 7 │ ); + + i This happens when you use ternary operators in JSX with alternate values that could be variables. + + i Replace with a safe alternate value like an empty string , null or another JSX element. + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/issue8288.valid.jsx b/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/issue8288.valid.jsx new file mode 100644 index 000000000000..efff56a1f3dd --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noLeakedRender/issue8288.valid.jsx @@ -0,0 +1,35 @@ +/* should not generate diagnostics */ +function getValue() { + return Math.random() > 0.5; +} + +export default function MyComponent() { + return