Skip to content

Commit e53f2fe

Browse files
sterliakovarendjr
andauthored
fix(biome_js_analyze): ignore as const and similar wrappers in noMagicNumbers rule (#6944)
Co-authored-by: Arend van Beelen jr. <[email protected]>
1 parent cfda528 commit e53f2fe

File tree

4 files changed

+62
-45
lines changed

4 files changed

+62
-45
lines changed

.changeset/crazy-moments-sniff.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 [#6910](https://github.com/biomejs/biome/issues/6910): Biome now ignores type casts and assertions when evaluating numbers for `noMagicNumbers` rule.

crates/biome_js_analyze/src/lint/nursery/no_magic_numbers.rs

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ use biome_js_syntax::{
88
JsComputedMemberAssignment, JsComputedMemberExpression, JsFormalParameter, JsInitializerClause,
99
JsNumberLiteralExpression, JsObjectBindingPatternShorthandProperty, JsParenthesizedExpression,
1010
JsPropertyClassMember, JsPropertyObjectMember, JsSyntaxNode, JsUnaryExpression,
11-
JsUnaryOperator, JsxExpressionAttributeValue, JsxExpressionChild, TsEnumMemberList,
12-
TsIndexedAccessType, TsNumberLiteralType, TsReturnTypeAnnotation, TsTypeAnnotation,
13-
TsUnionTypeVariantList,
11+
JsUnaryOperator, JsxExpressionAttributeValue, JsxExpressionChild, TsAsExpression,
12+
TsEnumMemberList, TsIndexedAccessType, TsNonNullAssertionExpression, TsNumberLiteralType,
13+
TsPredicateReturnType, TsReturnTypeAnnotation, TsSatisfiesExpression, TsTypeAnnotation,
14+
TsTypeAssertionExpression, TsUnionTypeVariantList,
1415
};
1516
use biome_rowan::{AstNode, declare_node_union};
1617
use biome_rule_options::no_magic_numbers::NoMagicNumbersOptions;
@@ -45,6 +46,11 @@ declare_lint_rule! {
4546
/// const TAX_RATE = 1.23;
4647
/// let total = price * TAX_RATE;
4748
/// ```
49+
///
50+
/// ```ts
51+
/// const TAX_RATE = 1.23 as const;
52+
/// let total = price * TAX_RATE;
53+
/// ```
4854
pub NoMagicNumbers {
4955
version: "2.1.0",
5056
name: "noMagicNumbers",
@@ -372,43 +378,28 @@ fn is_ts_numeric_literal_return_type(numeric_literal: &TsNumberLiteralType) -> b
372378
.is_some_and(|parent| TsReturnTypeAnnotation::can_cast(parent.kind()))
373379
}
374380

375-
/// Omits unary plus or minus expressions by returning the parent node if
376-
/// the current node is a unary expression with a plus or minus operator.
377-
/// Example: `-5` or `+3` will return the parent node, effectively skipping the unary operator parent node.
378-
fn omit_unary_plus_minus_parent(node: JsSyntaxNode) -> Option<JsSyntaxNode> {
379-
let unary_expression_plus_or_minus =
380-
JsUnaryExpression::cast(node.clone()).and_then(|unary_node| {
381-
if unary_node.operator().is_ok_and(|operator| {
382-
operator == JsUnaryOperator::Plus || operator == JsUnaryOperator::Minus
383-
}) {
384-
Some(unary_node)
385-
} else {
386-
None
387-
}
388-
});
389-
390-
if unary_expression_plus_or_minus.is_some() {
391-
node.parent()
392-
} else {
393-
Some(node)
394-
}
395-
}
396-
397-
/// Omits the parenthesized expression if the node is wrapped in parentheses.
398-
/// Example: `(5)` will return the parent node, effectively skipping the parenthesized expression.
399-
fn omit_parenthesized_parent(node: JsSyntaxNode) -> Option<JsSyntaxNode> {
400-
if JsParenthesizedExpression::can_cast(node.kind()) {
401-
node.parent()
402-
} else {
403-
Some(node)
404-
}
405-
}
406-
407-
/// Returns the parent node of the given node, skipping over any unary plus/minus or parenthesized expression wrappers.
381+
/// Returns the parent node of the given node, skipping over any unary plus/minus or parenthesized expression wrappers,
382+
/// as well as all type-level wrappers (casts and `satisfies`).
408383
/// It helps determine the true syntactic context of the node by ignoring these common, but semantically insignificant, wrappers.
409384
fn get_sanitized_parent_node(node: &JsSyntaxNode) -> Option<JsSyntaxNode> {
410-
node.parent()
411-
.and_then(|parent| omit_unary_plus_minus_parent(parent).and_then(omit_parenthesized_parent))
385+
node.ancestors().skip(1).find(|parent| {
386+
if TsAsExpression::can_cast(parent.kind())
387+
|| TsNonNullAssertionExpression::can_cast(parent.kind())
388+
|| TsSatisfiesExpression::can_cast(parent.kind())
389+
|| TsTypeAssertionExpression::can_cast(parent.kind())
390+
|| TsPredicateReturnType::can_cast(parent.kind())
391+
|| JsParenthesizedExpression::can_cast(parent.kind())
392+
{
393+
false
394+
} else if let Some(parent) = JsUnaryExpression::cast_ref(parent) {
395+
match parent.operator() {
396+
Err(_) => true,
397+
Ok(op) => !matches!(op, JsUnaryOperator::Plus | JsUnaryOperator::Minus),
398+
}
399+
} else {
400+
true
401+
}
402+
})
412403
}
413404

414405
const ALWAYS_IGNORED_IN_ARITHMETIC_OPERATIONS: &[&str] = &[

crates/biome_js_analyze/tests/specs/nursery/noMagicNumbers/valid.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ const MY_NUMBER = +42;
88
const foo = 42;
99
let foo = 42;
1010
let foo = -42;
11+
const foo = 42 as const;
12+
const foo = 42 as number;
13+
const foo = 42 satisfies number;
14+
const foo = (42 satisfies number) as const;
15+
const foo = ((42 satisfies number) as const);
16+
const foo = (42 satisfies number)!;
1117

1218
// jsx
1319
let jsx = (<div>{5}</div>);
@@ -95,9 +101,9 @@ function f(x: string): 100 {
95101
}
96102

97103
// bitwise operations
98-
let bitwiseOr = 1 | 2;
99-
let bitwiseAnd = 1 & 2;
100-
let bitwiseXor = 1 ^ 2;
104+
let bitwiseOr = 1 | 42;
105+
let bitwiseAnd = 1 & 42;
106+
let bitwiseXor = 1 ^ 42;
101107

102108
let a = ~5;
103109
let a = -5;
@@ -141,3 +147,8 @@ f(1n)
141147
f(-1n)
142148
f(1)
143149
f((-1))
150+
151+
// type predicate
152+
function isFourtyTwo(num): num is 42 {
153+
return true;
154+
}

crates/biome_js_analyze/tests/specs/nursery/noMagicNumbers/valid.tsx.snap

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
---
22
source: crates/biome_js_analyze/tests/spec_tests.rs
3-
assertion_line: 134
43
expression: valid.tsx
54
---
65
# Input
@@ -15,6 +14,12 @@ const MY_NUMBER = +42;
1514
const foo = 42;
1615
let foo = 42;
1716
let foo = -42;
17+
const foo = 42 as const;
18+
const foo = 42 as number;
19+
const foo = 42 satisfies number;
20+
const foo = (42 satisfies number) as const;
21+
const foo = ((42 satisfies number) as const);
22+
const foo = (42 satisfies number)!;
1823
1924
// jsx
2025
let jsx = (<div>{5}</div>);
@@ -102,9 +107,9 @@ function f(x: string): 100 {
102107
}
103108
104109
// bitwise operations
105-
let bitwiseOr = 1 | 2;
106-
let bitwiseAnd = 1 & 2;
107-
let bitwiseXor = 1 ^ 2;
110+
let bitwiseOr = 1 | 42;
111+
let bitwiseAnd = 1 & 42;
112+
let bitwiseXor = 1 ^ 42;
108113
109114
let a = ~5;
110115
let a = -5;
@@ -149,4 +154,9 @@ f(-1n)
149154
f(1)
150155
f((-1))
151156
157+
// type predicate
158+
function isFourtyTwo(num): num is 42 {
159+
return true;
160+
}
161+
152162
```

0 commit comments

Comments
 (0)