From 3656802fd48543701739f3902f64fc72767b5b14 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Sat, 24 Jun 2023 10:08:41 -0400 Subject: [PATCH] feat: add eslint no-setter-return rule (#434) Co-authored-by: Boshen --- crates/oxc_linter/src/rules.rs | 1 + .../oxc_linter/src/rules/no_setter_return.rs | 288 ++++++++++++++++++ .../src/snapshots/no_setter_return.snap | 257 ++++++++++++++++ crates/oxc_linter/src/tester.rs | 4 +- crates/oxc_semantic/src/binder.rs | 27 +- crates/oxc_syntax/src/scope.rs | 4 + 6 files changed, 579 insertions(+), 2 deletions(-) create mode 100644 crates/oxc_linter/src/rules/no_setter_return.rs create mode 100644 crates/oxc_linter/src/snapshots/no_setter_return.snap diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index f0a7f72b5..91f130528 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -19,6 +19,7 @@ oxc_macros::declare_all_lint_rules! { no_eval, no_new_symbol, no_self_compare, + no_setter_return, no_shadow_restricted_names, no_mixed_operators, no_constant_binary_expression, diff --git a/crates/oxc_linter/src/rules/no_setter_return.rs b/crates/oxc_linter/src/rules/no_setter_return.rs new file mode 100644 index 000000000..374432dc5 --- /dev/null +++ b/crates/oxc_linter/src/rules/no_setter_return.rs @@ -0,0 +1,288 @@ +use oxc_ast::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)] +#[error("eslint(no-setter-return): Setter cannot return a value")] +struct NoSetterReturnDiagnostic(#[label] pub Span); + +#[derive(Debug, Default, Clone)] +pub struct NoSetterReturn; + +declare_oxc_lint!( + /// ### What it does + /// + /// Setters cannot return values. + /// + /// ### Why is this bad? + /// + /// While returning a value from a setter does not produce an error, the returned value is + /// being ignored. Therefore, returning a value from a setter is either unnecessary or a + /// possible error, since the returned value cannot be used. + /// + /// ### Example + /// + /// ```javascript + /// class URL { + /// set origin() { + /// return true; + /// } + /// } + /// ``` + NoSetterReturn, + correctness +); + +impl Rule for NoSetterReturn { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + if let AstKind::ReturnStatement(stmt) = node.kind() + && stmt.argument.is_some() + && ctx.scopes().get_flags(node.scope_id()).is_set_accessor() { + ctx.diagnostic(NoSetterReturnDiagnostic(stmt.span)); + }; + } +} + +#[allow(clippy::too_many_lines)] +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + ("function foo() { return 1; }", None), + ("function set(val) { return 1; }", None), + ("var foo = function() { return 1; };", None), + ("var foo = function set() { return 1; };", None), + ("var set = function() { return 1; };", None), + ("var set = function set(val) { return 1; };", None), + ("var set = val => { return 1; };", None), + ("var set = val => 1;", None), + ("({ set a(val) { }}); function foo() { return 1; }", None), + ("({ set a(val) { }}); (function () { return 1; });", None), + ("({ set a(val) { }}); (() => { return 1; });", None), + ("({ set a(val) { }}); (() => 1);", None), + ("return 1;", None), + ("return 1;", None), + ("return 1; function foo(){ return 1; } return 1;", None), + ( + "function foo(){} return 1; var bar = function*(){ return 1; }; return 1; var baz = () => {}; return 1;", + None, + ), + ("({ set foo(val) { return; } })", None), + ("({ set foo(val) { if (val) { return; } } })", None), + ("class A { set foo(val) { return; } }", None), + ("(class { set foo(val) { if (val) { return; } else { return; } return; } })", None), + ("class A { set foo(val) { try {} catch(e) { return; } } }", None), + ("({ get foo() { return 1; } })", None), + ("({ get set() { return 1; } })", None), + ("({ set(val) { return 1; } })", None), + ("({ set: function(val) { return 1; } })", None), + ("({ foo: function set(val) { return 1; } })", None), + ("({ set: function set(val) { return 1; } })", None), + ("({ set: (val) => { return 1; } })", None), + ("({ set: (val) => 1 })", None), + ("set = { foo(val) { return 1; } };", None), + ("class A { constructor(val) { return 1; } }", None), + ("class set { constructor(val) { return 1; } }", None), + ("class set { foo(val) { return 1; } }", None), + ("var set = class { foo(val) { return 1; } }", None), + ("(class set { foo(val) { return 1; } })", None), + ("class A { get foo() { return val; } }", None), + ("class A { get set() { return val; } }", None), + ("class A { set(val) { return 1; } }", None), + ("class A { static set(val) { return 1; } }", None), + ("({ set: set = function set(val) { return 1; } } = {})", None), + ("({ set: set = (val) => 1 } = {})", None), + ("class C { set; foo() { return 1; } }", None), + ("({ set foo(val) { function foo(val) { return 1; } } })", None), + ("({ set foo(val) { var foo = function(val) { return 1; } } })", None), + ("({ set foo(val) { var foo = (val) => { return 1; } } })", None), + ("({ set foo(val) { var foo = (val) => 1; } })", None), + ("({ set [function() { return 1; }](val) {} })", None), + ("({ set [() => { return 1; }](val) {} })", None), + ("({ set [() => 1](val) {} })", None), + ("({ set foo(val = function() { return 1; }) {} })", None), + ("({ set foo(val = v => 1) {} })", None), + ("(class { set foo(val) { function foo(val) { return 1; } } })", None), + ("(class { set foo(val) { var foo = function(val) { return 1; } } })", None), + ("(class { set foo(val) { var foo = (val) => { return 1; } } })", None), + ("(class { set foo(val) { var foo = (val) => 1; } })", None), + ("(class { set [function() { return 1; }](val) {} })", None), + ("(class { set [() => { return 1; }](val) {} })", None), + ("(class { set [() => 1](val) {} })", None), + ("(class { set foo(val = function() { return 1; }) {} })", None), + ("(class { set foo(val = (v) => 1) {} })", None), + ("Object.defineProperty(foo, 'bar', { set(val) { return; } })", None), + ("Reflect.defineProperty(foo, 'bar', { set(val) { if (val) { return; } } })", None), + ( + "Object.defineProperties(foo, { bar: { set(val) { try { return; } catch(e){} } } })", + None, + ), + ("Object.create(foo, { bar: { set: function(val) { return; } } })", None), + ("x = { set(val) { return 1; } }", None), + ("x = { foo: { set(val) { return 1; } } }", None), + ("Object.defineProperty(foo, 'bar', { value(val) { return 1; } })", None), + ("Reflect.defineProperty(foo, 'bar', { value: function set(val) { return 1; } })", None), + ("Object.defineProperties(foo, { bar: { [set](val) { return 1; } } })", None), + ("Object.create(foo, { bar: { 'set ': function(val) { return 1; } } })", None), + ("Object.defineProperty(foo, 'bar', { [`set `]: (val) => { return 1; } })", None), + ("Reflect.defineProperty(foo, 'bar', { Set(val) { return 1; } })", None), + ("Object.defineProperties(foo, { bar: { value: (val) => 1 } })", None), + ("Object.create(foo, { set: { value: function(val) { return 1; } } })", None), + ("Object.defineProperty(foo, 'bar', { baz(val) { return 1; } })", None), + ("Reflect.defineProperty(foo, 'bar', { get(val) { return 1; } })", None), + ("Object.create(foo, { set: function(val) { return 1; } })", None), + ("Object.defineProperty(foo, { set: (val) => 1 })", None), + ("Object.defineProperty(foo, 'bar', { set(val) { function foo() { return 1; } } })", None), + ( + "Reflect.defineProperty(foo, 'bar', { set(val) { var foo = function() { return 1; } } })", + None, + ), + ("Object.defineProperties(foo, { bar: { set(val) { () => { return 1 }; } } })", None), + ("Object.create(foo, { bar: { set: (val) => { (val) => 1; } } })", None), + ("Object.defineProperty(foo, 'bar', 'baz', { set(val) { return 1; } })", None), + ("Object.defineProperty(foo, { set(val) { return 1; } }, 'bar')", None), + ("Object.defineProperty({ set(val) { return 1; } }, foo, 'bar')", None), + ("Reflect.defineProperty(foo, 'bar', 'baz', { set(val) { return 1; } })", None), + ("Reflect.defineProperty(foo, { set(val) { return 1; } }, 'bar')", None), + ("Reflect.defineProperty({ set(val) { return 1; } }, foo, 'bar')", None), + ("Object.defineProperties(foo, bar, { baz: { set(val) { return 1; } } })", None), + ("Object.defineProperties({ bar: { set(val) { return 1; } } }, foo)", None), + ("Object.create(foo, bar, { baz: { set(val) { return 1; } } })", None), + ("Object.create({ bar: { set(val) { return 1; } } }, foo)", None), + ("Object.DefineProperty(foo, 'bar', { set(val) { return 1; } })", None), + ("Reflect.DefineProperty(foo, 'bar', { set(val) { if (val) { return 1; } } })", None), + ( + "Object.DefineProperties(foo, { bar: { set(val) { try { return 1; } catch(e){} } } })", + None, + ), + ("Object.Create(foo, { bar: { set: function(val) { return 1; } } })", None), + ("object.defineProperty(foo, 'bar', { set(val) { return 1; } })", None), + ("reflect.defineProperty(foo, 'bar', { set(val) { if (val) { return 1; } } })", None), + ( + "Reflect.defineProperties(foo, { bar: { set(val) { try { return 1; } catch(e){} } } })", + None, + ), + ("object.create(foo, { bar: { set: function(val) { return 1; } } })", None), + ("Reflect.defineProperty(foo, 'bar', { set(val) { if (val) { return 1; } } })", None), + ( + "/* globals Object:off */ Object.defineProperty(foo, 'bar', { set(val) { return 1; } })", + None, + ), + ( + "Object.defineProperties(foo, { bar: { set(val) { try { return 1; } catch(e){} } } })", + None, + ), + ("let Object; Object.defineProperty(foo, 'bar', { set(val) { return 1; } })", None), + ( + "function f() { Reflect.defineProperty(foo, 'bar', { set(val) { if (val) { return 1; } } }); var Reflect;}", + None, + ), + ( + "function f(Object) { Object.defineProperties(foo, { bar: { set(val) { try { return 1; } catch(e){} } } }) }", + None, + ), + ( + "if (x) { const Object = getObject(); Object.create(foo, { bar: { set: function(val) { return 1; } } }) }", + None, + ), + ( + "x = function Object() { Object.defineProperty(foo, 'bar', { set(val) { return 1; } }) }", + None, + ), + ]; + + let fail = vec![ + ("({ set a(val){ return val + 1; } })", None), + ("({ set a(val) { return 1; } })", None), + ("class A { set a(val) { return 1; } }", None), + ("class A { static set a(val) { return 1; } }", None), + ("(class { set a(val) { return 1; } })", None), + ("({ set a(val) { return val; } })", None), + ("class A { set a(val) { return undefined; } }", None), + ("(class { set a(val) { return null; } })", None), + ("({ set a(val) { return x + y; } })", None), + ("class A { set a(val) { return foo(); } }", None), + ("(class { set a(val) { return this._a; } })", None), + ("({ set a(val) { return this.a; } })", None), + ("({ set a(val) { if (foo) { return 1; }; } })", None), + ("class A { set a(val) { try { return 1; } catch(e) {} } }", None), + ("(class { set a(val) { while (foo){ if (bar) break; else return 1; } } })", None), + ("({ set a(val) { return 1; }, set b(val) { return 1; } })", None), + ("class A { set a(val) { return 1; } set b(val) { return 1; } }", None), + ("(class { set a(val) { return 1; } static set b(val) { return 1; } })", None), + ("({ set a(val) { if(val) { return 1; } else { return 2 }; } })", None), + ( + "class A { set a(val) { switch(val) { case 1: return x; case 2: return y; default: return z } } }", + None, + ), + ( + "(class { static set a(val) { if (val > 0) { this._val = val; return val; } return false; } })", + None, + ), + ("({ set a(val) { if(val) { return 1; } else { return; }; } })", None), + ( + "class A { set a(val) { switch(val) { case 1: return x; case 2: return; default: return z } } }", + None, + ), + ( + "(class { static set a(val) { if (val > 0) { this._val = val; return; } return false; } })", + None, + ), + ("({ set a(val) { function b(){} return b(); } })", None), + ("class A { set a(val) { return () => {}; } }", None), + ("(class { set a(val) { function b(){ return 1; } return 2; } })", None), + ("({ set a(val) { function b(){ return; } return 1; } })", None), + ("class A { set a(val) { var x = function() { return 1; }; return 2; } }", None), + ("(class { set a(val) { var x = () => { return; }; return 2; } })", None), + ("function f(){}; ({ set a(val) { return 1; } });", None), + ("x = function f(){}; class A { set a(val) { return 1; } };", None), + ("x = () => {}; A = class { set a(val) { return 1; } };", None), + ("return; ({ set a(val) { return 1; } }); return 2;", None), + // ("Object.defineProperty(foo, 'bar', { set(val) { return 1; } })", None), + // ("Reflect.defineProperty(foo, 'bar', { set(val) { return 1; } })", None), + // ("Object.defineProperties(foo, { baz: { set(val) { return 1; } } })", None), + // ("Object.create(null, { baz: { set(val) { return 1; } } })", None), + // ("Object.defineProperty(foo, 'bar', { set: val => val })", None), + // ("Reflect.defineProperty(foo, 'bar', { set: val => f(val) })", None), + // ("Object.defineProperties(foo, { baz: { set: val => a + b } })", None), + // ("Object.create({}, { baz: { set: val => this._val } })", None), + // ( + // "Object.defineProperty(foo, 'bar', { set(val) { if (val) { return; } return false; }, get(val) { return 1; } })", + // None, + // ), + // ( + // "Reflect.defineProperty(foo, 'bar', { set(val) { try { return f(val) } catch (e) { return e }; } })", + // None, + // ), + // ( + // "Object.defineProperties(foo, { bar: { get(){ return null; }, set(val) { return null; } } })", + // None, + // ), + // ( + // "Object.create(null, { baz: { set(val) { return this._val; return; return undefined; } } })", + // None, + // ), + // ( + // "Object.defineProperties(foo, { baz: { set(val) { return 1; } }, bar: { set(val) { return 1; } } })", + // None, + // ), + // ("Object.create({}, { baz: { set(val) { return 1; } }, bar: { set: (val) => 1 } })", None), + // ("Object['defineProperty'](foo, 'bar', { set: function bar(val) { return 1; } })", None), + // ("Reflect.defineProperty(foo, 'bar', { 'set'(val) { return 1; } })", None), + // ("Object[`defineProperties`](foo, { baz: { ['set'](val) { return 1; } } })", None), + // ("Object.create({}, { baz: { [`set`]: (val) => { return 1; } } })", None), + // ("Object.defineProperty(foo, 'bar', { set: function Object(val) { return 1; } })", None), + // ("Object.defineProperty(foo, 'bar', { set: function(Object) { return 1; } })", None), + // ("Object?.defineProperty(foo, 'bar', { set(val) { return 1; } })", None), + // ("(Object?.defineProperty)(foo, 'bar', { set(val) { return 1; } })", None), + ]; + + Tester::new(NoSetterReturn::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/no_setter_return.snap b/crates/oxc_linter/src/snapshots/no_setter_return.snap new file mode 100644 index 000000000..b8d2c0fdb --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_setter_return.snap @@ -0,0 +1,257 @@ +--- +source: crates/oxc_linter/src/tester.rs +expression: no_setter_return +--- + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ ({ set a(val){ return val + 1; } }) + · ─────────────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ ({ set a(val) { return 1; } }) + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { set a(val) { return 1; } } + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { static set a(val) { return 1; } } + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ (class { set a(val) { return 1; } }) + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ ({ set a(val) { return val; } }) + · ─────────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { set a(val) { return undefined; } } + · ───────────────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ (class { set a(val) { return null; } }) + · ──────────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ ({ set a(val) { return x + y; } }) + · ───────────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { set a(val) { return foo(); } } + · ───────────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ (class { set a(val) { return this._a; } }) + · ─────────────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ ({ set a(val) { return this.a; } }) + · ────────────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ ({ set a(val) { if (foo) { return 1; }; } }) + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { set a(val) { try { return 1; } catch(e) {} } } + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ (class { set a(val) { while (foo){ if (bar) break; else return 1; } } }) + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ ({ set a(val) { return 1; }, set b(val) { return 1; } }) + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ ({ set a(val) { return 1; }, set b(val) { return 1; } }) + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { set a(val) { return 1; } set b(val) { return 1; } } + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { set a(val) { return 1; } set b(val) { return 1; } } + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ (class { set a(val) { return 1; } static set b(val) { return 1; } }) + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ (class { set a(val) { return 1; } static set b(val) { return 1; } }) + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ ({ set a(val) { if(val) { return 1; } else { return 2 }; } }) + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ ({ set a(val) { if(val) { return 1; } else { return 2 }; } }) + · ──────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { set a(val) { switch(val) { case 1: return x; case 2: return y; default: return z } } } + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { set a(val) { switch(val) { case 1: return x; case 2: return y; default: return z } } } + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { set a(val) { switch(val) { case 1: return x; case 2: return y; default: return z } } } + · ──────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ (class { static set a(val) { if (val > 0) { this._val = val; return val; } return false; } }) + · ─────────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ (class { static set a(val) { if (val > 0) { this._val = val; return val; } return false; } }) + · ───────────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ ({ set a(val) { if(val) { return 1; } else { return; }; } }) + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { set a(val) { switch(val) { case 1: return x; case 2: return; default: return z } } } + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { set a(val) { switch(val) { case 1: return x; case 2: return; default: return z } } } + · ──────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ (class { static set a(val) { if (val > 0) { this._val = val; return; } return false; } }) + · ───────────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ ({ set a(val) { function b(){} return b(); } }) + · ─────────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { set a(val) { return () => {}; } } + · ──────────────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ (class { set a(val) { function b(){ return 1; } return 2; } }) + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ ({ set a(val) { function b(){ return; } return 1; } }) + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ class A { set a(val) { var x = function() { return 1; }; return 2; } } + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ (class { set a(val) { var x = () => { return; }; return 2; } }) + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ function f(){}; ({ set a(val) { return 1; } }); + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ x = function f(){}; class A { set a(val) { return 1; } }; + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ x = () => {}; A = class { set a(val) { return 1; } }; + · ───────── + ╰──── + + × eslint(no-setter-return): Setter cannot return a value + ╭─[no_setter_return.tsx:1:1] + 1 │ return; ({ set a(val) { return 1; } }); return 2; + · ───────── + ╰──── + + diff --git a/crates/oxc_linter/src/tester.rs b/crates/oxc_linter/src/tester.rs index e5702c4f2..09409cb7c 100644 --- a/crates/oxc_linter/src/tester.rs +++ b/crates/oxc_linter/src/tester.rs @@ -73,7 +73,9 @@ impl Tester { let allocator = Allocator::default(); let path = PathBuf::from(name).with_extension("tsx"); let source_type = SourceType::from_path(&path).expect("incorrect {path:?}"); - let ret = Parser::new(&allocator, source_text, source_type).parse(); + let ret = Parser::new(&allocator, source_text, source_type) + .allow_return_outside_function(true) + .parse(); assert!(ret.errors.is_empty(), "{:?}", &ret.errors); let program = allocator.alloc(ret.program); let semantic_ret = SemanticBuilder::new(source_text, source_type) diff --git a/crates/oxc_semantic/src/binder.rs b/crates/oxc_semantic/src/binder.rs index 505f94f7b..44da7b126 100644 --- a/crates/oxc_semantic/src/binder.rs +++ b/crates/oxc_semantic/src/binder.rs @@ -78,8 +78,8 @@ fn function_as_var(flags: ScopeFlags, source_type: SourceType) -> bool { impl<'a> Binder for Function<'a> { fn bind(&self, builder: &mut SemanticBuilder) { + let current_scope_id = builder.current_scope_id; if let Some(ident) = &self.id { - let current_scope_id = builder.current_scope_id; let flags = builder.scope.get_flags(current_scope_id); if !flags.is_strict_mode() && matches!( @@ -116,6 +116,31 @@ impl<'a> Binder for Function<'a> { ); } } + + // bind scope flags: Constructor | GetAccessor | SetAccessor + debug_assert!(builder.scope.get_flags(current_scope_id).contains(ScopeFlags::Function)); + if let Some(kind) = builder.nodes.parent_kind(builder.current_node_id) { + match kind { + AstKind::MethodDefinition(def) => { + let flag = builder.scope.get_flags_mut(current_scope_id); + *flag |= match def.kind { + MethodDefinitionKind::Constructor => ScopeFlags::Constructor, + MethodDefinitionKind::Get => ScopeFlags::GetAccessor, + MethodDefinitionKind::Set => ScopeFlags::SetAccessor, + MethodDefinitionKind::Method => ScopeFlags::empty(), + }; + } + AstKind::ObjectProperty(prop) => { + let flag = builder.scope.get_flags_mut(current_scope_id); + *flag |= match prop.kind { + PropertyKind::Get => ScopeFlags::GetAccessor, + PropertyKind::Set => ScopeFlags::SetAccessor, + PropertyKind::Init => ScopeFlags::empty(), + }; + } + _ => {} + } + } } } diff --git a/crates/oxc_syntax/src/scope.rs b/crates/oxc_syntax/src/scope.rs index 551ba2b92..d350e243b 100644 --- a/crates/oxc_syntax/src/scope.rs +++ b/crates/oxc_syntax/src/scope.rs @@ -53,4 +53,8 @@ impl ScopeFlags { pub fn is_class(&self) -> bool { self.intersects(Self::Var) } + + pub fn is_set_accessor(&self) -> bool { + self.contains(Self::SetAccessor) + } }