[rust][sema] Support classes
Adds name resolution support for class declarations and expressions. Mostly this involves _not_ visiting some `Identifier` nodes that don't actually represent variables. For example, method names don't introduce new variables (but if they are computed, they may _refer_ to variables).
Joe Savona committed
Aug 17, 2023 at 10:02 UTC
543d27c2f0e43113fb22740a90946eeb52e57bb5
2 files changed
+114
-6
compiler/forget/crates/forget_estree/src/generated_extensions.rs
+41
-3
@@ -1,8 +1,8 @@
1
// Manual extensions to generated types
2
use crate::{
3
- ArrowFunctionExpression, Function, FunctionDeclaration, FunctionExpression,
4
- ImportDeclarationSpecifier, JSXElementName, JSXMemberExpression,
5
- JSXMemberExpressionOrIdentifier, Pattern, SourceRange, SourceType,
3
+ ArrowFunctionExpression, Class, ClassDeclaration, ClassExpression, Function,
4
+ FunctionDeclaration, FunctionExpression, ImportDeclarationSpecifier, JSXElementName,
5
+ JSXMemberExpression, JSXMemberExpressionOrIdentifier, Pattern, SourceRange, SourceType,
6
};
7
8
/// Sentinel trait to distinguish AST *node* types
@@ -102,3 +102,41 @@ impl IntoFunction for Function {
102
self
103
}
104
}
105
+
106
+pub trait IntoClass: ESTreeNode {
107
+ fn class(&self) -> &Class;
108
+
109
+ fn into_class(self) -> Class;
110
+}
111
+
112
+impl IntoClass for ClassDeclaration {
113
+ fn class(&self) -> &Class {
114
+ &self.class
115
+ }
116
+
117
+ fn into_class(self) -> Class {
118
+ self.class
119
+ }
120
+}
121
+
122
+impl IntoClass for ClassExpression {
123
+ fn class(&self) -> &Class {
124
+ &self.class
125
+ }
126
+
127
+ fn into_class(self) -> Class {
128
+ self.class
129
+ }
130
+}
131
+
132
+impl ESTreeNode for Class {}
133
+
134
+impl IntoClass for Class {
135
+ fn class(&self) -> &Class {
136
+ self
137
+ }
138
+
139
+ fn into_class(self) -> Class {
140
+ self
141
+ }
142
+}
compiler/forget/crates/forget_semantic_analysis/src/analyzer.rs
+73
-3
@@ -1,9 +1,9 @@
1
use forget_diagnostics::Diagnostic;
2
use forget_estree::{
3
AssignmentOperator, AssignmentPropertyOrRestElement, AssignmentTarget, Expression,
4
- ExpressionOrSuper, ForInInit, ForInit, FunctionBody, Identifier, ImportDeclarationSpecifier,
5
- IntoFunction, JSXElementName, Pattern, Program, SourceRange, Statement,
6
- VariableDeclarationKind, Visitor,
4
+ ExpressionOrPrivateIdentifier, ExpressionOrSuper, ForInInit, ForInit, FunctionBody, Identifier,
5
+ ImportDeclarationSpecifier, IntoClass, IntoFunction, JSXElementName, Pattern, Program,
6
+ SourceRange, Statement, VariableDeclarationKind, Visitor,
7
};
8
9
use crate::{
@@ -186,6 +186,18 @@ impl Analyzer {
186
.insert(AstNode::from(function), scope);
187
}
188
189
+ fn visit_class<T: IntoClass>(&mut self, node: &T) {
190
+ let class = node.class();
191
+ let scope = self.enter(ScopeKind::Class, |visitor| {
192
+ if let Some(super_class) = &class.super_class {
193
+ visitor.visit_expression(super_class);
194
+ }
195
+
196
+ visitor.visit_class_body(&class.body);
197
+ });
198
+ self.manager.node_scopes.insert(AstNode::from(class), scope);
199
+ }
200
+
201
fn visit_reference_identifier(
202
&mut self,
203
name: &str,
@@ -337,6 +349,64 @@ impl Visitor for Analyzer {
349
}
350
}
351
352
+ fn visit_class_declaration(&mut self, ast: &forget_estree::ClassDeclaration) {
353
+ if let Some(id) = &ast.class.id {
354
+ let declaration = self.manager.add_declaration(
355
+ self.current,
356
+ id.name.clone(),
357
+ DeclarationKind::Class,
358
+ id.range,
359
+ );
360
+ self.manager
361
+ .node_declarations
362
+ .insert(AstNode::from(id), declaration);
363
+ }
364
+ Analyzer::visit_class(self, ast);
365
+ }
366
+
367
+ fn visit_class_property(&mut self, ast: &forget_estree::ClassProperty) {
368
+ // Static (non-computed) property names do not introduce a new identifier
369
+ // into any scope
370
+ if ast.is_computed {
371
+ self.visit_expression(&ast.key);
372
+ }
373
+ if let Some(value) = &ast.value {
374
+ self.visit_expression(value)
375
+ }
376
+ }
377
+
378
+ fn visit_class_private_property(&mut self, ast: &forget_estree::ClassPrivateProperty) {
379
+ // Static (non-computed) property names do not introduce a new identifier
380
+ // into any scope
381
+ match &ast.key {
382
+ ExpressionOrPrivateIdentifier::Expression(key) => {
383
+ self.visit_expression(key);
384
+ }
385
+ ExpressionOrPrivateIdentifier::PrivateIdentifier(_)
386
+ | ExpressionOrPrivateIdentifier::PrivateName(_) => { /* no-op */ }
387
+ }
388
+ if let Some(value) = &ast.value {
389
+ self.visit_expression(value)
390
+ }
391
+ }
392
+
393
+ fn visit_static_block(&mut self, ast: &forget_estree::StaticBlock) {
394
+ self.enter(ScopeKind::StaticBlock, |visitor| {
395
+ for statement in &ast.body {
396
+ visitor.visit_statement(statement);
397
+ }
398
+ });
399
+ }
400
+
401
+ fn visit_method_definition(&mut self, ast: &forget_estree::MethodDefinition) {
402
+ // Static (non-computed) method names do not introduce a new identifier
403
+ // into any scope
404
+ if ast.is_computed {
405
+ self.visit_expression(&ast.key)
406
+ }
407
+ self.visit_function_expression(&ast.value);
408
+ }
409
+
410
fn visit_function_declaration(&mut self, ast: &forget_estree::FunctionDeclaration) {
411
if let Some(id) = &ast.function.id {
412
let declaration = self.manager.add_declaration(