Skip to content

Commit b9d6aa5

Browse files
fix(linter): fix false positives in no-confusing-non-null-assertion (#4665)
1 parent cbf08d2 commit b9d6aa5

File tree

2 files changed

+51
-15
lines changed

2 files changed

+51
-15
lines changed

crates/oxc_linter/src/rules/typescript/no_confusing_non_null_assertion.rs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use oxc_ast::{
55
use oxc_diagnostics::OxcDiagnostic;
66
use oxc_macros::declare_oxc_lint;
77
use oxc_span::Span;
8+
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator};
89

910
use crate::{context::LintContext, rule::Rule, AstNode};
1011

@@ -37,14 +38,22 @@ fn not_need_no_confusing_non_null_assertion_diagnostic(op_str: &str, span: Span)
3738
.with_label(span)
3839
}
3940

40-
fn wrap_up_no_confusing_non_null_assertion_diagnostic(span: Span) -> OxcDiagnostic {
41-
OxcDiagnostic::warn(
42-
"Confusing combinations of non-null assertion and equal test like \"a! = b\", which looks very similar to not equal \"a != b\"."
43-
)
41+
fn wrap_up_no_confusing_non_null_assertion_diagnostic(op_str: &str, span: Span) -> OxcDiagnostic {
42+
OxcDiagnostic::warn(format!(
43+
"Confusing combinations of non-null assertion and equal test like \"a! {op_str} b\", which looks very similar to not equal \"a !{op_str} b\"."
44+
))
4445
.with_help("Wrap left-hand side in parentheses to avoid putting non-null assertion \"!\" and \"=\" together.")
4546
.with_label(span)
4647
}
4748

49+
fn confusing_non_null_assignment_assertion_diagnostic(op_str: &str, span: Span) -> OxcDiagnostic {
50+
OxcDiagnostic::warn(format!(
51+
"Confusing combinations of non-null assertion and assignment like \"a! {op_str} b\", which looks very similar to not equal \"a !{op_str} b\"."
52+
))
53+
.with_help("Remove the \"!\", or wrap the left-hand side in parentheses.")
54+
.with_label(span)
55+
}
56+
4857
fn get_depth_ends_in_bang(expr: &Expression<'_>) -> Option<u32> {
4958
match expr {
5059
Expression::TSNonNullExpression(_) => Some(0),
@@ -61,10 +70,16 @@ fn get_depth_ends_in_bang(expr: &Expression<'_>) -> Option<u32> {
6170
}
6271
}
6372

73+
fn is_confusable_operator(operator: BinaryOperator) -> bool {
74+
matches!(operator, BinaryOperator::Equality | BinaryOperator::StrictEquality)
75+
}
76+
6477
impl Rule for NoConfusingNonNullAssertion {
6578
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
6679
match node.kind() {
67-
AstKind::BinaryExpression(binary_expr) => {
80+
AstKind::BinaryExpression(binary_expr)
81+
if is_confusable_operator(binary_expr.operator) =>
82+
{
6883
let Some(bang_depth) = get_depth_ends_in_bang(&binary_expr.left) else {
6984
return;
7085
};
@@ -75,16 +90,19 @@ impl Rule for NoConfusingNonNullAssertion {
7590
));
7691
} else {
7792
ctx.diagnostic(wrap_up_no_confusing_non_null_assertion_diagnostic(
93+
binary_expr.operator.as_str(),
7894
binary_expr.span,
7995
));
8096
}
8197
}
82-
AstKind::AssignmentExpression(assignment_expr) => {
98+
AstKind::AssignmentExpression(assignment_expr)
99+
if assignment_expr.operator == AssignmentOperator::Assign =>
100+
{
83101
let Some(simple_target) = assignment_expr.left.as_simple_assignment_target() else {
84102
return;
85103
};
86104
let SimpleAssignmentTarget::TSNonNullExpression(_) = simple_target else { return };
87-
ctx.diagnostic(not_need_no_confusing_non_null_assertion_diagnostic(
105+
ctx.diagnostic(confusing_non_null_assignment_assertion_diagnostic(
88106
assignment_expr.operator.as_str(),
89107
assignment_expr.span,
90108
));
@@ -102,7 +120,25 @@ impl Rule for NoConfusingNonNullAssertion {
102120
fn test() {
103121
use crate::tester::Tester;
104122

105-
let pass = vec!["a == b!;", "a = b!;", "a !== b;", "a != b;", "(a + b!) == c;"]; // "(a + b!) = c;"]; that's a parse error??
123+
let pass = vec![
124+
"a == b!;",
125+
"a = b!;",
126+
"a !== b;",
127+
"a != b;",
128+
"(a + b!) == c;",
129+
"a! + b;",
130+
"a! += b;",
131+
"a! - b;",
132+
"a! -= b;",
133+
"a! / b;",
134+
"a! /= b;",
135+
"a! * b;",
136+
"a! *= b;",
137+
"a! ** b;",
138+
"a! **= b;",
139+
"a! != b;",
140+
"a! !== b;",
141+
];
106142
let fail = vec![
107143
"a! == b;",
108144
"a! === b;",

crates/oxc_linter/src/snapshots/no_confusing_non_null_assertion.snap

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ source: crates/oxc_linter/src/tester.rs
1515
╰────
1616
help: Remove the "!", or prefix the "=" with it.
1717

18-
typescript-eslint(no-confusing-non-null-assertion): Confusing combinations of non-null assertion and equal test like "a! = b", which looks very similar to not equal "a != b".
18+
typescript-eslint(no-confusing-non-null-assertion): Confusing combinations of non-null assertion and equal test like "a! == b", which looks very similar to not equal "a !== b".
1919
╭─[no_confusing_non_null_assertion.tsx:1:1]
2020
1a + b! == c;
2121
· ───────────
@@ -36,23 +36,23 @@ source: crates/oxc_linter/src/tester.rs
3636
╰────
3737
help: Remove the "!", or prefix the "=" with it.
3838

39-
typescript-eslint(no-confusing-non-null-assertion): Confusing combinations of non-null assertion and equal test like "a! = b", which looks very similar to not equal "a != b".
39+
typescript-eslint(no-confusing-non-null-assertion): Confusing combinations of non-null assertion and assignment like "a! = b", which looks very similar to not equal "a != b".
4040
╭─[no_confusing_non_null_assertion.tsx:1:1]
4141
1a! = b;
4242
· ──────
4343
╰────
44-
help: Remove the "!", or prefix the "=" with it.
44+
help: Remove the "!", or wrap the left-hand side in parentheses.
4545

46-
typescript-eslint(no-confusing-non-null-assertion): Confusing combinations of non-null assertion and equal test like "a! = b", which looks very similar to not equal "a != b".
46+
typescript-eslint(no-confusing-non-null-assertion): Confusing combinations of non-null assertion and assignment like "a! = b", which looks very similar to not equal "a != b".
4747
╭─[no_confusing_non_null_assertion.tsx:1:1]
4848
1 │ (obj = new new OuterObj().InnerObj).Name! = c;
4949
· ─────────────────────────────────────────────
5050
╰────
51-
help: Remove the "!", or prefix the "=" with it.
51+
help: Remove the "!", or wrap the left-hand side in parentheses.
5252

53-
typescript-eslint(no-confusing-non-null-assertion): Confusing combinations of non-null assertion and equal test like "a! = b", which looks very similar to not equal "a != b".
53+
typescript-eslint(no-confusing-non-null-assertion): Confusing combinations of non-null assertion and assignment like "a! = b", which looks very similar to not equal "a != b".
5454
╭─[no_confusing_non_null_assertion.tsx:1:1]
5555
1 │ (a=b)! =c;
5656
· ─────────
5757
╰────
58-
help: Remove the "!", or prefix the "=" with it.
58+
help: Remove the "!", or wrap the left-hand side in parentheses.

0 commit comments

Comments
 (0)