mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
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
This commit is contained in:
parent
9f772c9872
commit
b39544e0f2
1 changed files with 40 additions and 14 deletions
|
|
@ -7,7 +7,7 @@ use oxc_macros::declare_oxc_lint;
|
|||
use oxc_span::{GetSpan, Span};
|
||||
use phf::{phf_set, Set};
|
||||
|
||||
use crate::{context::LintContext, rule::Rule, AstNode};
|
||||
use crate::{ast_util::outermost_paren_parent, context::LintContext, rule::Rule, AstNode};
|
||||
|
||||
fn use_jest_mocked(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn("Prefer `jest.mocked()` over `fn as jest.Mock`.")
|
||||
|
|
@ -51,17 +51,17 @@ declare_oxc_lint!(
|
|||
/// ```
|
||||
PreferJestMocked,
|
||||
style,
|
||||
fix
|
||||
conditional_fix
|
||||
);
|
||||
|
||||
impl Rule for PreferJestMocked {
|
||||
fn run(&self, node: &AstNode, ctx: &LintContext) {
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
if let AstKind::TSAsExpression(ts_expr) = node.kind() {
|
||||
if !matches!(ctx.nodes().parent_kind(node.id()), Some(AstKind::TSAsExpression(_))) {
|
||||
Self::check_ts_as_expression(ts_expr, ctx);
|
||||
Self::check_ts_as_expression(node, ts_expr, ctx);
|
||||
}
|
||||
} else if let AstKind::TSTypeAssertion(assert_type) = node.kind() {
|
||||
Self::check_assert_type(assert_type, ctx);
|
||||
Self::check_assert_type(node, assert_type, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -74,23 +74,37 @@ const MOCK_TYPES: Set<&'static str> = phf_set! {
|
|||
};
|
||||
|
||||
impl PreferJestMocked {
|
||||
fn check_ts_as_expression(as_expr: &TSAsExpression, ctx: &LintContext) {
|
||||
fn check_ts_as_expression<'a>(
|
||||
node: &AstNode<'a>,
|
||||
as_expr: &TSAsExpression,
|
||||
ctx: &LintContext<'a>,
|
||||
) {
|
||||
let TSType::TSTypeReference(ts_reference) = &as_expr.type_annotation else {
|
||||
return;
|
||||
};
|
||||
let arg_span = as_expr.expression.get_inner_expression().span();
|
||||
Self::check(ts_reference, arg_span, as_expr.span, ctx);
|
||||
Self::check(node, ts_reference, arg_span, as_expr.span, ctx);
|
||||
}
|
||||
|
||||
fn check_assert_type(assert_type: &TSTypeAssertion, ctx: &LintContext) {
|
||||
fn check_assert_type<'a>(
|
||||
node: &AstNode<'a>,
|
||||
assert_type: &TSTypeAssertion,
|
||||
ctx: &LintContext<'a>,
|
||||
) {
|
||||
let TSType::TSTypeReference(ts_reference) = &assert_type.type_annotation else {
|
||||
return;
|
||||
};
|
||||
let arg_span = assert_type.expression.get_inner_expression().span();
|
||||
Self::check(ts_reference, arg_span, assert_type.span, ctx);
|
||||
Self::check(node, ts_reference, arg_span, assert_type.span, ctx);
|
||||
}
|
||||
|
||||
fn check(ts_reference: &TSTypeReference, arg_span: Span, span: Span, ctx: &LintContext) {
|
||||
fn check<'a>(
|
||||
node: &AstNode<'a>,
|
||||
ts_reference: &TSTypeReference,
|
||||
arg_span: Span,
|
||||
span: Span,
|
||||
ctx: &LintContext<'a>,
|
||||
) {
|
||||
let TSTypeName::QualifiedName(qualified_name) = &ts_reference.type_name else {
|
||||
return;
|
||||
};
|
||||
|
|
@ -104,13 +118,22 @@ impl PreferJestMocked {
|
|||
return;
|
||||
}
|
||||
|
||||
ctx.diagnostic_with_fix(use_jest_mocked(span), |fixer| {
|
||||
let span_source_code = fixer.source_range(arg_span);
|
||||
fixer.replace(span, format!("jest.mocked({span_source_code})"))
|
||||
});
|
||||
if can_fix(node, ctx) {
|
||||
ctx.diagnostic_with_fix(use_jest_mocked(span), |fixer| {
|
||||
let span_source_code = fixer.source_range(arg_span);
|
||||
fixer.replace(span, format!("jest.mocked({span_source_code})"))
|
||||
});
|
||||
} else {
|
||||
ctx.diagnostic(use_jest_mocked(span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn can_fix<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bool {
|
||||
outermost_paren_parent(node, ctx)
|
||||
.map_or(false, |parent| !matches!(parent.kind(), AstKind::SimpleAssignmentTarget(_)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
|
|
@ -320,6 +343,9 @@ fn test() {
|
|||
).mockReturnValue(1);
|
||||
",
|
||||
),
|
||||
// we can't fix this case, as fixing it would result in a syntax error
|
||||
// (we'd be attempting attempting to assign `jest.fn()` to invalid left-hand side)
|
||||
("(foo as jest.Mock) = jest.fn();", "(foo as jest.Mock) = jest.fn();"),
|
||||
];
|
||||
|
||||
Tester::new(PreferJestMocked::NAME, pass, fail)
|
||||
|
|
|
|||
Loading…
Reference in a new issue