feat(linter) eslint plugin unicorn: no this assignment (#1250)

This commit is contained in:
Cameron 2023-11-13 00:49:05 +00:00 committed by GitHub
parent 8496156bb7
commit 11b43fa528
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 182 additions and 0 deletions

View file

@ -156,6 +156,7 @@ mod unicorn {
pub mod no_object_as_default_parameter;
pub mod no_static_only_class;
pub mod no_thenable;
pub mod no_this_assignment;
pub mod no_unnecessary_await;
pub mod no_useless_fallback_in_spread;
pub mod prefer_add_event_listener;
@ -298,6 +299,7 @@ oxc_macros::declare_all_lint_rules! {
unicorn::no_object_as_default_parameter,
unicorn::no_static_only_class,
unicorn::no_thenable,
unicorn::no_this_assignment,
unicorn::no_unnecessary_await,
unicorn::no_useless_fallback_in_spread,
unicorn::prefer_add_event_listener,

View file

@ -0,0 +1,147 @@
use oxc_ast::{
ast::{AssignmentTarget, BindingPatternKind, Expression, SimpleAssignmentTarget},
AstKind,
};
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::{self, Error},
};
use oxc_macros::declare_oxc_lint;
use oxc_span::{Atom, Span};
use crate::{context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Error, Diagnostic)]
#[error("eslint-plugin-unicorn(no-this-assignment): Do not assign `this` to `{1}`")]
#[diagnostic(
severity(warning),
help("Reference `this` directly instead of assigning it to a variable.")
)]
struct NoThisAssignmentDiagnostic(#[label] pub Span, Atom);
#[derive(Debug, Default, Clone)]
pub struct NoThisAssignment;
declare_oxc_lint!(
/// ### What it does
///
/// Disallow assigning `this` to a variable.
///
/// ### Why is this bad?
///
/// Assigning `this` to a variable is unnecessary and confusing.
///
/// ### Example
/// ```javascript
/// // fail
/// const foo = this;
/// class Bar {
/// method() {
/// foo.baz();
/// }
/// }
///
/// new Bar().method();
///
/// // pass
/// class Bar {
/// constructor(fooInstance) {
/// this.fooInstance = fooInstance;
/// }
/// method() {
/// this.fooInstance.baz();
/// }
/// }
///
/// new Bar(this).method();
/// ```
NoThisAssignment,
pedantic
);
impl Rule for NoThisAssignment {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
match node.kind() {
AstKind::VariableDeclarator(variable_decl) => {
let Some(init) = &variable_decl.init else {
return;
};
if !matches!(init.without_parenthesized(), Expression::ThisExpression(_)) {
return;
}
let BindingPatternKind::BindingIdentifier(binding_ident) = &variable_decl.id.kind
else {
return;
};
ctx.diagnostic(NoThisAssignmentDiagnostic(
variable_decl.span,
binding_ident.name.clone(),
));
}
AstKind::AssignmentExpression(assignment_expr) => {
if !matches!(
assignment_expr.right.without_parenthesized(),
Expression::ThisExpression(_)
) {
return;
}
let AssignmentTarget::SimpleAssignmentTarget(simple_assignment_target) =
&assignment_expr.left
else {
return;
};
let SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) =
simple_assignment_target
else {
return;
};
ctx.diagnostic(NoThisAssignmentDiagnostic(
assignment_expr.span,
ident.name.clone(),
));
}
_ => {}
}
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
r#"const {property} = this;"#,
r#"const property = this.property;"#,
r#"const [element] = this;"#,
r#"const element = this[0];"#,
r#"([element] = this);"#,
r#"element = this[0];"#,
r#"property = this.property;"#,
r#"const [element] = [this];"#,
r#"([element] = [this]);"#,
r#"const {property} = {property: this};"#,
r#"({property} = {property: this});"#,
r#"const self = true && this;"#,
r#"const self = false || this;"#,
r#"const self = false ?? this;"#,
r#"foo.bar = this;"#,
r#"function foo(a = this) {}"#,
r#"function foo({a = this}) {}"#,
r#"function foo([a = this]) {}"#,
];
let fail = vec![
r#"const foo = this;"#,
r#"let foo;foo = this;"#,
r#"var foo = bar, baz = this;"#,
r#"var foo = (bar), baz = (this);"#,
];
Tester::new_without_config(NoThisAssignment::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,33 @@
---
source: crates/oxc_linter/src/tester.rs
expression: no_this_assignment
---
⚠ eslint-plugin-unicorn(no-this-assignment): Do not assign `this` to `foo`
╭─[no_this_assignment.tsx:1:1]
1 │ const foo = this;
· ──────────
╰────
help: Reference `this` directly instead of assigning it to a variable.
⚠ eslint-plugin-unicorn(no-this-assignment): Do not assign `this` to `foo`
╭─[no_this_assignment.tsx:1:1]
1 │ let foo;foo = this;
· ──────────
╰────
help: Reference `this` directly instead of assigning it to a variable.
⚠ eslint-plugin-unicorn(no-this-assignment): Do not assign `this` to `baz`
╭─[no_this_assignment.tsx:1:1]
1 │ var foo = bar, baz = this;
· ──────────
╰────
help: Reference `this` directly instead of assigning it to a variable.
⚠ eslint-plugin-unicorn(no-this-assignment): Do not assign `this` to `baz`
╭─[no_this_assignment.tsx:1:1]
1 │ var foo = (bar), baz = (this);
· ────────────
╰────
help: Reference `this` directly instead of assigning it to a variable.