From 0e88ebb1ac9706beb9cf2141538d14a91ace6b30 Mon Sep 17 00:00:00 2001 From: Sysix Date: Mon, 9 Dec 2024 18:08:52 +0100 Subject: [PATCH] feat(linter): add eslint/constructor-super --- .../src/rules/eslint/constructor_super.rs | 37 ++++++++++++++----- .../snapshots/eslint_constructor_super.snap | 21 +++++++++-- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/constructor_super.rs b/crates/oxc_linter/src/rules/eslint/constructor_super.rs index 7a65d9fbd3f86..9b054877139fc 100644 --- a/crates/oxc_linter/src/rules/eslint/constructor_super.rs +++ b/crates/oxc_linter/src/rules/eslint/constructor_super.rs @@ -240,20 +240,31 @@ fn executes_always_super_expression<'a>( } if let Statement::IfStatement(if_statement) = &statement { - if if_statement.alternate.is_none() { + if matches!(&if_statement.consequent, Statement::ReturnStatement(return_statement) if return_statement.argument.is_none()) + { return Err(ErrorReport { - reason: ErrorReason::NotFound, - spans: vec![if_statement.span], + reason: ErrorReason::ReturnWithoutCall, + spans: vec![if_statement.consequent.span()], }); } if let Ok(mut consequent) = executes_always_super_expression(&if_statement.consequent) { - if let Ok(alternative) = - executes_always_super_expression(if_statement.alternate.as_ref().unwrap()) - { + let Some(alternative_call) = if_statement.alternate.as_ref() else { + return Err(ErrorReport { + reason: ErrorReason::MissingCallOnBranch, + spans: vec![if_statement.span], + }); + }; + + if let Ok(alternative) = executes_always_super_expression(alternative_call) { consequent.extend(alternative); return Ok(consequent); } + + return Err(ErrorReport { + reason: ErrorReason::MissingCallOnBranch, + spans: vec![if_statement.span], + }); } return Err(ErrorReport { reason: ErrorReason::NotFound, spans: vec![if_statement.span] }); @@ -362,7 +373,7 @@ fn has_return_statement<'a>(method: &'a MethodDefinition<'a>) -> bool { }; for statement in &func_body.statements { - if matches!(statement, Statement::ReturnStatement(_)) { + if is_blocking_execution(statement) { return true; } } @@ -370,6 +381,14 @@ fn has_return_statement<'a>(method: &'a MethodDefinition<'a>) -> bool { false } +fn is_blocking_execution<'a>(statement: &'a Statement<'a>) -> bool { + if matches!(statement, Statement::ReturnStatement(_) | Statement::ThrowStatement(_)) { + return true; + } + + false +} + fn get_constructor_method<'a>(class: &'a ClassBody<'a>) -> Option<&'a MethodDefinition<'a>> { if class.body.is_empty() { return None; @@ -524,10 +543,10 @@ fn test() { "class A extends B { constructor() { switch (a) { case 0: break; default: super(); } } }", "class A extends B { constructor() { try { super(); } catch (err) {} } }", "class A extends B { constructor() { try { a; } catch (err) { super(); } } }", -// "class A extends B { constructor() { if (a) return; super(); } }", +"class A extends B { constructor() { if (a) return; super(); } }", // "class A extends B { constructor() { super(); super(); } }", // "class A extends B { constructor() { super() || super(); } }", -// "class A extends B { constructor() { if (a) super(); super(); } }", +"class A extends B { constructor() { if (a) super(); super(); } }", // "class A extends B { constructor() { switch (a) { case 0: super(); default: super(); } } }", "class A extends B { constructor(a) { while (a) super(); } }", "class A extends B { constructor() { return; super(); } }", diff --git a/crates/oxc_linter/src/snapshots/eslint_constructor_super.snap b/crates/oxc_linter/src/snapshots/eslint_constructor_super.snap index 40ec5d027b014..b07b6b5b47569 100644 --- a/crates/oxc_linter/src/snapshots/eslint_constructor_super.snap +++ b/crates/oxc_linter/src/snapshots/eslint_constructor_super.snap @@ -139,10 +139,11 @@ snapshot_kind: text · ─────────── ╰──── - ⚠ eslint(constructor-super): Expected to call 'super()' - ╭─[constructor_super.tsx:1:21] + ⚠ eslint(constructor-super): Lacked a call of 'super()' in some code paths. + ╭─[constructor_super.tsx:1:37] 1 │ class A extends B { constructor() { if (a) super(); } } - · ─────────── + · ───────┬─────── + · ╰── This path is lacking of a 'super()' call ╰──── ⚠ eslint(constructor-super): Expected to call 'super()' @@ -186,6 +187,20 @@ snapshot_kind: text · ╰── Inside a 'try' and 'catch' block, a 'super' call not be guaranteed to be called ╰──── + ⚠ eslint(constructor-super): Lacked a call of 'super()' in some code paths. + ╭─[constructor_super.tsx:1:44] + 1 │ class A extends B { constructor() { if (a) return; super(); } } + · ───┬─── + · ╰── This path fast returns without calling 'super()' + ╰──── + + ⚠ eslint(constructor-super): Lacked a call of 'super()' in some code paths. + ╭─[constructor_super.tsx:1:37] + 1 │ class A extends B { constructor() { if (a) super(); super(); } } + · ───────┬─────── + · ╰── This path is lacking of a 'super()' call + ╰──── + ⚠ eslint(constructor-super): Expected to call 'super()' ╭─[constructor_super.tsx:1:21] 1 │ class A extends B { constructor(a) { while (a) super(); } }