[rust][sema] Visit imports as declarations
Teaches the semantic analyzer about import statements. We now treat imports as declarations, so that subsequent references to the imported value can be resolved.
Joe Savona committed
Aug 15, 2023 at 09:30 UTC
b133b6c0b47f2f124430cc2ab3d3bdee70c6c921
4 files changed
+132
-127
compiler/forget/crates/forget_estree/src/generated_extensions.rs
+13
-2
@@ -1,7 +1,8 @@
1
// Manual extensions to generated types
2
use crate::{
3
- ArrowFunctionExpression, Function, FunctionDeclaration, FunctionExpression, JSXElementName,
4
- JSXMemberExpression, JSXMemberExpressionOrIdentifier, Pattern, SourceRange, SourceType,
3
+ ArrowFunctionExpression, Function, FunctionDeclaration, FunctionExpression, ImportDeclaration,
4
+ ImportDeclarationSpecifier, JSXElementName, JSXMemberExpression,
5
+ JSXMemberExpressionOrIdentifier, Pattern, SourceRange, SourceType,
6
};
7
8
/// Sentinel trait to distinguish AST *node* types
@@ -25,6 +26,16 @@ impl Pattern {
26
}
27
}
28
29
+impl ImportDeclarationSpecifier {
30
+ pub fn range(&self) -> Option<SourceRange> {
31
+ match self {
32
+ Self::ImportDefaultSpecifier(specifier) => specifier.range,
33
+ Self::ImportNamespaceSpecifier(specifier) => specifier.range,
34
+ Self::ImportSpecifier(specifier) => specifier.range,
35
+ }
36
+ }
37
+}
38
+
39
impl JSXElementName {
40
pub fn root_name(&self) -> &str {
41
match self {
compiler/forget/crates/forget_semantic_analysis/src/analyzer.rs
+40
-2
@@ -1,8 +1,9 @@
1
use forget_diagnostics::Diagnostic;
2
use forget_estree::{
3
AssignmentOperator, AssignmentPropertyOrRestElement, AssignmentTarget, Expression,
4
- ExpressionOrSuper, ForInInit, ForInit, FunctionBody, Identifier, IntoFunction, JSXElementName,
5
- Pattern, Program, SourceRange, SourceType, Statement, VariableDeclarationKind, Visitor,
4
+ ExpressionOrSuper, ForInInit, ForInit, FunctionBody, Identifier, ImportDeclarationSpecifier,
5
+ IntoFunction, JSXElementName, Pattern, Program, SourceRange, SourceType, Statement,
6
+ VariableDeclarationKind, Visitor,
7
};
8
9
use crate::{
@@ -280,6 +281,43 @@ impl Analyzer {
281
}
282
283
impl Visitor for Analyzer {
284
+ fn visit_import_declaration_specifier(
285
+ &mut self,
286
+ ast: &forget_estree::ImportDeclarationSpecifier,
287
+ ) {
288
+ let kind = self.manager.scope(self.current).kind;
289
+ if kind != ScopeKind::Module {
290
+ self.manager.diagnostics.push(Diagnostic::invalid_syntax(
291
+ "`import` declarations are only allowed at the top-level of a module",
292
+ ast.range(),
293
+ ))
294
+ }
295
+ match ast {
296
+ ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => {
297
+ Analyzer::visit_declaration_identifier(
298
+ self,
299
+ &specifier.local,
300
+ Some(DeclarationKind::Import),
301
+ );
302
+ }
303
+ ImportDeclarationSpecifier::ImportSpecifier(specifier) => {
304
+ // note: ignore the `imported` identifier
305
+ Analyzer::visit_declaration_identifier(
306
+ self,
307
+ &specifier.local,
308
+ Some(DeclarationKind::Import),
309
+ );
310
+ }
311
+ ImportDeclarationSpecifier::ImportNamespaceSpecifier(specifier) => {
312
+ Analyzer::visit_declaration_identifier(
313
+ self,
314
+ &specifier.local,
315
+ Some(DeclarationKind::Import),
316
+ );
317
+ }
318
+ }
319
+ }
320
+
321
fn visit_function_declaration(&mut self, ast: &forget_estree::FunctionDeclaration) {
322
if let Some(id) = &ast.function.id {
323
let declaration = self.manager.add_declaration(
compiler/forget/crates/forget_semantic_analysis/src/scope_manager.rs
+2
@@ -248,6 +248,7 @@ impl ScopeManager {
248
fn get_scope_for_declaration(&self, scope: ScopeId, kind: DeclarationKind) -> ScopeId {
249
match kind {
250
DeclarationKind::Let
251
+ | DeclarationKind::Import
252
| DeclarationKind::Const
253
| DeclarationKind::CatchClause
254
| DeclarationKind::For => scope,
@@ -401,6 +402,7 @@ pub enum DeclarationKind {
402
FunctionDeclaration,
403
For,
404
CatchClause,
405
+ Import,
406
}
407
408
impl From<VariableDeclarationKind> for DeclarationKind {
compiler/forget/crates/forget_semantic_analysis/tests/snapshots/analysis_test__fixtures@globals-and-imports.js.snap
+77
-123
@@ -38,10 +38,37 @@ Scope {
38
),
39
kind: Module,
40
declarations: {
41
- "Component": Declaration {
41
+ "Foo": Declaration {
42
id: DeclarationId(
43
0,
44
),
45
+ kind: Import,
46
+ scope: ScopeId(
47
+ 1,
48
+ ),
49
+ },
50
+ "Bar": Declaration {
51
+ id: DeclarationId(
52
+ 1,
53
+ ),
54
+ kind: Import,
55
+ scope: ScopeId(
56
+ 1,
57
+ ),
58
+ },
59
+ "Baz": Declaration {
60
+ id: DeclarationId(
61
+ 2,
62
+ ),
63
+ kind: Import,
64
+ scope: ScopeId(
65
+ 1,
66
+ ),
67
+ },
68
+ "Component": Declaration {
69
+ id: DeclarationId(
70
+ 3,
71
+ ),
72
kind: FunctionDeclaration,
73
scope: ScopeId(
74
1,
@@ -58,7 +85,7 @@ Scope {
85
declarations: {
86
"props": Declaration {
87
id: DeclarationId(
61
- 1,
88
+ 4,
89
),
90
kind: FunctionDeclaration,
91
scope: ScopeId(
@@ -67,7 +94,7 @@ Scope {
94
},
95
"g": Declaration {
96
id: DeclarationId(
70
- 2,
97
+ 5,
98
),
99
kind: Let,
100
scope: ScopeId(
@@ -76,7 +103,7 @@ Scope {
103
},
104
"y": Declaration {
105
id: DeclarationId(
79
- 3,
106
+ 6,
107
),
108
kind: Let,
109
scope: ScopeId(
@@ -85,7 +112,7 @@ Scope {
112
},
113
"s": Declaration {
114
id: DeclarationId(
88
- 4,
115
+ 7,
116
),
117
kind: Let,
118
scope: ScopeId(
@@ -94,7 +121,7 @@ Scope {
121
},
122
"b": Declaration {
123
id: DeclarationId(
97
- 5,
124
+ 8,
125
),
126
kind: Let,
127
scope: ScopeId(
@@ -103,7 +130,7 @@ Scope {
130
},
131
"n": Declaration {
132
id: DeclarationId(
106
- 6,
133
+ 9,
134
),
135
kind: Let,
136
scope: ScopeId(
@@ -112,7 +139,7 @@ Scope {
139
},
140
"x": Declaration {
141
id: DeclarationId(
115
- 7,
142
+ 10,
143
),
144
kind: Let,
145
scope: ScopeId(
@@ -127,7 +154,7 @@ Scope {
154
),
155
kind: Read,
156
declaration: DeclarationId(
130
- 1,
157
+ 4,
158
),
159
declaration (name): "props",
160
scope: ScopeId(
@@ -140,7 +167,7 @@ Scope {
167
),
168
kind: Read,
169
declaration: DeclarationId(
143
- 1,
170
+ 4,
171
),
172
declaration (name): "props",
173
scope: ScopeId(
@@ -153,13 +180,52 @@ Scope {
180
),
181
kind: Read,
182
declaration: DeclarationId(
156
- 1,
183
+ 4,
184
),
185
declaration (name): "props",
186
scope: ScopeId(
187
2,
188
),
189
},
190
+ Reference {
191
+ id: ReferenceId(
192
+ 3,
193
+ ),
194
+ kind: Read,
195
+ declaration: DeclarationId(
196
+ 0,
197
+ ),
198
+ declaration (name): "Foo",
199
+ scope: ScopeId(
200
+ 2,
201
+ ),
202
+ },
203
+ Reference {
204
+ id: ReferenceId(
205
+ 4,
206
+ ),
207
+ kind: Read,
208
+ declaration: DeclarationId(
209
+ 1,
210
+ ),
211
+ declaration (name): "Bar",
212
+ scope: ScopeId(
213
+ 2,
214
+ ),
215
+ },
216
+ Reference {
217
+ id: ReferenceId(
218
+ 5,
219
+ ),
220
+ kind: Read,
221
+ declaration: DeclarationId(
222
+ 2,
223
+ ),
224
+ declaration (name): "Baz",
225
+ scope: ScopeId(
226
+ 2,
227
+ ),
228
+ },
229
],
230
children: [
231
Scope {
@@ -186,70 +252,6 @@ Scope {
252
},
253
],
254
}
189
-Diagnostic(
190
- DiagnosticData {
191
- message: "Undefined variable",
192
- span: Some(
193
- SourceSpan {
194
- offset: SourceOffset(
195
- 7,
196
- ),
197
- length: 3,
198
- },
199
- ),
200
- related_information: [],
201
- severity: InvalidSyntax,
202
- data: [],
203
- },
204
-)
205
-Diagnostic(
206
- DiagnosticData {
207
- message: "Undefined variable",
208
- span: Some(
209
- SourceSpan {
210
- offset: SourceOffset(
211
- 35,
212
- ),
213
- length: 3,
214
- },
215
- ),
216
- related_information: [],
217
- severity: InvalidSyntax,
218
- data: [],
219
- },
220
-)
221
-Diagnostic(
222
- DiagnosticData {
223
- message: "Undefined variable",
224
- span: Some(
225
- SourceSpan {
226
- offset: SourceOffset(
227
- 60,
228
- ),
229
- length: 3,
230
- },
231
- ),
232
- related_information: [],
233
- severity: InvalidSyntax,
234
- data: [],
235
- },
236
-)
237
-Diagnostic(
238
- DiagnosticData {
239
- message: "Undefined variable",
240
- span: Some(
241
- SourceSpan {
242
- offset: SourceOffset(
243
- 60,
244
- ),
245
- length: 3,
246
- },
247
- ),
248
- related_information: [],
249
- severity: InvalidSyntax,
250
- data: [],
251
- },
252
-)
255
Diagnostic(
256
DiagnosticData {
257
message: "Undefined variable",
@@ -378,52 +380,4 @@ Diagnostic(
380
data: [],
381
},
382
)
381
-Diagnostic(
382
- DiagnosticData {
383
- message: "Undefined variable",
384
- span: Some(
385
- SourceSpan {
386
- offset: SourceOffset(
387
- 327,
388
- ),
389
- length: 3,
390
- },
391
- ),
392
- related_information: [],
393
- severity: InvalidSyntax,
394
- data: [],
395
- },
396
-)
397
-Diagnostic(
398
- DiagnosticData {
399
- message: "Undefined variable",
400
- span: Some(
401
- SourceSpan {
402
- offset: SourceOffset(
403
- 334,
404
- ),
405
- length: 3,
406
- },
407
- ),
408
- related_information: [],
409
- severity: InvalidSyntax,
410
- data: [],
411
- },
412
-)
413
-Diagnostic(
414
- DiagnosticData {
415
- message: "Undefined variable",
416
- span: Some(
417
- SourceSpan {
418
- offset: SourceOffset(
419
- 341,
420
- ),
421
- length: 3,
422
- },
423
- ),
424
- related_information: [],
425
- severity: InvalidSyntax,
426
- data: [],
427
- },
428
-)
383