oxc_isolated_declarations/
declaration.rs1use std::cell::Cell;
2
3use oxc_allocator::{Box as ArenaBox, CloneIn, Vec as ArenaVec};
4use oxc_ast::ast::*;
5use oxc_ast_visit::{Visit, VisitMut, walk_mut::walk_ts_signatures};
6use oxc_ecmascript::BoundNames;
7use oxc_span::{GetSpan, SPAN};
8use oxc_syntax::scope::ScopeFlags;
9
10use crate::{
11 IsolatedDeclarations,
12 diagnostics::{
13 accessor_must_have_explicit_return_type, binding_element_export,
14 inferred_type_of_expression, signature_computed_property_name,
15 variable_must_have_explicit_type,
16 },
17};
18
19impl<'a> IsolatedDeclarations<'a> {
20 pub(crate) fn transform_variable_declaration(
21 &self,
22 decl: &VariableDeclaration<'a>,
23 check_binding: bool,
24 ) -> Option<ArenaBox<'a, VariableDeclaration<'a>>> {
25 if decl.declare {
26 None
27 } else {
28 let declarations =
29 self.ast.vec_from_iter(decl.declarations.iter().filter_map(|declarator| {
30 self.transform_variable_declarator(declarator, check_binding)
31 }));
32 Some(self.transform_variable_declaration_with_new_declarations(decl, declarations))
33 }
34 }
35
36 pub(crate) fn transform_variable_declaration_with_new_declarations(
37 &self,
38 decl: &VariableDeclaration<'a>,
39 declarations: ArenaVec<'a, VariableDeclarator<'a>>,
40 ) -> ArenaBox<'a, VariableDeclaration<'a>> {
41 self.ast.alloc_variable_declaration(decl.span, decl.kind, declarations, self.is_declare())
42 }
43
44 pub(crate) fn transform_variable_declarator(
45 &self,
46 decl: &VariableDeclarator<'a>,
47 check_binding: bool,
48 ) -> Option<VariableDeclarator<'a>> {
49 if decl.id.is_destructuring_pattern() {
50 decl.id.bound_names(&mut |id| {
51 if !check_binding || self.scope.has_value_reference(&id.name) {
52 self.error(binding_element_export(id.span));
53 }
54 });
55 return None;
56 }
57
58 if check_binding
59 && let Some(name) = decl.id.get_identifier_name()
60 && !self.scope.has_value_reference(&name)
61 {
62 return None;
63 }
64
65 let mut binding_type = None;
66 let mut init = None;
67 if decl.type_annotation.is_none() {
68 if let Some(init_expr) = &decl.init {
69 if decl.kind.is_const() && !Self::is_need_to_infer_type_from_expression(init_expr) {
71 if let Expression::TemplateLiteral(lit) = init_expr {
72 init =
73 self.transform_template_to_string(lit).map(Expression::StringLiteral);
74 } else {
75 init = Some(init_expr.clone_in(self.ast.allocator));
76 }
77 } else if !decl.kind.is_const()
78 || !matches!(init_expr, Expression::TemplateLiteral(_))
79 {
80 binding_type = self.infer_type_from_expression(init_expr);
82 }
83 }
84 if init.is_none() && binding_type.is_none() {
85 binding_type = Some(self.ast.ts_type_unknown_keyword(SPAN));
86 if !decl.init.as_ref().is_some_and(Expression::is_function) {
87 self.error(variable_must_have_explicit_type(decl.id.span()));
88 }
89 }
90 }
91 let id = decl.id.clone_in(self.ast.allocator);
92
93 if binding_type.is_none()
94 && let Some(ts_type) = &decl.type_annotation
95 {
96 binding_type = Some(ts_type.type_annotation.clone_in(self.ast.allocator));
97 }
98
99 let type_annotation =
100 binding_type.map(|ts_type| self.ast.ts_type_annotation(SPAN, ts_type));
101
102 Some(self.ast.variable_declarator(
103 decl.span,
104 decl.kind,
105 id,
106 type_annotation,
107 init,
108 decl.definite,
109 ))
110 }
111
112 fn transform_ts_module_block(
113 &mut self,
114 block: &ArenaBox<'a, TSModuleBlock<'a>>,
115 ) -> ArenaBox<'a, TSModuleBlock<'a>> {
116 self.scope.enter_scope(ScopeFlags::TsModuleBlock, &Cell::default());
119 let stmts = self.transform_statements_on_demand(&block.body);
120 self.scope.leave_scope();
121 self.ast.alloc_ts_module_block(SPAN, self.ast.vec(), stmts)
122 }
123
124 pub(crate) fn transform_ts_module_declaration(
125 &mut self,
126 decl: &ArenaBox<'a, TSModuleDeclaration<'a>>,
127 ) -> ArenaBox<'a, TSModuleDeclaration<'a>> {
128 if decl.declare {
129 return decl.clone_in(self.ast.allocator);
130 }
131
132 let Some(body) = &decl.body else {
133 return decl.clone_in(self.ast.allocator);
134 };
135
136 let kind = TSModuleDeclarationKind::Namespace;
138 match body {
139 TSModuleDeclarationBody::TSModuleDeclaration(inner_decl) => {
140 let inner = self.transform_ts_module_declaration(inner_decl);
141 self.ast.alloc_ts_module_declaration(
142 decl.span,
143 decl.id.clone_in(self.ast.allocator),
144 Some(TSModuleDeclarationBody::TSModuleDeclaration(inner)),
145 kind,
146 self.is_declare(),
147 )
148 }
149 TSModuleDeclarationBody::TSModuleBlock(block) => {
150 let body = self.transform_ts_module_block(block);
151 self.ast.alloc_ts_module_declaration(
152 decl.span,
153 decl.id.clone_in(self.ast.allocator),
154 Some(TSModuleDeclarationBody::TSModuleBlock(body)),
155 kind,
156 self.is_declare(),
157 )
158 }
159 }
160 }
161
162 pub(crate) fn transform_declaration(
163 &mut self,
164 decl: &Declaration<'a>,
165 check_binding: bool,
166 ) -> Option<Declaration<'a>> {
167 match decl {
168 Declaration::FunctionDeclaration(func) => {
169 let needs_transform = !check_binding
170 || func.id.as_ref().is_some_and(|id| self.scope.has_value_reference(&id.name));
171 needs_transform
172 .then(|| Declaration::FunctionDeclaration(self.transform_function(func, None)))
173 }
174 Declaration::VariableDeclaration(decl) => self
175 .transform_variable_declaration(decl, check_binding)
176 .map(Declaration::VariableDeclaration),
177 Declaration::ClassDeclaration(decl) => {
178 let needs_transform = !check_binding
179 || decl.id.as_ref().is_some_and(|id| self.scope.has_reference(&id.name));
180 needs_transform
181 .then(|| Declaration::ClassDeclaration(self.transform_class(decl, None)))
182 }
183 Declaration::TSTypeAliasDeclaration(alias_decl) => {
184 if !check_binding || self.scope.has_reference(&alias_decl.id.name) {
185 let mut decl = decl.clone_in(self.ast.allocator);
186 self.visit_declaration(&mut decl);
187 Some(decl)
188 } else {
189 None
190 }
191 }
192 Declaration::TSInterfaceDeclaration(interface_decl) => {
193 if !check_binding || self.scope.has_reference(&interface_decl.id.name) {
194 let mut decl = decl.clone_in(self.ast.allocator);
195 self.visit_declaration(&mut decl);
196 Some(decl)
197 } else {
198 None
199 }
200 }
201 Declaration::TSEnumDeclaration(enum_decl) => {
202 if !check_binding || self.scope.has_reference(&enum_decl.id.name) {
203 Some(self.transform_ts_enum_declaration(enum_decl))
204 } else {
205 None
206 }
207 }
208 Declaration::TSModuleDeclaration(decl) => {
209 if !check_binding
210 || matches!(
211 &decl.id,
212 TSModuleDeclarationName::Identifier(ident)
213 if self.scope.has_reference(&ident.name)
214 )
215 {
216 Some(Declaration::TSModuleDeclaration(
217 self.transform_ts_module_declaration(decl),
218 ))
219 } else {
220 None
221 }
222 }
223 Declaration::TSGlobalDeclaration(decl) => {
224 Some(Declaration::TSGlobalDeclaration(decl.clone_in(self.ast.allocator)))
225 }
226 Declaration::TSImportEqualsDeclaration(decl) => {
227 if !check_binding || self.scope.has_reference(&decl.id.name) {
228 Some(Declaration::TSImportEqualsDeclaration(decl.clone_in(self.ast.allocator)))
229 } else {
230 None
231 }
232 }
233 }
234 }
235
236 fn report_signature_property_key(&self, key: &PropertyKey<'a>, computed: bool) {
237 if !computed {
238 return;
239 }
240
241 let is_not_allowed = match key {
242 PropertyKey::StaticIdentifier(_) | PropertyKey::Identifier(_) => false,
243 PropertyKey::StaticMemberExpression(expr) => {
244 !expr.get_first_object().is_identifier_reference()
245 }
246 key => !Self::is_literal_key(key),
247 };
248
249 if is_not_allowed {
250 self.error(signature_computed_property_name(key.span()));
251 }
252 }
253}
254
255impl<'a> VisitMut<'a> for IsolatedDeclarations<'a> {
256 fn visit_ts_signatures(&mut self, signatures: &mut ArenaVec<'a, TSSignature<'a>>) {
257 self.transform_ts_signatures(signatures);
258 walk_ts_signatures(self, signatures);
259 }
260
261 fn visit_ts_method_signature(&mut self, signature: &mut TSMethodSignature<'a>) {
262 self.report_signature_property_key(&signature.key, signature.computed);
263 if signature.return_type.is_none() {
264 match signature.kind {
265 TSMethodSignatureKind::Method => {
266 self.error(inferred_type_of_expression(signature.span));
267 }
268 TSMethodSignatureKind::Get => {
269 self.error(accessor_must_have_explicit_return_type(signature.key.span()));
270 }
271 TSMethodSignatureKind::Set => {
272 }
274 }
275 }
276 }
277
278 fn visit_ts_property_signature(&mut self, signature: &mut TSPropertySignature<'a>) {
279 self.report_signature_property_key(&signature.key, signature.computed);
280 }
281}