Skip to content

Commit

Permalink
Fix no-this-before-super rule (denoland#149)
Browse files Browse the repository at this point in the history
  • Loading branch information
magurotuna committed Jun 11, 2020
1 parent 665cc37 commit 40014a8
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 19 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Blazingly fast, see comparison with ESLint:
- `no-setter-return`
- `no-sparse-array`
- `no-this-alias`
- `no-this-before-super`
- `no-throw-literal`
- `no-unsafe-finally`
- `no-unsafe-negation`
Expand Down
1 change: 1 addition & 0 deletions src/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ pub fn get_recommended_rules() -> Vec<Box<dyn LintRule>> {
no_setter_return::NoSetterReturn::new(),
no_sparse_array::NoSparseArray::new(),
no_this_alias::NoThisAlias::new(),
no_this_before_super::NoThisBeforeSuper::new(),
no_unsafe_finally::NoUnsafeFinally::new(),
no_unsafe_negation::NoUnsafeNegation::new(),
no_with::NoWith::new(),
Expand Down
264 changes: 245 additions & 19 deletions src/rules/no_this_before_super.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use super::Context;
use super::LintRule;
use swc_ecma_ast::{
CallExpr, Class, ClassMember, ExprOrSuper, Super, ThisExpr,
CallExpr, Class, Constructor, ExprOrSuper, Super, ThisExpr,
};
use swc_ecma_visit::{Node, Visit};

Expand All @@ -25,39 +25,73 @@ impl LintRule for NoThisBeforeSuper {

struct NoThisBeforeSuperVisitor {
context: Context,
super_called: bool,
}

impl NoThisBeforeSuperVisitor {
pub fn new(context: Context) -> Self {
fn new(context: Context) -> Self {
Self { context }
}
}

impl Visit for NoThisBeforeSuperVisitor {
fn visit_class(&mut self, class: &Class, parent: &dyn Node) {
let mut class_visitor =
ClassVisitor::new(&self.context, class.super_class.is_some());
swc_ecma_visit::visit_class(&mut class_visitor, class, parent);
}
}

struct ClassVisitor<'a> {
context: &'a Context,
has_super_class: bool,
}

impl<'a> ClassVisitor<'a> {
fn new(context: &'a Context, has_super_class: bool) -> Self {
Self {
context,
super_called: false,
has_super_class,
}
}
}

fn init_on_class(&mut self) {
self.super_called = false;
impl<'a> Visit for ClassVisitor<'a> {
fn visit_class(&mut self, class: &Class, parent: &dyn Node) {
let mut class_visitor =
ClassVisitor::new(&self.context, class.super_class.is_some());
swc_ecma_visit::visit_class(&mut class_visitor, class, parent);
}
}

impl Visit for NoThisBeforeSuperVisitor {
fn visit_class(&mut self, class: &Class, _parent: &dyn Node) {
if class.super_class.is_none() {
return;
fn visit_constructor(&mut self, cons: &Constructor, parent: &dyn Node) {
if self.has_super_class {
let mut cons_visitor = ConstructorVisitor::new(&self.context);
cons_visitor.visit_constructor(cons, parent);
} else {
swc_ecma_visit::visit_constructor(self, cons, parent);
}
}
}

self.init_on_class();

let cons = class.body.iter().find_map(|m| match m {
ClassMember::Constructor(c) => Some(c),
_ => None,
});
struct ConstructorVisitor<'a> {
context: &'a Context,
super_called: bool,
}

if let Some(cons) = cons {
self.visit_constructor(cons, class);
impl<'a> ConstructorVisitor<'a> {
fn new(context: &'a Context) -> Self {
Self {
context,
super_called: false,
}
}
}

impl<'a> Visit for ConstructorVisitor<'a> {
fn visit_class(&mut self, class: &Class, parent: &dyn Node) {
let mut class_visitor =
ClassVisitor::new(&self.context, class.super_class.is_some());
swc_ecma_visit::visit_class(&mut class_visitor, class, parent);
}

fn visit_call_expr(&mut self, call_expr: &CallExpr, _parent: &dyn Node) {
for arg in &call_expr.args {
Expand Down Expand Up @@ -182,5 +216,197 @@ class A extends B {
4,
10,
);

assert_lint_err_on_line::<NoThisBeforeSuper>(
r#"
class A extends B {
constructor() {
super();
}
}
class C extends D {
constructor() {
this.c = 42;
super();
}
}
"#,
9,
4,
);
}

#[test]
fn no_this_before_super_inline_super_class() {
assert_lint_ok::<NoThisBeforeSuper>(
r#"
class A extends class extends B {
constructor() {
super();
this.a = 0;
}
} {
constructor() {
super();
this.a = 0;
}
}
"#,
);

assert_lint_err_on_line::<NoThisBeforeSuper>(
r#"
class A extends class extends B {
constructor() {
this.a = 0;
super();
}
} {
constructor() {
super();
this.a = 0;
}
}
"#,
4,
4,
);

assert_lint_err_on_line::<NoThisBeforeSuper>(
r#"
class A extends class extends B {
constructor() {
super();
this.a = 0;
}
} {
constructor() {
this.a = 0;
super();
}
}
"#,
9,
6,
);
}

#[test]
fn no_this_before_super_nested_class() {
assert_lint_ok::<NoThisBeforeSuper>(
r#"
class A extends B {
constructor() {
super();
this.a = 0;
}
foo() {
class C extends D {
constructor() {
super();
this.c = 1;
}
}
}
}
"#,
);

assert_lint_err_on_line::<NoThisBeforeSuper>(
r#"
class A extends B {
constructor() {
super();
this.a = 0;
}
foo() {
class C extends D {
constructor() {
this.c = 1;
}
}
}
}
"#,
10,
8,
);

assert_lint_err_on_line::<NoThisBeforeSuper>(
r#"
class A extends B {
constructor() {
this.a = 0;
super();
}
foo() {
class C extends D {
constructor() {
super();
this.c = 1;
}
}
}
}
"#,
4,
4,
);

assert_lint_err_on_line::<NoThisBeforeSuper>(
r#"
class A {
constructor() {
this.a = 0;
}
foo() {
class C extends D {
constructor() {
this.c = 1;
}
}
}
}
"#,
9,
8,
);

assert_lint_err_on_line::<NoThisBeforeSuper>(
r#"
class A extends B {
constructor() {
this.a = 0;
}
foo() {
class C {
constructor() {
this.c = 1;
}
}
}
}
"#,
4,
4,
);

assert_lint_err_on_line::<NoThisBeforeSuper>(
r#"
class A extends B {
constructor() {
super();
this.a = 0;
class C extends D {
constructor() {
this.c = 1;
}
}
}
}
"#,
8,
8,
);
}
}

0 comments on commit 40014a8

Please sign in to comment.