From 4bf329e1cf13f895e7b81a9ba994b1925d2911a8 Mon Sep 17 00:00:00 2001 From: Devin-Yeung <53309384+Devin-Yeung@users.noreply.github.com> Date: Sat, 16 Sep 2023 18:10:49 +0800 Subject: [PATCH] feat(linter): implement eslint-plugin-unicorn/no-thenable rule (#910) related to #684 --- crates/oxc_linter/src/rules.rs | 2 + .../src/rules/unicorn/no_thenable.rs | 452 +++++++++++++ .../oxc_linter/src/snapshots/no_thenable.snap | 614 ++++++++++++++++++ 3 files changed, 1068 insertions(+) create mode 100644 crates/oxc_linter/src/rules/unicorn/no_thenable.rs create mode 100644 crates/oxc_linter/src/snapshots/no_thenable.snap diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 18b6b0d3f..72a19492e 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -116,6 +116,7 @@ mod jest { mod unicorn { pub mod no_instanceof_array; + pub mod no_thenable; pub mod no_unnecessary_await; } @@ -211,6 +212,7 @@ oxc_macros::declare_all_lint_rules! { jest::no_jasmine_globals, unicorn::no_instanceof_array, unicorn::no_unnecessary_await, + unicorn::no_thenable, import::named, import::no_cycle, import::no_self_import, diff --git a/crates/oxc_linter/src/rules/unicorn/no_thenable.rs b/crates/oxc_linter/src/rules/unicorn/no_thenable.rs new file mode 100644 index 000000000..11d43faa5 --- /dev/null +++ b/crates/oxc_linter/src/rules/unicorn/no_thenable.rs @@ -0,0 +1,452 @@ +use oxc_ast::{ + ast::{ + Argument, ArrayExpressionElement, AssignmentExpression, AssignmentTarget, + BindingPatternKind, CallExpression, Declaration, Expression, MemberExpression, + ModuleDeclaration, ModuleExportName, ObjectPropertyKind, PropertyKey, + SimpleAssignmentTarget, VariableDeclarator, + }, + AstKind, +}; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{context::LintContext, rule::Rule, AstNode}; + +#[derive(Debug, Error, Diagnostic)] +enum NoThenableDiagnostic { + #[error("Do not add `then` to an object.")] + #[diagnostic(severity(warning), help("consider to remove `then`"))] + Object(#[label] Span), + #[error("Do not export `then`.")] + #[diagnostic(severity(warning), help("consider to remove `then`"))] + Export(#[label] Span), + #[error("Do not add `then` to a class.")] + #[diagnostic(severity(warning), help("consider to remove `then`"))] + Class(#[label] Span), +} + +#[derive(Debug, Default, Clone)] +pub struct NoThenable; + +declare_oxc_lint!( + /// ### What it does + /// disallow `then` property + /// + /// ### Why is this bad? + /// If an object is defined as "thenable", once it's accidentally + /// used in an await expression, it may cause problems: + /// + /// + /// ### Example + /// ```javascript + /// const foo = { + /// unicorn: 1, + /// then() {}, + /// }; + /// + /// const {unicorn} = await foo; + /// + /// console.log('after'); //<- This will never execute + /// ``` + NoThenable, + correctness +); + +impl Rule for NoThenable { + #[allow(clippy::too_many_lines)] + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + match node.kind() { + AstKind::ObjectExpression(expr) => { + expr.properties.iter().for_each(|prop| { + if let ObjectPropertyKind::ObjectProperty(prop) = prop { + if contains_then(&prop.key, ctx) { + ctx.diagnostic(NoThenableDiagnostic::Object(prop.span)); + } + } + }); + } + AstKind::PropertyDefinition(def) => { + if contains_then(&def.key, ctx) { + ctx.diagnostic(NoThenableDiagnostic::Class(def.span)); + } + } + AstKind::MethodDefinition(def) => { + if contains_then(&def.key, ctx) { + ctx.diagnostic(NoThenableDiagnostic::Class(def.span)); + } + } + AstKind::ModuleDeclaration(ModuleDeclaration::ExportNamedDeclaration(decl)) => { + // check declaration + if let Some(ref decl) = decl.declaration { + match decl { + Declaration::VariableDeclaration(decl) => { + for decl in &decl.declarations { + check_binding_pattern(&decl.id.kind, ctx); + } + } + Declaration::FunctionDeclaration(decl) => { + if let Some(bind) = decl.id.as_ref() { + if bind.name == "then" { + ctx.diagnostic(NoThenableDiagnostic::Export(bind.span)); + } + }; + } + Declaration::ClassDeclaration(decl) => { + if let Some(bind) = decl.id.as_ref() { + if bind.name == "then" { + ctx.diagnostic(NoThenableDiagnostic::Export(bind.span)); + } + }; + } + _ => {} + } + } + // check specifier + for spec in &decl.specifiers { + match spec.exported { + ModuleExportName::Identifier(ref ident) => { + if ident.name == "then" { + ctx.diagnostic(NoThenableDiagnostic::Export(ident.span)); + } + } + ModuleExportName::StringLiteral(ref lit) => { + if lit.value == "then" { + ctx.diagnostic(NoThenableDiagnostic::Export(lit.span)); + } + } + } + } + } + AstKind::CallExpression(expr) => check_call_expression(expr, ctx), + // foo.then = ... + AstKind::AssignmentExpression(AssignmentExpression { + left: + AssignmentTarget::SimpleAssignmentTarget( + SimpleAssignmentTarget::MemberAssignmentTarget(target), + ), + .. + }) => match &target.0 { + MemberExpression::ComputedMemberExpression(expr) => { + if let Some(span) = check_expression(&expr.expression, ctx) { + ctx.diagnostic(NoThenableDiagnostic::Class(span)); + } + } + MemberExpression::StaticMemberExpression(expr) => { + if expr.property.name == "then" { + ctx.diagnostic(NoThenableDiagnostic::Class(expr.span)); + } + } + MemberExpression::PrivateFieldExpression(_) => {} + }, + _ => {} + } + } +} + +fn check_call_expression(expr: &CallExpression, ctx: &LintContext) { + // `Object.defineProperty(foo, 'then', …)` + // `Reflect.defineProperty(foo, 'then', …)` + if !{ + !expr.optional + && expr.arguments.len() >= 3 + && !matches!(expr.arguments[0], Argument::SpreadElement(_)) + && match expr.callee { + Expression::MemberExpression(ref me) => { + me.object().get_identifier_reference().map_or(false, |ident_ref| { + ident_ref.name == "Reflect" || ident_ref.name == "Object" + }) && me.static_property_name() == Some("defineProperty") + && !me.optional() + } + _ => false, + } + } { + } else if let Argument::Expression(inner) = &expr.arguments[1] { + if let Some(span) = check_expression(inner, ctx) { + ctx.diagnostic(NoThenableDiagnostic::Object(span)); + } + } + + // `Object.fromEntries([['then', …]])` + if !{ + !expr.optional + && expr.arguments.len() == 1 + && matches!(expr.arguments[0], Argument::Expression(Expression::ArrayExpression(_))) + && match expr.callee { + Expression::MemberExpression(ref me) => { + me.object() + .get_identifier_reference() + .map_or(false, |ident_ref| ident_ref.name == "Object") + && me.static_property_name() == Some("fromEntries") + && !me.optional() + } + _ => false, + } + } { + } else if let Argument::Expression(Expression::ArrayExpression(outer)) = &expr.arguments[0] { + for inner in &outer.elements { + // inner item is array + if let ArrayExpressionElement::Expression(Expression::ArrayExpression(inner)) = inner { + if inner.elements.len() > 0 + && !matches!(inner.elements[0], ArrayExpressionElement::SpreadElement(_)) + { + if let ArrayExpressionElement::Expression(ref expr) = inner.elements[0] { + if let Some(span) = check_expression(expr, ctx) { + ctx.diagnostic(NoThenableDiagnostic::Object(span)); + } + } + } + } + } + } +} + +fn check_binding_pattern(pat: &BindingPatternKind, ctx: &LintContext) { + match pat { + BindingPatternKind::BindingIdentifier(bind) => { + if bind.name == "then" { + ctx.diagnostic(NoThenableDiagnostic::Export(bind.span)); + } + } + BindingPatternKind::ObjectPattern(obj) => { + for prop in &obj.properties { + check_binding_pattern(&prop.value.kind, ctx); + } + if let Some(elem) = obj.rest.as_ref() { + check_binding_pattern(&elem.argument.kind, ctx); + } + } + BindingPatternKind::ArrayPattern(arr) => { + for pat in &arr.elements { + if let Some(pat) = pat.as_ref() { + check_binding_pattern(&pat.kind, ctx); + } + } + if let Some(elem) = arr.rest.as_ref() { + check_binding_pattern(&elem.argument.kind, ctx); + } + } + BindingPatternKind::AssignmentPattern(assign) => { + check_binding_pattern(&assign.left.kind, ctx); + } + } +} + +fn check_expression(expr: &Expression, ctx: &LintContext<'_>) -> Option { + match expr { + Expression::StringLiteral(lit) => { + if lit.value == "then" { + Some(lit.span) + } else { + None + } + } + Expression::TemplateLiteral(lit) => { + lit.quasi().and_then(|quasi| if quasi == &"then" { Some(lit.span) } else { None }) + } + Expression::Identifier(ident) => { + let tab = ctx.semantic().symbols(); + ident.reference_id.get().and_then(|ref_id| { + tab.get_reference(ref_id).symbol_id().and_then(|symbol_id| { + let decl = ctx.semantic().nodes().get_node(tab.get_declaration(symbol_id)); + if let AstKind::VariableDeclarator(VariableDeclarator { + init: Some(Expression::StringLiteral(ref lit)), + .. + }) = decl.kind() + { + if lit.value == "then" { + Some(lit.span) + } else { + None + } + } else { + None + } + }) + }) + } + _ => None, + } +} + +fn contains_then(key: &PropertyKey, ctx: &LintContext) -> bool { + match key { + PropertyKey::Identifier(ident) => ident.name == "then", + PropertyKey::Expression(expr) => check_expression(expr, ctx).is_some(), + PropertyKey::PrivateIdentifier(_) => false, + } +} + +#[test] +#[allow(clippy::too_many_lines)] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + ("const then = {}", None), + ("const notThen = then", None), + ("const then = then.then", None), + ("const foo = {notThen: 1}", None), + ("const foo = {notThen() {}}", None), + ("const foo = {[then]: 1}", None), + ("const NOT_THEN = \"no-then\";const foo = {[NOT_THEN]: 1}", None), + ("function foo({then}) {}", None), + ("class then {}", None), + ("class Foo {notThen}", None), + ("class Foo {notThen() {}}", None), + ("class Foo {[then]}", None), + ("class Foo {#then}", None), + ("class Foo {#then() {}}", None), + ("class Foo {[then]() {}}", None), + ("class Foo {get notThen() {}}", None), + ("class Foo {get #then() {}}", None), + ("class Foo {get [then]() {}}", None), + ("class Foo {static notThen}", None), + ("class Foo {static notThen() {}}", None), + ("class Foo {static #then}", None), + ("class Foo {static #then() {}}", None), + ("class Foo {static [then]}", None), + ("class Foo {static [then]() {}}", None), + ("class Foo {static get notThen() {}}", None), + ("class Foo {static get #then() {}}", None), + ("class Foo {static get [then]() {}}", None), + ("class Foo {notThen = then}", None), + ("foo[then] = 1", None), + ("foo.notThen = 1", None), + ("then.notThen = then.then", None), + ("const NOT_THEN = \"no-then\";foo[NOT_THEN] = 1", None), + ("foo.then ++", None), + ("++ foo.then", None), + ("delete foo.then", None), + ("typeof foo.then", None), + ("foo.then != 1", None), + ("Object.fromEntries([then, 1])", None), + ("Object.fromEntries([,,])", None), + ("Object.fromEntries([[,,],[]])", None), + ("const NOT_THEN = \"not-then\";Object.fromEntries([[NOT_THEN, 1]])", None), + ("Object.fromEntries([[[\"then\", 1]]])", None), + ("NotObject.fromEntries([[\"then\", 1]])", None), + ("Object.notFromEntries([[\"then\", 1]])", None), + ("Object.fromEntries?.([[\"then\", 1]])", None), + ("Object?.fromEntries([[\"then\", 1]])", None), + ("Object.fromEntries([[...\"then\", 1]])", None), + ("Object.fromEntries([[\"then\", 1]], extraArgument)", None), + ("Object.fromEntries(...[[\"then\", 1]])", None), + ("Object.defineProperty(foo, then, 1)", None), + ("Object.defineProperty(foo, \"not-then\", 1)", None), + ("const then = \"no-then\";Object.defineProperty(foo, then, 1)", None), + ("Reflect.defineProperty(foo, then, 1)", None), + ("Reflect.defineProperty(foo, \"not-then\", 1)", None), + ("const then = \"no-then\";Reflect.defineProperty(foo, then, 1)", None), + ("Object.defineProperty(foo, \"then\", )", None), + ("Object.defineProperty(...foo, \"then\", 1)", None), + ("Object.defineProperty(foo, ...[\"then\", 1])", None), + ("export {default} from \"then\"", None), + ("const then = 1; export {then as notThen}", None), + ("export default then", None), + ("export function notThen(){}", None), + ("export class notThen {}", None), + ("export default function then (){}", None), + ("export default class then {}", None), + ("export default function (){}", None), + ("export default class {}", None), + ("export const notThen = 1", None), + ("export const {then: notThen} = 1", None), + ("export const {then: notThen = then} = 1", None), + ]; + + let fail = vec![ + ("const foo = {then: 1}", None), + ("const foo = {[\"then\"]: 1}", None), + ("const foo = {[`then`]: 1}", None), + ("const THEN = \"then\";const foo = {[THEN]: 1}", None), + ("const foo = {then() {}}", None), + ("const foo = {[\"then\"]() {}}", None), + ("const foo = {[`then`]() {}}", None), + ("const THEN = \"then\";const foo = {[THEN]() {}}", None), + ("const foo = {get then() {}}", None), + ("const foo = {get [\"then\"]() {}}", None), + ("const foo = {get [`then`]() {}}", None), + ("const THEN = \"then\";const foo = {get [THEN]() {}}", None), + ("class Foo {then}", None), + ("const Foo = class {then}", None), + ("class Foo {[\"then\"]}", None), + ("class Foo {[`then`]}", None), + ("const THEN = \"then\";class Foo {[THEN]}", None), + ("class Foo {then() {}}", None), + ("class Foo {[\"then\"]() {}}", None), + ("class Foo {[`then`]() {}}", None), + ("const THEN = \"then\";class Foo {[THEN]() {}}", None), + ("class Foo {static then}", None), + ("class Foo {static [\"then\"]}", None), + ("class Foo {static [`then`]}", None), + ("const THEN = \"then\";class Foo {static [THEN]}", None), + ("class Foo {static then() {}}", None), + ("class Foo {static [\"then\"]() {}}", None), + ("class Foo {static [`then`]() {}}", None), + ("const THEN = \"then\";class Foo {static [THEN]() {}}", None), + ("class Foo {get then() {}}", None), + ("class Foo {get [\"then\"]() {}}", None), + ("class Foo {get [`then`]() {}}", None), + ("const THEN = \"then\";class Foo {get [THEN]() {}}", None), + ("class Foo {set then(v) {}}", None), + ("class Foo {set [\"then\"](v) {}}", None), + ("class Foo {set [`then`](v) {}}", None), + ("const THEN = \"then\";class Foo {set [THEN](v) {}}", None), + ("class Foo {static get then() {}}", None), + ("class Foo {static get [\"then\"]() {}}", None), + ("class Foo {static get [`then`]() {}}", None), + ("const THEN = \"then\";class Foo {static get [THEN]() {}}", None), + ("foo.then = 1", None), + ("foo[\"then\"] = 1", None), + ("foo[`then`] = 1", None), + ("const THEN = \"then\";foo[THEN] = 1", None), + ("foo.then += 1", None), + ("foo.then ||= 1", None), + ("foo.then ??= 1", None), + ("Object.defineProperty(foo, \"then\", 1)", None), + ("Object.defineProperty(foo, `then`, 1)", None), + ("const THEN = \"then\";Object.defineProperty(foo, THEN, 1)", None), + ("Reflect.defineProperty(foo, \"then\", 1)", None), + ("Reflect.defineProperty(foo, `then`, 1)", None), + ("const THEN = \"then\";Reflect.defineProperty(foo, THEN, 1)", None), + ("Object.fromEntries([[\"then\", 1]])", None), + ("Object.fromEntries([[\"then\"]])", None), + ("Object.fromEntries([[`then`, 1]])", None), + ("const THEN = \"then\";Object.fromEntries([[THEN, 1]])", None), + ("Object.fromEntries([foo, [\"then\", 1]])", None), + ("const then = 1; export {then}", None), + ("const notThen = 1; export {notThen as then}", None), + ("export {then} from \"foo\"", None), + ("export function then() {}", None), + ("export async function then() {}", None), + ("export function * then() {}", None), + ("export async function * then() {}", None), + ("export class then {}", None), + ("export const then = 1", None), + ("export let then = 1", None), + ("export var then = 1", None), + ("export const [then] = 1", None), + ("export let [then] = 1", None), + ("export var [then] = 1", None), + ("export const [, then] = 1", None), + ("export let [, then] = 1", None), + ("export var [, then] = 1", None), + ("export const [, ...then] = 1", None), + ("export let [, ...then] = 1", None), + ("export var [, ...then] = 1", None), + ("export const {then} = 1", None), + ("export let {then} = 1", None), + ("export var {then} = 1", None), + ("export const {foo, ...then} = 1", None), + ("export let {foo, ...then} = 1", None), + ("export var {foo, ...then} = 1", None), + ("export const {foo: {bar: [{baz: then}]}} = 1", None), + ("export const notThen = 1, then = 1", None), + ]; + + Tester::new(NoThenable::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/no_thenable.snap b/crates/oxc_linter/src/snapshots/no_thenable.snap new file mode 100644 index 000000000..7afe6e7fa --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_thenable.snap @@ -0,0 +1,614 @@ +--- +source: crates/oxc_linter/src/tester.rs +expression: no_thenable +--- + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const foo = {then: 1} + · ─────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const foo = {["then"]: 1} + · ─────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const foo = {[`then`]: 1} + · ─────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";const foo = {[THEN]: 1} + · ───────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const foo = {then() {}} + · ───────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const foo = {["then"]() {}} + · ───────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const foo = {[`then`]() {}} + · ───────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";const foo = {[THEN]() {}} + · ─────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const foo = {get then() {}} + · ───────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const foo = {get ["then"]() {}} + · ───────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const foo = {get [`then`]() {}} + · ───────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";const foo = {get [THEN]() {}} + · ─────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {then} + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ const Foo = class {then} + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {["then"]} + · ──────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {[`then`]} + · ──────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";class Foo {[THEN]} + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {then() {}} + · ───────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {["then"]() {}} + · ───────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {[`then`]() {}} + · ───────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";class Foo {[THEN]() {}} + · ─────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {static then} + · ─────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {static ["then"]} + · ─────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {static [`then`]} + · ─────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";class Foo {static [THEN]} + · ───────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {static then() {}} + · ──────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {static ["then"]() {}} + · ──────────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {static [`then`]() {}} + · ──────────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";class Foo {static [THEN]() {}} + · ────────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {get then() {}} + · ───────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {get ["then"]() {}} + · ───────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {get [`then`]() {}} + · ───────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";class Foo {get [THEN]() {}} + · ─────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {set then(v) {}} + · ────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {set ["then"](v) {}} + · ────────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {set [`then`](v) {}} + · ────────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";class Foo {set [THEN](v) {}} + · ──────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {static get then() {}} + · ──────────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {static get ["then"]() {}} + · ──────────────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ class Foo {static get [`then`]() {}} + · ──────────────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";class Foo {static get [THEN]() {}} + · ────────────────────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ foo.then = 1 + · ──────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ foo["then"] = 1 + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ foo[`then`] = 1 + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";foo[THEN] = 1 + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ foo.then += 1 + · ──────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ foo.then ||= 1 + · ──────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to a class. + ╭─[no_thenable.tsx:1:1] + 1 │ foo.then ??= 1 + · ──────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ Object.defineProperty(foo, "then", 1) + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ Object.defineProperty(foo, `then`, 1) + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";Object.defineProperty(foo, THEN, 1) + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ Reflect.defineProperty(foo, "then", 1) + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ Reflect.defineProperty(foo, `then`, 1) + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";Reflect.defineProperty(foo, THEN, 1) + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ Object.fromEntries([["then", 1]]) + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ Object.fromEntries([["then"]]) + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ Object.fromEntries([[`then`, 1]]) + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ const THEN = "then";Object.fromEntries([[THEN, 1]]) + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not add `then` to an object. + ╭─[no_thenable.tsx:1:1] + 1 │ Object.fromEntries([foo, ["then", 1]]) + · ────── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ const then = 1; export {then} + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ const notThen = 1; export {notThen as then} + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export {then} from "foo" + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export function then() {} + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export async function then() {} + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export function * then() {} + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export async function * then() {} + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export class then {} + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export const then = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export let then = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export var then = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export const [then] = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export let [then] = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export var [then] = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export const [, then] = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export let [, then] = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export var [, then] = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export const [, ...then] = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export let [, ...then] = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export var [, ...then] = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export const {then} = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export let {then} = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export var {then} = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export const {foo, ...then} = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export let {foo, ...then} = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export var {foo, ...then} = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export const {foo: {bar: [{baz: then}]}} = 1 + · ──── + ╰──── + help: consider to remove `then` + + ⚠ Do not export `then`. + ╭─[no_thenable.tsx:1:1] + 1 │ export const notThen = 1, then = 1 + · ──── + ╰──── + help: consider to remove `then` + +