diff --git a/crates/oxc_linter/src/generated/rule_runner_impls.rs b/crates/oxc_linter/src/generated/rule_runner_impls.rs index 9c12119fafc7e..f335c875eb5c4 100644 --- a/crates/oxc_linter/src/generated/rule_runner_impls.rs +++ b/crates/oxc_linter/src/generated/rule_runner_impls.rs @@ -22,6 +22,10 @@ impl RuleRunner for crate::rules::eslint::block_scoped_var::BlockScopedVar { Some(&AstTypesBitset::from_types(&[AstType::VariableDeclaration])); } +impl RuleRunner for crate::rules::eslint::class_methods_use_this::ClassMethodsUseThis { + const NODE_TYPES: Option<&AstTypesBitset> = None; +} + impl RuleRunner for crate::rules::eslint::curly::Curly { const NODE_TYPES: Option<&AstTypesBitset> = None; } diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index e829278b1a511..5246fa424b55c 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -42,6 +42,7 @@ pub(crate) mod eslint { pub mod array_callback_return; pub mod arrow_body_style; pub mod block_scoped_var; + pub mod class_methods_use_this; pub mod curly; pub mod default_case; pub mod default_case_last; @@ -636,6 +637,7 @@ oxc_macros::declare_all_lint_rules! { eslint::array_callback_return, eslint::arrow_body_style, eslint::block_scoped_var, + eslint::class_methods_use_this, eslint::curly, eslint::default_case, eslint::default_case_last, diff --git a/crates/oxc_linter/src/rules/eslint/class_methods_use_this.rs b/crates/oxc_linter/src/rules/eslint/class_methods_use_this.rs new file mode 100644 index 0000000000000..596a46c5c3842 --- /dev/null +++ b/crates/oxc_linter/src/rules/eslint/class_methods_use_this.rs @@ -0,0 +1,651 @@ +use std::borrow::Cow; + +use itertools::Itertools; +use oxc_ast::{ + AstKind, + ast::{AccessorProperty, Expression, PropertyDefinition, TSAccessibility}, +}; +use oxc_ast_visit::Visit; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_semantic::AstNode; +use oxc_span::{CompactStr, GetSpan, Span}; + +use crate::{LintContext, rule::Rule}; + +fn class_methods_use_this_diagnostic(span: Span, name: Option>) -> OxcDiagnostic { + let method_name_str = name.map_or(String::new(), |name| format!(" `{name}`")); + OxcDiagnostic::warn(format!("Expected method{method_name_str} to have this.")) + .with_help(format!("Consider converting method{method_name_str} to a static method.")) + .with_label(span) +} + +#[derive(Debug, Clone)] +pub struct ClassMethodsUseThisConfig { + except_methods: Vec, + enforce_for_class_fields: bool, + ignore_override_methods: bool, + ignore_classes_with_implements: Option, +} + +impl Default for ClassMethodsUseThisConfig { + fn default() -> Self { + Self { + except_methods: Vec::new(), + enforce_for_class_fields: true, + ignore_override_methods: false, + ignore_classes_with_implements: None, + } + } +} + +#[derive(Debug, Clone, Default)] +pub struct ClassMethodsUseThis(Box); + +#[derive(Debug, Clone)] +struct MethodException { + name: CompactStr, + private: bool, +} + +#[derive(Debug, Clone)] +enum IgnoreClassWithImplements { + All, + PublicFields, +} + +declare_oxc_lint!( + /// ### What it does + /// + /// Enforce that class methods utilize this. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```js + /// class A { + /// foo() { + /// console.log("Hello World"); + /// } + /// } + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```js + /// class A { + /// foo() { + /// this.bar = "Hello World"; // OK, this is used + /// } + /// } + /// + /// class B { + /// constructor() { + /// // OK. constructor is exempt + /// } + /// } + /// + /// class C { + /// static foo() { + /// // OK. static methods aren't expected to use this. + /// } + /// } + /// ``` + ClassMethodsUseThis, + eslint, + restriction, +); + +impl Rule for ClassMethodsUseThis { + fn from_configuration(value: serde_json::Value) -> Self { + let obj = value.get(0); + Self(Box::new(ClassMethodsUseThisConfig { + except_methods: obj + .and_then(|o| o.get("exceptMethods")) + .and_then(|v| v.as_array()) + .map_or(Vec::new(), |a| { + a.iter() + .filter_map(|method| { + let method = method.as_str()?; + match method.strip_prefix("#") { + Some(method) => { + Some(MethodException { name: method.into(), private: true }) + } + None => { + Some(MethodException { name: method.into(), private: false }) + } + } + }) + .collect_vec() + }), + enforce_for_class_fields: obj + .and_then(|o| o.get("enforceForClassFields")) + .and_then(serde_json::Value::as_bool) + .unwrap_or(true), + ignore_override_methods: obj + .and_then(|o| o.get("ignoreOverrideMethods")) + .and_then(serde_json::Value::as_bool) + .unwrap_or(false), + ignore_classes_with_implements: obj + .and_then(|o| o.get("ignoreClassesWithImplements")) + .and_then(|v| v.as_str()) + .map(|s| match s { + "public-fields" => IgnoreClassWithImplements::PublicFields, + _ => IgnoreClassWithImplements::All, + }), + })) + } + + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + let config = &self.0; + let function_pair = match node.kind() { + AstKind::AccessorProperty(accessor) => { + if accessor.r#static + || !config.enforce_for_class_fields + || (config.ignore_override_methods && accessor.r#override) + || self.check_ignore_classes_with_implements( + node, + ctx, + accessor.accessibility, + accessor.key.is_private_identifier(), + ) + { + return; + } + accessor.value.as_ref().and_then(|value| match value { + Expression::ArrowFunctionExpression(arrow_function) => { + Some((&arrow_function.body, &accessor.key)) + } + Expression::FunctionExpression(function_expression) => { + Some((function_expression.body.as_ref()?, &accessor.key)) + } + _ => None, + }) + } + AstKind::MethodDefinition(method_definition) => { + if method_definition.r#static + || method_definition.kind.is_constructor() + || (config.ignore_override_methods && method_definition.r#override) + || self.check_ignore_classes_with_implements( + node, + ctx, + method_definition.accessibility, + method_definition.key.is_private_identifier(), + ) + { + return; + } + let Some(function_body) = method_definition.value.body.as_ref() else { return }; + Some((function_body, &method_definition.key)) + } + AstKind::PropertyDefinition(property_definition) => { + if property_definition.r#static + || !config.enforce_for_class_fields + || (config.ignore_override_methods && property_definition.r#override) + || self.check_ignore_classes_with_implements( + node, + ctx, + property_definition.accessibility, + property_definition.key.is_private_identifier(), + ) + { + return; + } + property_definition.value.as_ref().and_then(|value| match value { + Expression::ArrowFunctionExpression(arrow_function) => { + Some((&arrow_function.body, &property_definition.key)) + } + Expression::FunctionExpression(function_expression) => { + Some((function_expression.body.as_ref()?, &property_definition.key)) + } + _ => None, + }) + } + _ => None, + }; + let Some((function_body, name)) = function_pair else { return }; + if let Some(name_str) = name.name() { + if config.except_methods.iter().any(|method| { + method.name == name_str && method.private == name.is_private_identifier() + }) { + return; + } + } + let mut finder = ThisFinder::new(); + finder.visit_function_body(function_body); + if !finder.has_this { + ctx.diagnostic(class_methods_use_this_diagnostic(name.span(), name.name())); + } + } +} + +impl ClassMethodsUseThis { + fn check_ignore_classes_with_implements( + &self, + node: &AstNode<'_>, + ctx: &LintContext<'_>, + accessibility: Option, + is_private: bool, + ) -> bool { + let config = &self.0; + let Some(ignore_classes_with_implements) = &config.ignore_classes_with_implements else { + return false; + }; + let mut current_node = node; + loop { + current_node = ctx.nodes().parent_node(current_node.id()); + let AstKind::Class(class) = current_node.kind() else { + continue; + }; + if class.implements.is_empty() { + return false; + } + return match ignore_classes_with_implements { + IgnoreClassWithImplements::All => true, + IgnoreClassWithImplements::PublicFields => accessibility + .map_or(!is_private, |accessibility| accessibility == TSAccessibility::Public), + }; + } + } +} + +struct ThisFinder { + has_this: bool, +} + +impl ThisFinder { + fn new() -> Self { + Self { has_this: false } + } +} + +impl Visit<'_> for ThisFinder { + fn visit_this_expression(&mut self, _it: &oxc_ast::ast::ThisExpression) { + self.has_this = true; + } + + fn visit_super(&mut self, _it: &oxc_ast::ast::Super) { + self.has_this = true; + } + + fn visit_function( + &mut self, + _it: &oxc_ast::ast::Function<'_>, + _flags: oxc_semantic::ScopeFlags, + ) { + } + + fn visit_static_block(&mut self, _it: &oxc_ast::ast::StaticBlock<'_>) {} + + fn visit_property_definition(&mut self, it: &PropertyDefinition<'_>) { + self.visit_property_key(&it.key); + } + + fn visit_accessor_property(&mut self, _it: &AccessorProperty<'_>) {} +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + ("class A { constructor() {} }", None, None), + ("class A { foo() {this} }", None, None), + ("class A { foo() {this.bar = 'bar';} }", None, None), + ("class A { foo() {bar(this);} }", None, None), + ("class A extends B { foo() {super.foo();} }", None, None), + ("class A { foo() { if(true) { return this; } } }", None, None), + ("class A { static foo() {} }", None, None), + ("({ a(){} });", None, None), + ("class A { foo() { () => this; } }", None, None), + ("({ a: function () {} });", None, None), + ("class A { foo = function() {this} }", None, None), + ("class A { foo = () => {this} }", None, None), + ("class A { foo = () => {super.toString} }", None, None), + ("class A { static foo = function() {} }", None, None), + ("class A { static foo = () => {} }", None, None), + ("class A { foo() { return class { [this.foo] = 1 }; } }", None, None), + ("class A { static {} }", None, None), + ("class A { accessor foo = function() {this} }", None, None), + ("class A { accessor foo = () => {this} }", None, None), + ("class A { accessor foo = 1; }", None, None), + ("class A { static accessor foo = function() {} }", None, None), + ("class A { static accessor foo = () => {} }", None, None), + ( + "class A { foo() {this} bar() {} }", + Some(serde_json::json!([{ "exceptMethods": ["bar"] }])), + None, + ), + ( + "class A { \"foo\"() { } }", + Some(serde_json::json!([{ "exceptMethods": ["foo"] }])), + None, + ), + ("class A { 42() { } }", Some(serde_json::json!([{ "exceptMethods": ["42"] }])), None), + ("class A { #bar() {} }", Some(serde_json::json!([{ "exceptMethods": ["#bar"] }])), None), + ( + "class A { foo = function () {} }", + Some(serde_json::json!([{ "enforceForClassFields": false }])), + None, + ), + ( + "class A { foo = () => {} }", + Some(serde_json::json!([{ "enforceForClassFields": false }])), + None, + ), + ( + "class Foo { override method() {} }", + Some(serde_json::json!([{ "ignoreOverrideMethods": true }])), + None, + ), + ( + "class Foo { private override method() {} }", + Some(serde_json::json!([{ "ignoreOverrideMethods": true }])), + None, + ), + ( + "class Foo { protected override method() {} }", + Some(serde_json::json!([{ "ignoreOverrideMethods": true }])), + None, + ), + ( + "class Foo { override accessor method = () => {} }", + Some(serde_json::json!([{ "ignoreOverrideMethods": true }])), + None, + ), + ( + "class Foo { override get getter(): number {} }", + Some(serde_json::json!([{ "ignoreOverrideMethods": true }])), + None, + ), + ( + "class Foo { private override get getter(): number {} }", + Some(serde_json::json!([{ "ignoreOverrideMethods": true }])), + None, + ), + ( + "class Foo { protected override get getter(): number {} }", + Some(serde_json::json!([{ "ignoreOverrideMethods": true }])), + None, + ), + ( + "class Foo { override set setter(v: number) {} }", + Some(serde_json::json!([{ "ignoreOverrideMethods": true }])), + None, + ), + ( + "class Foo { private override set setter(v: number) {} }", + Some(serde_json::json!([{ "ignoreOverrideMethods": true }])), + None, + ), + ( + "class Foo { protected override set setter(v: number) {} }", + Some(serde_json::json!([{ "ignoreOverrideMethods": true }])), + None, + ), + ( + "class Foo implements Bar { override method() {} }", + Some( + serde_json::json!([{ "ignoreOverrideMethods": true, "ignoreClassesWithImplements": "all" }]), + ), + None, + ), + ( + "class Foo implements Bar { private override method() {} }", + Some( + serde_json::json!([{ "ignoreOverrideMethods": true, "ignoreClassesWithImplements": "public-fields" }]), + ), + None, + ), + ( + "class Foo implements Bar { protected override method() {} }", + Some( + serde_json::json!([{ "ignoreOverrideMethods": true, "ignoreClassesWithImplements": "public-fields" }]), + ), + None, + ), + ( + "class Foo implements Bar { override get getter(): number {} }", + Some( + serde_json::json!([{ "ignoreOverrideMethods": true, "ignoreClassesWithImplements": "all" }]), + ), + None, + ), + ( + "class Foo implements Bar { private override get getter(): number {} }", + Some( + serde_json::json!([{ "ignoreOverrideMethods": true, "ignoreClassesWithImplements": "public-fields" }]), + ), + None, + ), + ( + "class Foo implements Bar { protected override get getter(): number {} }", + Some( + serde_json::json!([{ "ignoreOverrideMethods": true, "ignoreClassesWithImplements": "public-fields" }]), + ), + None, + ), + ( + "class Foo implements Bar { override set setter(v: number) {} }", + Some( + serde_json::json!([{ "ignoreOverrideMethods": true, "ignoreClassesWithImplements": "all" }]), + ), + None, + ), + ( + "class Foo implements Bar { private override set setter(v: number) {} }", + Some( + serde_json::json!([{ "ignoreOverrideMethods": true, "ignoreClassesWithImplements": "public-fields" }]), + ), + None, + ), + ( + "class Foo implements Bar { protected override set setter(v: number) {} }", + Some( + serde_json::json!([{ "ignoreOverrideMethods": true, "ignoreClassesWithImplements": "public-fields" }]), + ), + None, + ), + ( + "class Foo { override property = () => {} }", + Some(serde_json::json!([{ "ignoreOverrideMethods": true }])), + None, + ), + ( + "class Foo { private override property = () => {} }", + Some(serde_json::json!([{ "ignoreOverrideMethods": true }])), + None, + ), + ( + "class Foo { protected override property = () => {} }", + Some(serde_json::json!([{ "ignoreOverrideMethods": true }])), + None, + ), + ( + "class Foo implements Bar { override property = () => {} }", + Some( + serde_json::json!([{ "ignoreOverrideMethods": true, "ignoreClassesWithImplements": "all" }]), + ), + None, + ), + ( + "class Foo implements Bar { private override property = () => {} }", + Some( + serde_json::json!([{ "ignoreOverrideMethods": true, "ignoreClassesWithImplements": "public-fields" }]), + ), + None, + ), + ( + "class Foo implements Bar { protected override property = () => {} }", + Some( + serde_json::json!([{ "ignoreOverrideMethods": true, "ignoreClassesWithImplements": "public-fields" }]), + ), + None, + ), + ( + "class Foo implements Bar { method() {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "all" }])), + None, + ), + ( + "class Foo implements Bar { accessor method = () => {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "all" }])), + None, + ), + ( + "class Foo implements Bar { get getter() {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "all" }])), + None, + ), + ( + "class Foo implements Bar { set setter(value: string) {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "all" }])), + None, + ), + ( + "class Foo implements Bar { property = () => {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "all" }])), + None, + ), + ( + "class A { accessor foo = function () {} }", + Some(serde_json::json!([{ "enforceForClassFields": false }])), + None, + ), + ( + "class A { accessor foo = () => {} }", + Some(serde_json::json!([{ "enforceForClassFields": false }])), + None, + ), + ( + "class A { override foo = () => {} }", + Some(serde_json::json!([{ "enforceForClassFields": false }])), + None, + ), + ( + "class Foo implements Bar { property = () => {} }", + Some(serde_json::json!([{ "enforceForClassFields": false }])), + None, + ), + ]; + + let fail = vec![ + ("class A { foo() {} }", None, None), + ("class A { foo() {/**this**/} }", None, None), + ("class A { foo() {var a = function () {this};} }", None, None), + ("class A { foo() {var a = function () {var b = function(){this}};} }", None, None), + ("class A { foo() {window.this} }", None, None), + ("class A { foo() {that.this = 'this';} }", None, None), + ("class A { foo() { () => undefined; } }", None, None), + ( + "class A { foo(){} 'bar'(){} 123(){} [`baz`](){} [a](){} [f(a)](){} get quux(){} set[a](b){} *quuux(){} }", + None, + None, + ), + ("class A { foo = function() {} }", None, None), + ("class A { foo = () => {} }", None, None), + ("class A { #foo = function() {} }", None, None), + ("class A { #foo = () => {} }", None, None), + ("class A { #foo() {} }", None, None), + ("class A { get #foo() {} }", None, None), + ("class A { set #foo(x) {} }", None, None), + ("class A { foo () { return class { foo = this }; } }", None, None), + ("class A { foo () { return function () { foo = this }; } }", None, None), + ("class A { foo () { return class { static { this; } } } }", None, None), + ("class Foo { private method() {} }", None, None), + ("class Foo { protected method() {} }", None, None), + ("class Foo { accessor method = function () {} }", None, None), + ("class Foo { accessor method = () => {} }", None, None), + ("class Foo { private accessor method = () => {} }", None, None), + ("class Foo { protected accessor method = () => {} }", None, None), + ("class A { foo () { return class { accessor bar = this }; } }", None, None), + ("class Derived extends Base { override method() {} }", None, None), + ("class Derived extends Base { property = () => {} }", None, None), + ("class Derived extends Base { public property = () => {} }", None, None), + ("class Derived extends Base { override property = () => {} }", None, None), + ("class Foo { private get getter(): number {} }", None, None), + ("class Foo { protected get getter(): number {} }", None, None), + ("class Foo { private set setter(b: number) {} }", None, None), + ("class Foo { protected set setter(b: number) {} }", None, None), + ("function fn() { this.foo = 303; class Foo { method() {} } }", None, None), + ("class Foo implements Bar { override property = () => {}; }", None, None), + ( + "class A { foo() {} bar() {} }", + Some(serde_json::json!([{ "exceptMethods": ["bar"] }])), + None, + ), + ( + "class A { foo() {} hasOwnProperty() {} }", + Some(serde_json::json!([{ "exceptMethods": ["foo"] }])), + None, + ), + ("class A { [foo]() {} }", Some(serde_json::json!([{ "exceptMethods": ["foo"] }])), None), + ( + "class A { #foo() { } foo() {} #bar() {} }", + Some(serde_json::json!([{ "exceptMethods": ["#foo"] }])), + None, + ), + ( + "class Foo implements Bar { #method() {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "public-fields" }])), + None, + ), + ( + "class Foo implements Bar { private method() {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "public-fields" }])), + None, + ), + ( + "class Foo implements Bar { protected method() {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "public-fields" }])), + None, + ), + ( + "class Foo implements Bar { get #getter(): number {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "public-fields" }])), + None, + ), + ( + "class Foo implements Bar { private get getter(): number {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "public-fields" }])), + None, + ), + ( + "class Foo implements Bar { protected get getter(): number {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "public-fields" }])), + None, + ), + ( + "class Foo implements Bar { set #setter(v: number) {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "public-fields" }])), + None, + ), + ( + "class Foo implements Bar { private set setter(v: number) {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "public-fields" }])), + None, + ), + ( + "class Foo implements Bar { protected set setter(v: number) {} }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "public-fields" }])), + None, + ), + ( + "class Foo implements Bar { #property = () => {}; }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "public-fields" }])), + None, + ), + ( + "class Foo implements Bar { private property = () => {}; }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "public-fields" }])), + None, + ), + ( + "class Foo implements Bar { protected property = () => {}; }", + Some(serde_json::json!([{ "ignoreClassesWithImplements": "public-fields" }])), + None, + ), + ]; + + Tester::new(ClassMethodsUseThis::NAME, ClassMethodsUseThis::PLUGIN, pass, fail) + .test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/eslint_class_methods_use_this.snap b/crates/oxc_linter/src/snapshots/eslint_class_methods_use_this.snap new file mode 100644 index 0000000000000..a6825cf7c69bc --- /dev/null +++ b/crates/oxc_linter/src/snapshots/eslint_class_methods_use_this.snap @@ -0,0 +1,422 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo() {} } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo() {/**this**/} } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo() {var a = function () {this};} } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo() {var a = function () {var b = function(){this}};} } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo() {window.this} } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo() {that.this = 'this';} } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo() { () => undefined; } } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo(){} 'bar'(){} 123(){} [`baz`](){} [a](){} [f(a)](){} get quux(){} set[a](b){} *quuux(){} } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `bar` to have this. + ╭─[class_methods_use_this.tsx:1:19] + 1 │ class A { foo(){} 'bar'(){} 123(){} [`baz`](){} [a](){} [f(a)](){} get quux(){} set[a](b){} *quuux(){} } + · ───── + ╰──── + help: Consider converting method `bar` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `123` to have this. + ╭─[class_methods_use_this.tsx:1:29] + 1 │ class A { foo(){} 'bar'(){} 123(){} [`baz`](){} [a](){} [f(a)](){} get quux(){} set[a](b){} *quuux(){} } + · ─── + ╰──── + help: Consider converting method `123` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `baz` to have this. + ╭─[class_methods_use_this.tsx:1:38] + 1 │ class A { foo(){} 'bar'(){} 123(){} [`baz`](){} [a](){} [f(a)](){} get quux(){} set[a](b){} *quuux(){} } + · ───── + ╰──── + help: Consider converting method `baz` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method to have this. + ╭─[class_methods_use_this.tsx:1:50] + 1 │ class A { foo(){} 'bar'(){} 123(){} [`baz`](){} [a](){} [f(a)](){} get quux(){} set[a](b){} *quuux(){} } + · ─ + ╰──── + help: Consider converting method to a static method. + + ⚠ eslint(class-methods-use-this): Expected method to have this. + ╭─[class_methods_use_this.tsx:1:58] + 1 │ class A { foo(){} 'bar'(){} 123(){} [`baz`](){} [a](){} [f(a)](){} get quux(){} set[a](b){} *quuux(){} } + · ──── + ╰──── + help: Consider converting method to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `quux` to have this. + ╭─[class_methods_use_this.tsx:1:72] + 1 │ class A { foo(){} 'bar'(){} 123(){} [`baz`](){} [a](){} [f(a)](){} get quux(){} set[a](b){} *quuux(){} } + · ──── + ╰──── + help: Consider converting method `quux` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method to have this. + ╭─[class_methods_use_this.tsx:1:85] + 1 │ class A { foo(){} 'bar'(){} 123(){} [`baz`](){} [a](){} [f(a)](){} get quux(){} set[a](b){} *quuux(){} } + · ─ + ╰──── + help: Consider converting method to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `quuux` to have this. + ╭─[class_methods_use_this.tsx:1:94] + 1 │ class A { foo(){} 'bar'(){} 123(){} [`baz`](){} [a](){} [f(a)](){} get quux(){} set[a](b){} *quuux(){} } + · ───── + ╰──── + help: Consider converting method `quuux` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo = function() {} } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo = () => {} } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { #foo = function() {} } + · ──── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { #foo = () => {} } + · ──── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { #foo() {} } + · ──── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:15] + 1 │ class A { get #foo() {} } + · ──── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:15] + 1 │ class A { set #foo(x) {} } + · ──── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo () { return class { foo = this }; } } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo () { return function () { foo = this }; } } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo () { return class { static { this; } } } } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `method` to have this. + ╭─[class_methods_use_this.tsx:1:21] + 1 │ class Foo { private method() {} } + · ────── + ╰──── + help: Consider converting method `method` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `method` to have this. + ╭─[class_methods_use_this.tsx:1:23] + 1 │ class Foo { protected method() {} } + · ────── + ╰──── + help: Consider converting method `method` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `method` to have this. + ╭─[class_methods_use_this.tsx:1:22] + 1 │ class Foo { accessor method = function () {} } + · ────── + ╰──── + help: Consider converting method `method` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `method` to have this. + ╭─[class_methods_use_this.tsx:1:22] + 1 │ class Foo { accessor method = () => {} } + · ────── + ╰──── + help: Consider converting method `method` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `method` to have this. + ╭─[class_methods_use_this.tsx:1:30] + 1 │ class Foo { private accessor method = () => {} } + · ────── + ╰──── + help: Consider converting method `method` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `method` to have this. + ╭─[class_methods_use_this.tsx:1:32] + 1 │ class Foo { protected accessor method = () => {} } + · ────── + ╰──── + help: Consider converting method `method` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo () { return class { accessor bar = this }; } } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `method` to have this. + ╭─[class_methods_use_this.tsx:1:39] + 1 │ class Derived extends Base { override method() {} } + · ────── + ╰──── + help: Consider converting method `method` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `property` to have this. + ╭─[class_methods_use_this.tsx:1:30] + 1 │ class Derived extends Base { property = () => {} } + · ──────── + ╰──── + help: Consider converting method `property` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `property` to have this. + ╭─[class_methods_use_this.tsx:1:37] + 1 │ class Derived extends Base { public property = () => {} } + · ──────── + ╰──── + help: Consider converting method `property` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `property` to have this. + ╭─[class_methods_use_this.tsx:1:39] + 1 │ class Derived extends Base { override property = () => {} } + · ──────── + ╰──── + help: Consider converting method `property` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `getter` to have this. + ╭─[class_methods_use_this.tsx:1:25] + 1 │ class Foo { private get getter(): number {} } + · ────── + ╰──── + help: Consider converting method `getter` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `getter` to have this. + ╭─[class_methods_use_this.tsx:1:27] + 1 │ class Foo { protected get getter(): number {} } + · ────── + ╰──── + help: Consider converting method `getter` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `setter` to have this. + ╭─[class_methods_use_this.tsx:1:25] + 1 │ class Foo { private set setter(b: number) {} } + · ────── + ╰──── + help: Consider converting method `setter` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `setter` to have this. + ╭─[class_methods_use_this.tsx:1:27] + 1 │ class Foo { protected set setter(b: number) {} } + · ────── + ╰──── + help: Consider converting method `setter` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `method` to have this. + ╭─[class_methods_use_this.tsx:1:45] + 1 │ function fn() { this.foo = 303; class Foo { method() {} } } + · ────── + ╰──── + help: Consider converting method `method` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `property` to have this. + ╭─[class_methods_use_this.tsx:1:37] + 1 │ class Foo implements Bar { override property = () => {}; } + · ──────── + ╰──── + help: Consider converting method `property` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:11] + 1 │ class A { foo() {} bar() {} } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `hasOwnProperty` to have this. + ╭─[class_methods_use_this.tsx:1:20] + 1 │ class A { foo() {} hasOwnProperty() {} } + · ────────────── + ╰──── + help: Consider converting method `hasOwnProperty` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method to have this. + ╭─[class_methods_use_this.tsx:1:12] + 1 │ class A { [foo]() {} } + · ─── + ╰──── + help: Consider converting method to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `foo` to have this. + ╭─[class_methods_use_this.tsx:1:22] + 1 │ class A { #foo() { } foo() {} #bar() {} } + · ─── + ╰──── + help: Consider converting method `foo` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `bar` to have this. + ╭─[class_methods_use_this.tsx:1:31] + 1 │ class A { #foo() { } foo() {} #bar() {} } + · ──── + ╰──── + help: Consider converting method `bar` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `method` to have this. + ╭─[class_methods_use_this.tsx:1:28] + 1 │ class Foo implements Bar { #method() {} } + · ─────── + ╰──── + help: Consider converting method `method` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `method` to have this. + ╭─[class_methods_use_this.tsx:1:36] + 1 │ class Foo implements Bar { private method() {} } + · ────── + ╰──── + help: Consider converting method `method` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `method` to have this. + ╭─[class_methods_use_this.tsx:1:38] + 1 │ class Foo implements Bar { protected method() {} } + · ────── + ╰──── + help: Consider converting method `method` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `getter` to have this. + ╭─[class_methods_use_this.tsx:1:32] + 1 │ class Foo implements Bar { get #getter(): number {} } + · ─────── + ╰──── + help: Consider converting method `getter` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `getter` to have this. + ╭─[class_methods_use_this.tsx:1:40] + 1 │ class Foo implements Bar { private get getter(): number {} } + · ────── + ╰──── + help: Consider converting method `getter` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `getter` to have this. + ╭─[class_methods_use_this.tsx:1:42] + 1 │ class Foo implements Bar { protected get getter(): number {} } + · ────── + ╰──── + help: Consider converting method `getter` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `setter` to have this. + ╭─[class_methods_use_this.tsx:1:32] + 1 │ class Foo implements Bar { set #setter(v: number) {} } + · ─────── + ╰──── + help: Consider converting method `setter` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `setter` to have this. + ╭─[class_methods_use_this.tsx:1:40] + 1 │ class Foo implements Bar { private set setter(v: number) {} } + · ────── + ╰──── + help: Consider converting method `setter` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `setter` to have this. + ╭─[class_methods_use_this.tsx:1:42] + 1 │ class Foo implements Bar { protected set setter(v: number) {} } + · ────── + ╰──── + help: Consider converting method `setter` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `property` to have this. + ╭─[class_methods_use_this.tsx:1:28] + 1 │ class Foo implements Bar { #property = () => {}; } + · ───────── + ╰──── + help: Consider converting method `property` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `property` to have this. + ╭─[class_methods_use_this.tsx:1:36] + 1 │ class Foo implements Bar { private property = () => {}; } + · ──────── + ╰──── + help: Consider converting method `property` to a static method. + + ⚠ eslint(class-methods-use-this): Expected method `property` to have this. + ╭─[class_methods_use_this.tsx:1:38] + 1 │ class Foo implements Bar { protected property = () => {}; } + · ──────── + ╰──── + help: Consider converting method `property` to a static method.