Skip to content

Commit

Permalink
Merge branch 'main' into tyler/jsx-a11y/no-noninteractive-tabindex
Browse files Browse the repository at this point in the history
  • Loading branch information
taearls committed Dec 28, 2024
2 parents 49dea7c + d8d2ec6 commit c427e26
Show file tree
Hide file tree
Showing 26 changed files with 500 additions and 31 deletions.
19 changes: 18 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ serde-wasm-bindgen = "0.6.5"
sha1 = "0.10.6"
simdutf8 = { version = "0.1.5", features = ["aarch64_neon"] }
similar = "2.6.0"
similar-asserts = "1.6.0"
string_wizard = "0.0.25"
tempfile = "3.14.0"
tokio = "1.42.0"
Expand Down
4 changes: 4 additions & 0 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ mod eslint {
pub mod no_loss_of_precision;
pub mod no_magic_numbers;
pub mod no_multi_str;
pub mod no_nested_ternary;
pub mod no_new;
pub mod no_new_func;
pub mod no_new_native_nonconstructor;
Expand Down Expand Up @@ -140,6 +141,7 @@ mod eslint {
pub mod prefer_exponentiation_operator;
pub mod prefer_numeric_literals;
pub mod prefer_object_has_own;
pub mod prefer_rest_params;
pub mod prefer_spread;
pub mod radix;
pub mod require_await;
Expand Down Expand Up @@ -536,6 +538,7 @@ oxc_macros::declare_all_lint_rules! {
eslint::max_classes_per_file,
eslint::max_lines,
eslint::max_params,
eslint::no_nested_ternary,
eslint::no_labels,
eslint::no_restricted_imports,
eslint::no_object_constructor,
Expand Down Expand Up @@ -629,6 +632,7 @@ oxc_macros::declare_all_lint_rules! {
eslint::no_var,
eslint::no_void,
eslint::no_with,
eslint::prefer_rest_params,
eslint::prefer_exponentiation_operator,
eslint::prefer_numeric_literals,
eslint::prefer_object_has_own,
Expand Down
100 changes: 100 additions & 0 deletions crates/oxc_linter/src/rules/eslint/no_nested_ternary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use oxc_ast::ast::Expression;
use oxc_ast::AstKind;
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;

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

fn no_nested_ternary_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Do not nest ternary expressions.").with_label(span)
}

#[derive(Debug, Default, Clone)]
pub struct NoNestedTernary;

declare_oxc_lint!(
/// ### What it does
///
/// Disallows nested ternary expressions to improve code readability and maintainability.
///
/// ### Why is this bad?
///
/// Nested ternary expressions make code harder to read and understand. They can lead to complex, difficult-to-debug logic.
///
/// ### Examples
///
/// Examples of **incorrect** code for this rule:
/// ```js
/// const result = condition1 ? (condition2 ? "a" : "b") : "c";
/// ```
///
/// Examples of **correct** code for this rule:
/// ```js
/// let result;
/// if (condition1) {
/// result = condition2 ? "a" : "b";
/// } else {
/// result = "c";
/// }
/// ```
NoNestedTernary,
style,
);

impl Rule for NoNestedTernary {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::ConditionalExpression(node) = node.kind() {
if matches!(
node.consequent.get_inner_expression(),
Expression::ConditionalExpression(_)
) || matches!(
node.alternate.get_inner_expression(),
Expression::ConditionalExpression(_)
) {
ctx.diagnostic(no_nested_ternary_diagnostic(node.span));
}
}
}
}

#[test]
fn test() {
use crate::tester::Tester;

let pass = vec![
"foo ? doBar() : doBaz();",
"var foo = bar === baz ? qux : quxx;",
"var result = foo && bar ? baz : qux || quux;",
"var result = foo ? bar : baz === qux;",
"foo ? doSomething(a, b) : doSomethingElse(c, d);",
// Parenthesized Expressions
"var result = (foo ? bar : baz) || qux;",
"var result = (foo ? bar : baz) && qux;",
"var result = foo === bar ? (baz || qux) : quux;",
"var result = (foo ? bar : baz) ? qux : quux;",
// TypeScript
"var result = foo! ? bar : baz;",
"var result = foo ? bar! : baz;",
"var result = (foo as boolean) ? bar : baz;",
"var result = foo ? (bar as string) : baz;",
];

let fail = vec![
"foo ? bar : baz === qux ? quxx : foobar;",
"foo ? baz === qux ? quxx : foobar : bar;",
// Parenthesized Expressions
"var result = foo ? (bar ? baz : qux) : quux;",
"var result = foo ? (bar === baz ? qux : quux) : foobar;",
"doSomething(foo ? bar : baz ? qux : quux);",
// Comment
"var result = foo /* comment */ ? bar : baz ? qux : quux;",
// TypeScript
"var result = foo! ? bar : baz! ? qux : quux;",
"var result = foo ? bar! : (baz! ? qux : quux);",
"var result = (foo as boolean) ? bar : (baz as string) ? qux : quux;",
"var result = foo ? (bar as string) : (baz as number ? qux : quux);",
];

Tester::new(NoNestedTernary::NAME, NoNestedTernary::CATEGORY, pass, fail).test_and_snapshot();
}
126 changes: 126 additions & 0 deletions crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use crate::{context::LintContext, rule::Rule, AstNode};
use oxc_ast::AstKind;
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};

fn prefer_rest_params_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Use the rest parameters instead of 'arguments'.").with_label(span)
}

#[derive(Debug, Default, Clone)]
pub struct PreferRestParams;

declare_oxc_lint!(
/// ### What it does
///
/// Disallows the use of the `arguments` object and instead enforces the use of rest parameters.
///
/// ### Why is this bad?
///
/// The `arguments` object does not have methods from `Array.prototype`, making it inconvenient for array-like operations.
/// Using rest parameters provides a more intuitive and efficient way to handle variadic arguments.
///
/// ### Examples
///
/// Examples of **incorrect** code for this rule:
/// ```javascript
/// function foo() {
/// console.log(arguments);
/// }
///
/// function foo(action) {
/// var args = Array.prototype.slice.call(arguments, 1);
/// action.apply(null, args);
/// }
///
/// function foo(action) {
/// var args = [].slice.call(arguments, 1);
/// action.apply(null, args);
/// }
/// ```
///
/// Examples of **correct** code for this rule:
/// ```javascript
/// function foo(...args) {
/// console.log(args);
/// }
///
/// function foo(action, ...args) {
/// action.apply(null, args); // Or use `action(...args)` (related to `prefer-spread` rule).
/// }
///
/// // Note: Implicit `arguments` can be shadowed.
/// function foo(arguments) {
/// console.log(arguments); // This refers to the first argument.
/// }
/// function foo() {
/// var arguments = 0;
/// console.log(arguments); // This is a local variable.
/// }
/// ```
PreferRestParams,
style,
);

impl Rule for PreferRestParams {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::IdentifierReference(identifier) = node.kind() {
if identifier.name != "arguments"
|| !is_inside_of_function(node, ctx)
|| is_not_normal_member_access(node, ctx)
{
return;
}
let binding = ctx.scopes().find_binding(node.scope_id(), "arguments");
if binding.is_none() {
ctx.diagnostic(prefer_rest_params_diagnostic(node.span()));
}
}
}
}

fn is_inside_of_function(node: &AstNode, ctx: &LintContext) -> bool {
let mut current = node;
while let Some(parent) = ctx.nodes().parent_node(current.id()) {
if matches!(parent.kind(), AstKind::Function(_)) {
return true;
}
current = parent;
}
false
}

fn is_not_normal_member_access(identifier: &AstNode, ctx: &LintContext) -> bool {
let parent = ctx.nodes().parent_node(identifier.id());
if let Some(parent) = parent {
if let AstKind::MemberExpression(member) = parent.kind() {
return member.object().span() == identifier.span() && !member.is_computed();
}
}
false
}

#[test]
fn test() {
use crate::tester::Tester;

let pass = vec![
"arguments;",
"function foo(arguments) { arguments; }",
"function foo() { var arguments; arguments; }",
"var foo = () => arguments;",
"function foo(...args) { args; }",
"function foo() { arguments.length; }",
"function foo() { arguments.callee; }",
];

let fail = vec![
"function foo() { arguments; }",
"function foo() { arguments[0]; }",
"function foo() { arguments[1]; }",
"function foo() { arguments[Symbol.iterator]; }",
];

Tester::new(PreferRestParams::NAME, PreferRestParams::CATEGORY, pass, fail).test_and_snapshot();
}
4 changes: 4 additions & 0 deletions crates/oxc_linter/src/rules/oxc/const_comparisons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,9 +415,11 @@ fn test() {
"status_code > 500 && foo() && bar || status_code < 400;",
// oxc specific
"a < b",
"a.b.c < b.b.c",
"a <= b",
"a > b",
"a >= b",
"class Foo { #a; #b; constructor() { this.#a = 1; }; test() { return this.#a > this.#b } }",
];

let fail = vec![
Expand Down Expand Up @@ -500,10 +502,12 @@ fn test() {
"a <= a",
"a > a",
"a >= a",
"a.b.c >= a.b.c",
"a == b && a == b",
"a == b || a == b",
"!foo && !foo",
"!foo || !foo",
"class Foo { #a; #b; constructor() { this.#a = 1; }; test() { return this.#a > this.#a } }",
];

Tester::new(ConstComparisons::NAME, ConstComparisons::CATEGORY, pass, fail).test_and_snapshot();
Expand Down
10 changes: 8 additions & 2 deletions crates/oxc_linter/src/rules/react/rules_of_hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
context::LintContext,
rule::Rule,
utils::{is_react_component_or_hook_name, is_react_function_call, is_react_hook},
AstNode,
AstNode, FrameworkFlags,
};

mod diagnostics {
Expand Down Expand Up @@ -99,7 +99,7 @@ pub struct RulesOfHooks;
declare_oxc_lint!(
/// ### What it does
///
/// This enforcecs the Rules of Hooks
/// This enforces the Rules of Hooks
///
/// <https://reactjs.org/docs/hooks-rules.html>
///
Expand All @@ -108,6 +108,12 @@ declare_oxc_lint!(
);

impl Rule for RulesOfHooks {
fn should_run(&self, ctx: &crate::rules::ContextHost) -> bool {
// disable this rule in vue/nuxt and svelte(kit) files
// top level useFunction are very common
!ctx.frameworks().contains(FrameworkFlags::SvelteKit | FrameworkFlags::Nuxt)
}

fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::CallExpression(call) = node.kind() else { return };

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ impl Rule for ConsistentGenericConstructors {
.unwrap_or_default(),
}))
}

fn should_run(&self, ctx: &crate::rules::ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

impl ConsistentGenericConstructors {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ impl Rule for NoEmptyObjectType {
_ => {}
}
}

fn should_run(&self, ctx: &crate::rules::ContextHost) -> bool {
ctx.source_type().is_typescript()
}
}

fn check_interface_declaration(
Expand Down
Loading

0 comments on commit c427e26

Please sign in to comment.