Skip to content

Commit b39544e

Browse files
committedAug 27, 2024·
fix(linter/jest): fixer for prefer-jest-mocked creates invalid LHS expressions (#5243)
closes #5228 interestingly `eslint-plugin-jest` reports an error on this code, it also immedietly fixes it. however fixing here is not good as it results in invalid code. should fix https://github.com/oxc-project/oxlint-ecosystem-ci/actions/runs/10518058484/job/29247525457
1 parent 9f772c9 commit b39544e

File tree

1 file changed

+40
-14
lines changed

1 file changed

+40
-14
lines changed
 

‎crates/oxc_linter/src/rules/jest/prefer_jest_mocked.rs

+40-14
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use oxc_macros::declare_oxc_lint;
77
use oxc_span::{GetSpan, Span};
88
use phf::{phf_set, Set};
99

10-
use crate::{context::LintContext, rule::Rule, AstNode};
10+
use crate::{ast_util::outermost_paren_parent, context::LintContext, rule::Rule, AstNode};
1111

1212
fn use_jest_mocked(span: Span) -> OxcDiagnostic {
1313
OxcDiagnostic::warn("Prefer `jest.mocked()` over `fn as jest.Mock`.")
@@ -51,17 +51,17 @@ declare_oxc_lint!(
5151
/// ```
5252
PreferJestMocked,
5353
style,
54-
fix
54+
conditional_fix
5555
);
5656

5757
impl Rule for PreferJestMocked {
58-
fn run(&self, node: &AstNode, ctx: &LintContext) {
58+
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
5959
if let AstKind::TSAsExpression(ts_expr) = node.kind() {
6060
if !matches!(ctx.nodes().parent_kind(node.id()), Some(AstKind::TSAsExpression(_))) {
61-
Self::check_ts_as_expression(ts_expr, ctx);
61+
Self::check_ts_as_expression(node, ts_expr, ctx);
6262
}
6363
} else if let AstKind::TSTypeAssertion(assert_type) = node.kind() {
64-
Self::check_assert_type(assert_type, ctx);
64+
Self::check_assert_type(node, assert_type, ctx);
6565
}
6666
}
6767
}
@@ -74,23 +74,37 @@ const MOCK_TYPES: Set<&'static str> = phf_set! {
7474
};
7575

7676
impl PreferJestMocked {
77-
fn check_ts_as_expression(as_expr: &TSAsExpression, ctx: &LintContext) {
77+
fn check_ts_as_expression<'a>(
78+
node: &AstNode<'a>,
79+
as_expr: &TSAsExpression,
80+
ctx: &LintContext<'a>,
81+
) {
7882
let TSType::TSTypeReference(ts_reference) = &as_expr.type_annotation else {
7983
return;
8084
};
8185
let arg_span = as_expr.expression.get_inner_expression().span();
82-
Self::check(ts_reference, arg_span, as_expr.span, ctx);
86+
Self::check(node, ts_reference, arg_span, as_expr.span, ctx);
8387
}
8488

85-
fn check_assert_type(assert_type: &TSTypeAssertion, ctx: &LintContext) {
89+
fn check_assert_type<'a>(
90+
node: &AstNode<'a>,
91+
assert_type: &TSTypeAssertion,
92+
ctx: &LintContext<'a>,
93+
) {
8694
let TSType::TSTypeReference(ts_reference) = &assert_type.type_annotation else {
8795
return;
8896
};
8997
let arg_span = assert_type.expression.get_inner_expression().span();
90-
Self::check(ts_reference, arg_span, assert_type.span, ctx);
98+
Self::check(node, ts_reference, arg_span, assert_type.span, ctx);
9199
}
92100

93-
fn check(ts_reference: &TSTypeReference, arg_span: Span, span: Span, ctx: &LintContext) {
101+
fn check<'a>(
102+
node: &AstNode<'a>,
103+
ts_reference: &TSTypeReference,
104+
arg_span: Span,
105+
span: Span,
106+
ctx: &LintContext<'a>,
107+
) {
94108
let TSTypeName::QualifiedName(qualified_name) = &ts_reference.type_name else {
95109
return;
96110
};
@@ -104,13 +118,22 @@ impl PreferJestMocked {
104118
return;
105119
}
106120

107-
ctx.diagnostic_with_fix(use_jest_mocked(span), |fixer| {
108-
let span_source_code = fixer.source_range(arg_span);
109-
fixer.replace(span, format!("jest.mocked({span_source_code})"))
110-
});
121+
if can_fix(node, ctx) {
122+
ctx.diagnostic_with_fix(use_jest_mocked(span), |fixer| {
123+
let span_source_code = fixer.source_range(arg_span);
124+
fixer.replace(span, format!("jest.mocked({span_source_code})"))
125+
});
126+
} else {
127+
ctx.diagnostic(use_jest_mocked(span));
128+
}
111129
}
112130
}
113131

132+
fn can_fix<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bool {
133+
outermost_paren_parent(node, ctx)
134+
.map_or(false, |parent| !matches!(parent.kind(), AstKind::SimpleAssignmentTarget(_)))
135+
}
136+
114137
#[test]
115138
fn test() {
116139
use crate::tester::Tester;
@@ -320,6 +343,9 @@ fn test() {
320343
).mockReturnValue(1);
321344
",
322345
),
346+
// we can't fix this case, as fixing it would result in a syntax error
347+
// (we'd be attempting attempting to assign `jest.fn()` to invalid left-hand side)
348+
("(foo as jest.Mock) = jest.fn();", "(foo as jest.Mock) = jest.fn();"),
323349
];
324350

325351
Tester::new(PreferJestMocked::NAME, pass, fail)

0 commit comments

Comments
 (0)
Please sign in to comment.