@samitouri / QOS-React-2 / commits / 543d27c2f0

[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(