Skip to content

Commit 0628e0a

Browse files
authored
fix(linter): useExhaustiveDependencies proper rest parameters handling, fixes #8967 (#9033)
1 parent 6ebf6c6 commit 0628e0a

File tree

4 files changed

+89
-19
lines changed

4 files changed

+89
-19
lines changed

.changeset/four-cougars-fail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Fixed [#8967](https://github.com/biomejs/biome/issues/8967). [useExhaustiveDependencies](https://biomejs.dev/linter/rules/use-exhaustive-dependencies/) no longer reports false positives for variables destructured from a rest pattern.

crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,13 +1003,14 @@ fn get_single_pattern_member(
10031003
.map(|member| {
10041004
(
10051005
array_pattern.syntax().clone(),
1006-
ReactHookResultMember::Index(member),
1006+
Some(ReactHookResultMember::Index(member)),
10071007
)
10081008
})
10091009
})
10101010
}),
10111011
JsSyntaxKind::JS_OBJECT_BINDING_PATTERN_PROPERTY
1012-
| JsSyntaxKind::JS_OBJECT_BINDING_PATTERN_SHORTHAND_PROPERTY => {
1012+
| JsSyntaxKind::JS_OBJECT_BINDING_PATTERN_SHORTHAND_PROPERTY
1013+
| JsSyntaxKind::JS_OBJECT_BINDING_PATTERN_REST => {
10131014
let Some(object_pattern) = parent_syntax
10141015
.parent()
10151016
.and_then(JsObjectBindingPatternPropertyList::cast)
@@ -1019,27 +1020,41 @@ fn get_single_pattern_member(
10191020
else {
10201021
return GetSinglePatternMemberResult::Unknown;
10211022
};
1022-
let Some(member) = (match AnyJsObjectBindingPatternMember::try_cast(parent_syntax) {
1023-
Ok(AnyJsObjectBindingPatternMember::JsObjectBindingPatternProperty(property)) => {
1024-
property
1023+
if matches!(
1024+
parent_syntax.kind(),
1025+
JsSyntaxKind::JS_OBJECT_BINDING_PATTERN_REST
1026+
) {
1027+
Some((object_pattern.syntax().clone(), None))
1028+
} else if let Some(member) =
1029+
match AnyJsObjectBindingPatternMember::try_cast(parent_syntax) {
1030+
Ok(AnyJsObjectBindingPatternMember::JsObjectBindingPatternProperty(
1031+
property,
1032+
)) => property
10251033
.member()
10261034
.ok()
10271035
.and_then(|member| member.name())
1028-
.map(ReactHookResultMember::Key)
1036+
.map(ReactHookResultMember::Key),
1037+
Ok(
1038+
AnyJsObjectBindingPatternMember::JsObjectBindingPatternShorthandProperty(
1039+
shorthand_property,
1040+
),
1041+
) => shorthand_property
1042+
.identifier()
1043+
.ok()
1044+
.and_then(|identifier| {
1045+
identifier.as_js_identifier_binding()?.name_token().ok()
1046+
})
1047+
.map(|name_token| {
1048+
ReactHookResultMember::Key(name_token.token_text_trimmed())
1049+
}),
1050+
// Shouldn't happen because of the previous check
1051+
_ => None,
10291052
}
1030-
Ok(AnyJsObjectBindingPatternMember::JsObjectBindingPatternShorthandProperty(
1031-
shorthand_property,
1032-
)) => shorthand_property
1033-
.identifier()
1034-
.ok()
1035-
.and_then(|identifier| identifier.as_js_identifier_binding()?.name_token().ok())
1036-
.map(|name_token| ReactHookResultMember::Key(name_token.token_text_trimmed())),
1037-
// Shouldn't happen because of the previous check
1038-
_ => None,
1039-
}) else {
1053+
{
1054+
Some((object_pattern.syntax().clone(), Some(member)))
1055+
} else {
10401056
return GetSinglePatternMemberResult::Unknown;
1041-
};
1042-
Some((object_pattern.syntax().clone(), member))
1057+
}
10431058
}
10441059
JsSyntaxKind::JS_VARIABLE_DECLARATOR => {
10451060
return GetSinglePatternMemberResult::NoPattern;
@@ -1054,7 +1069,10 @@ fn get_single_pattern_member(
10541069
{
10551070
return GetSinglePatternMemberResult::TooDeep;
10561071
}
1057-
GetSinglePatternMemberResult::Member(member)
1072+
match member {
1073+
Some(member) => GetSinglePatternMemberResult::Member(member),
1074+
None => GetSinglePatternMemberResult::NoPattern,
1075+
}
10581076
}
10591077

10601078
enum GetSinglePatternMemberResult {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* should not generate diagnostics */
2+
3+
import { useEffect } from "react";
4+
5+
// Issue #8967: Rest pattern destructuring should not cause false positives
6+
function Component(props) {
7+
const { data, ...restProps } = props;
8+
const { prop1 } = restProps;
9+
10+
useEffect(() => {
11+
console.log(prop1);
12+
}, [prop1]);
13+
14+
useEffect(() => {
15+
console.log(restProps.prop1);
16+
}, [restProps.prop1]);
17+
18+
return null;
19+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
source: crates/biome_js_analyze/tests/spec_tests.rs
3+
assertion_line: 152
4+
expression: issue8967.js
5+
---
6+
# Input
7+
```js
8+
/* should not generate diagnostics */
9+
10+
import { useEffect } from "react";
11+
12+
// Issue #8967: Rest pattern destructuring should not cause false positives
13+
function Component(props) {
14+
const { data, ...restProps } = props;
15+
const { prop1 } = restProps;
16+
17+
useEffect(() => {
18+
console.log(prop1);
19+
}, [prop1]);
20+
21+
useEffect(() => {
22+
console.log(restProps.prop1);
23+
}, [restProps.prop1]);
24+
25+
return null;
26+
}
27+
28+
```

0 commit comments

Comments
 (0)