rslint_core/groups/errors/
no_this_before_super.rs1use crate::rule_prelude::*;
2use ast::{ClassDecl, ClassElement};
3
4declare_lint! {
5 #[derive(Default)]
78 NoThisBeforeSuper,
79 errors,
80 tags(Recommended),
81 "no-this-before-super",
82}
83
84#[typetag::serde]
85impl CstRule for NoThisBeforeSuper {
86 fn check_node(&self, node: &SyntaxNode, ctx: &mut RuleCtx) -> Option<()> {
87 let class_decl = node.try_to::<ClassDecl>()?;
88 let constructor = class_decl.body()?.elements().find_map(|x| match x {
89 ClassElement::Constructor(c) => Some(c),
90 _ => None,
91 })?;
92
93 let mut super_call = None;
94 let mut this_expr = None;
95 for node in constructor.syntax().descendants_with_tokens() {
96 if node.kind() == SyntaxKind::SUPER_CALL {
97 super_call = Some(node.text_range());
98
99 if let Some(this) = node.as_node().and_then(|node| {
100 node.descendants_with_tokens().skip(2).find(|n| {
101 n.kind() == SyntaxKind::SUPER_KW || n.kind() == SyntaxKind::THIS_EXPR
102 })
103 }) {
104 this_expr = Some(this);
105 }
106
107 continue;
108 }
109
110 if (node.kind() == SyntaxKind::THIS_EXPR
111 || (node.kind() == SyntaxKind::SUPER_KW
112 && node.parent()?.kind() != SyntaxKind::SUPER_CALL))
113 && super_call.is_none()
114 {
115 this_expr = Some(node);
116 }
119 }
120
121 match (super_call, this_expr) {
122 (Some(super_call), Some(this)) => {
123 let use_name = match this.kind() {
124 SyntaxKind::SUPER_KW => "super",
125 SyntaxKind::THIS_EXPR => "this",
126 _ => unreachable!(),
127 };
128 let err = ctx
129 .err(
130 self.name(),
131 format!("`{}` is not allowed before calling `super()`", use_name),
132 )
133 .primary(this, format!("`{}` is used here...", use_name))
134 .secondary(super_call, "...but `super` is called here")
135 .footer_note(format!(
136 "using `{}` before calling `super()` will result in a runtime error",
137 use_name
138 ));
139 ctx.add_err(err);
140
141 None
142 }
143 _ => None,
144 }
145 }
146}
147
148rule_tests! {
149 NoThisBeforeSuper::default(),
150 err: {
151 "class A extends B { constructor() { this.a = 0; super(); } }",
152 "class A extends B { constructor() { this.foo(); super(); } }",
153 "class A extends B { constructor() { super.foo(); super(); } }",
154 "class A extends B { constructor() { super(this.foo()); } }",
155 },
156 ok: {
157 "class A { constructor() { this.a = 0; } }",
158 "class A extends B { constructor() { super(); this.a = 0; } }",
159 "class A extends B { foo() { this.a = 0; } }",
160 }
161}