1use crate::context::CheckerContext;
7use tsz_parser::parser::{NodeIndex, node_flags, syntax_kind_ext};
8use tsz_scanner::SyntaxKind;
9
10pub struct DeclarationChecker<'a, 'ctx> {
15 pub ctx: &'a mut CheckerContext<'ctx>,
16}
17
18impl<'a, 'ctx> DeclarationChecker<'a, 'ctx> {
19 pub const fn new(ctx: &'a mut CheckerContext<'ctx>) -> Self {
21 Self { ctx }
22 }
23
24 pub(crate) fn is_ambient_declaration(&self, var_idx: NodeIndex) -> bool {
27 if self.ctx.file_name.ends_with(".d.ts") {
29 return true;
30 }
31
32 if let Some(node) = self.ctx.arena.get(var_idx)
34 && self.node_has_declare_modifier(var_idx, node)
35 {
36 return true;
37 }
38
39 let mut current = var_idx;
41 while current.is_some() {
42 if let Some(node) = self.ctx.arena.get(current) {
43 if (node.flags as u32) & node_flags::AMBIENT != 0 {
44 return true;
45 }
46 if self.node_has_declare_modifier_any(current, node) {
49 return true;
50 }
51
52 if let Some(ext) = self.ctx.arena.get_extended(current) {
54 current = ext.parent;
55 } else {
56 break;
57 }
58 } else {
59 break;
60 }
61 }
62 false
63 }
64
65 fn node_has_declare_modifier(
67 &self,
68 _node_idx: NodeIndex,
69 node: &tsz_parser::parser::node::Node,
70 ) -> bool {
71 let modifiers = if let Some(class) = self.ctx.arena.get_class(node) {
72 &class.modifiers
73 } else if let Some(func) = self.ctx.arena.get_function(node) {
74 &func.modifiers
75 } else {
76 return false;
77 };
78 self.modifiers_contain_declare(modifiers)
79 }
80
81 fn node_has_declare_modifier_any(
83 &self,
84 _node_idx: NodeIndex,
85 node: &tsz_parser::parser::node::Node,
86 ) -> bool {
87 let modifiers = if let Some(class) = self.ctx.arena.get_class(node) {
89 &class.modifiers
90 } else if let Some(func) = self.ctx.arena.get_function(node) {
91 &func.modifiers
92 } else if node.kind == syntax_kind_ext::MODULE_DECLARATION {
93 if let Some(module) = self.ctx.arena.get_module(node) {
95 &module.modifiers
96 } else {
97 return false;
98 }
99 } else if let Some(var_data) = self.ctx.arena.get_variable(node) {
100 &var_data.modifiers
101 } else if let Some(enum_decl) = self.ctx.arena.get_enum(node) {
102 &enum_decl.modifiers
103 } else {
104 return false;
105 };
106 self.modifiers_contain_declare(modifiers)
107 }
108
109 fn modifiers_contain_declare(&self, modifiers: &Option<tsz_parser::parser::NodeList>) -> bool {
111 if let Some(mods) = modifiers {
112 for &mod_idx in &mods.nodes {
113 if let Some(mod_node) = self.ctx.arena.get(mod_idx)
114 && mod_node.kind == SyntaxKind::DeclareKeyword as u16
115 {
116 return true;
117 }
118 }
119 }
120 false
121 }
122
123 pub fn check(&mut self, decl_idx: NodeIndex) {
128 let Some(node) = self.ctx.arena.get(decl_idx) else {
129 return;
130 };
131
132 match node.kind {
133 k if k == syntax_kind_ext::VARIABLE_STATEMENT => {
134 self.check_variable_statement(decl_idx);
135 }
136 k if k == syntax_kind_ext::FUNCTION_DECLARATION => {
137 self.check_function_declaration(decl_idx);
138 }
139 k if k == syntax_kind_ext::CLASS_DECLARATION => {
140 self.check_class_declaration(decl_idx);
141 }
142 k if k == syntax_kind_ext::INTERFACE_DECLARATION => {
143 self.check_interface_declaration(decl_idx);
144 }
145 k if k == syntax_kind_ext::TYPE_ALIAS_DECLARATION => {
146 self.check_type_alias_declaration(decl_idx);
147 }
148 k if k == syntax_kind_ext::ENUM_DECLARATION => {
149 self.check_enum_declaration(decl_idx);
150 }
151 k if k == syntax_kind_ext::MODULE_DECLARATION => {
152 self.check_module_declaration(decl_idx);
153 }
154 _ => {
155 }
157 }
158 }
159
160 pub fn check_variable_statement(&mut self, stmt_idx: NodeIndex) {
162 let Some(node) = self.ctx.arena.get(stmt_idx) else {
163 return;
164 };
165
166 if let Some(var_stmt) = self.ctx.arena.get_variable(node) {
167 for &decl_idx in &var_stmt.declarations.nodes {
168 self.check_variable_declaration(decl_idx);
169 }
170 }
171 }
172
173 pub fn check_variable_declaration_list(&mut self, list_idx: NodeIndex) {
175 let Some(node) = self.ctx.arena.get(list_idx) else {
176 return;
177 };
178
179 if let Some(var_list) = self.ctx.arena.get_variable(node) {
180 for &decl_idx in &var_list.declarations.nodes {
181 self.check_variable_declaration(decl_idx);
182 }
183 }
184 }
185
186 pub fn check_variable_declaration(&mut self, decl_idx: NodeIndex) {
188 let Some(decl_node) = self.ctx.arena.get(decl_idx) else {
189 return;
190 };
191 let Some(decl_data) = self.ctx.arena.get_variable_declaration(decl_node) else {
192 return;
193 };
194
195 let parent_idx = if let Some(ext) = self.ctx.arena.get_extended(decl_idx) {
198 ext.parent
199 } else {
200 return;
201 };
202
203 let Some(parent_node) = self.ctx.arena.get(parent_idx) else {
204 return;
205 };
206
207 let is_const = (parent_node.flags & node_flags::CONST as u16) != 0;
209
210 if is_const && decl_data.initializer.is_none() {
212 if let Some(name_node) = self.ctx.arena.get(decl_data.name) {
214 if name_node.kind == syntax_kind_ext::OBJECT_BINDING_PATTERN
215 || name_node.kind == syntax_kind_ext::ARRAY_BINDING_PATTERN
216 {
217 } else {
220 if let Some(parent_ext) = self.ctx.arena.get_extended(parent_idx)
222 && let Some(gp_node) = self.ctx.arena.get(parent_ext.parent)
223 && (gp_node.kind == syntax_kind_ext::FOR_IN_STATEMENT
224 || gp_node.kind == syntax_kind_ext::FOR_OF_STATEMENT)
225 {
226 return;
228 }
229
230 let is_ambient = self.is_ambient_declaration(decl_idx);
232 if is_ambient {
233 return;
234 }
235
236 self.ctx.error(
237 decl_node.pos,
238 decl_node.end - decl_node.pos,
239 "'const' declarations must be initialized.".to_string(),
240 1155,
241 );
242 }
243 }
244 }
245
246 }
252
253 pub fn check_function_declaration(&mut self, func_idx: NodeIndex) {
255 let Some(node) = self.ctx.arena.get(func_idx) else {
256 return;
257 };
258 let Some(func) = self.ctx.arena.get_function(node) else {
259 return;
260 };
261
262 let has_declare = self
265 .ctx
266 .arena
267 .has_modifier(&func.modifiers, tsz_scanner::SyntaxKind::DeclareKeyword);
268
269 if has_declare && !func.parameters.nodes.is_empty() {
270 for ¶m_idx in &func.parameters.nodes {
271 let Some(param_node) = self.ctx.arena.get(param_idx) else {
272 continue;
273 };
274 let Some(param) = self.ctx.arena.get_parameter(param_node) else {
275 continue;
276 };
277
278 if param.initializer.is_some() {
281 let name_node = self.ctx.arena.get(param.name).unwrap_or(param_node);
282 self.ctx.error(
283 name_node.pos,
284 name_node.end - name_node.pos,
285 "A parameter initializer is only allowed in a function or constructor implementation.".to_string(),
286 2371, );
288 }
289 }
290 }
291
292 self.check_strict_mode_function_in_block(func_idx);
295 }
296
297 fn check_strict_mode_function_in_block(&mut self, func_idx: NodeIndex) {
300 if !self.ctx.compiler_options.target.is_es5() {
302 return;
303 }
304
305 let Some(ext) = self.ctx.arena.get_extended(func_idx) else {
308 return;
309 };
310 let parent_idx = ext.parent;
311 let Some(parent_node) = self.ctx.arena.get(parent_idx) else {
312 return;
313 };
314
315 if parent_node.kind != syntax_kind_ext::BLOCK {
317 return;
318 }
319
320 let Some(block_ext) = self.ctx.arena.get_extended(parent_idx) else {
323 return;
324 };
325 let block_parent_idx = block_ext.parent;
326 let Some(block_parent) = self.ctx.arena.get(block_parent_idx) else {
327 return;
328 };
329
330 match block_parent.kind {
331 k if k == syntax_kind_ext::FUNCTION_DECLARATION
332 || k == syntax_kind_ext::FUNCTION_EXPRESSION
333 || k == syntax_kind_ext::ARROW_FUNCTION
334 || k == syntax_kind_ext::METHOD_DECLARATION
335 || k == syntax_kind_ext::CONSTRUCTOR
336 || k == syntax_kind_ext::GET_ACCESSOR
337 || k == syntax_kind_ext::SET_ACCESSOR
338 || k == syntax_kind_ext::SOURCE_FILE
339 || k == syntax_kind_ext::MODULE_DECLARATION
340 || k == syntax_kind_ext::CLASS_STATIC_BLOCK_DECLARATION =>
341 {
342 return;
344 }
345 _ => {}
346 }
347
348 let in_class = self.is_inside_class(func_idx);
350 let in_strict = in_class
351 || self.ctx.compiler_options.always_strict
352 || self.has_use_strict_directive(func_idx);
353
354 if !in_strict {
355 return;
356 }
357
358 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
360 let error_node = self
361 .ctx
362 .arena
363 .get(func_idx)
364 .and_then(|n| self.ctx.arena.get_function(n))
365 .map(|f| f.name)
366 .filter(|n| n.is_some())
367 .unwrap_or(func_idx);
368 let (pos, len) = self
369 .ctx
370 .arena
371 .get(error_node)
372 .map_or((0, 0), |n| (n.pos, n.end - n.pos));
373 if in_class {
374 self.ctx.error(
375 pos,
376 len,
377 diagnostic_messages::FUNCTION_DECLARATIONS_ARE_NOT_ALLOWED_INSIDE_BLOCKS_IN_STRICT_MODE_WHEN_TARGETIN_2
378 .to_string(),
379 diagnostic_codes::FUNCTION_DECLARATIONS_ARE_NOT_ALLOWED_INSIDE_BLOCKS_IN_STRICT_MODE_WHEN_TARGETIN_2,
380 );
381 } else {
382 self.ctx.error(
383 pos,
384 len,
385 diagnostic_messages::FUNCTION_DECLARATIONS_ARE_NOT_ALLOWED_INSIDE_BLOCKS_IN_STRICT_MODE_WHEN_TARGETIN
386 .to_string(),
387 diagnostic_codes::FUNCTION_DECLARATIONS_ARE_NOT_ALLOWED_INSIDE_BLOCKS_IN_STRICT_MODE_WHEN_TARGETIN,
388 );
389 }
390 }
391
392 fn is_inside_class(&self, node_idx: NodeIndex) -> bool {
394 let mut current = node_idx;
395 while let Some(ext) = self.ctx.arena.get_extended(current) {
396 let parent_idx = ext.parent;
397 if parent_idx.is_none() {
398 return false;
399 }
400 let Some(parent) = self.ctx.arena.get(parent_idx) else {
401 return false;
402 };
403 if parent.kind == syntax_kind_ext::CLASS_DECLARATION
404 || parent.kind == syntax_kind_ext::CLASS_EXPRESSION
405 {
406 return true;
407 }
408 current = parent_idx;
409 }
410 false
411 }
412
413 fn has_use_strict_directive(&self, node_idx: NodeIndex) -> bool {
416 let mut current = node_idx;
417 while let Some(ext) = self.ctx.arena.get_extended(current) {
418 let parent_idx = ext.parent;
419 if parent_idx.is_none() {
420 return false;
421 }
422 let Some(parent) = self.ctx.arena.get(parent_idx) else {
423 return false;
424 };
425
426 match parent.kind {
428 k if k == syntax_kind_ext::SOURCE_FILE => {
429 return self.source_file_has_use_strict(parent_idx);
430 }
431 k if k == syntax_kind_ext::FUNCTION_DECLARATION
432 || k == syntax_kind_ext::FUNCTION_EXPRESSION
433 || k == syntax_kind_ext::ARROW_FUNCTION =>
434 {
435 if let Some(func) = self.ctx.arena.get_function(parent)
436 && self.block_has_use_strict(func.body)
437 {
438 return true;
439 }
440 }
442 _ => {}
443 }
444 current = parent_idx;
445 }
446 false
447 }
448
449 fn source_file_has_use_strict(&self, sf_idx: NodeIndex) -> bool {
451 let Some(node) = self.ctx.arena.get(sf_idx) else {
452 return false;
453 };
454 let Some(sf) = self.ctx.arena.get_source_file(node) else {
455 return false;
456 };
457 self.statements_have_use_strict(&sf.statements.nodes)
458 }
459
460 fn block_has_use_strict(&self, block_idx: NodeIndex) -> bool {
462 let Some(node) = self.ctx.arena.get(block_idx) else {
463 return false;
464 };
465 let Some(block) = self.ctx.arena.get_block(node) else {
466 return false;
467 };
468 self.statements_have_use_strict(&block.statements.nodes)
469 }
470
471 fn statements_have_use_strict(&self, stmts: &[NodeIndex]) -> bool {
473 for &stmt_idx in stmts {
474 let Some(stmt) = self.ctx.arena.get(stmt_idx) else {
475 continue;
476 };
477 if stmt.kind != syntax_kind_ext::EXPRESSION_STATEMENT {
478 break; }
480 let Some(expr_stmt) = self.ctx.arena.get_expression_statement(stmt) else {
481 break;
482 };
483 let Some(expr) = self.ctx.arena.get(expr_stmt.expression) else {
484 break;
485 };
486 if expr.kind == SyntaxKind::StringLiteral as u16 {
487 if let Some(lit) = self.ctx.arena.get_literal(expr)
488 && lit.text == "use strict"
489 {
490 return true;
491 }
492 } else {
493 break; }
495 }
496 false
497 }
498
499 pub fn check_class_declaration(&mut self, class_idx: NodeIndex) {
503 let Some(node) = self.ctx.arena.get(class_idx) else {
504 return;
505 };
506 let Some(class_decl) = self.ctx.arena.get_class(node) else {
507 return;
508 };
509
510 self.check_property_initialization(class_idx, class_decl);
512 }
513
514 const fn check_property_initialization(
521 &mut self,
522 _class_idx: NodeIndex,
523 _class_decl: &tsz_parser::parser::node::ClassData,
524 ) {
525 }
530
531 pub const fn check_interface_declaration(&mut self, _iface_idx: NodeIndex) {
533 }
539
540 pub const fn check_type_alias_declaration(&mut self, _alias_idx: NodeIndex) {
542 }
548
549 pub fn check_enum_declaration(&mut self, enum_idx: NodeIndex) {
551 use crate::diagnostics::diagnostic_codes;
552 use tsz_parser::parser::node::NodeAccess;
553 use tsz_scanner::SyntaxKind;
554
555 let Some(node) = self.ctx.arena.get(enum_idx) else {
556 return;
557 };
558
559 let Some(enum_data) = self.ctx.arena.get_enum(node) else {
560 return;
561 };
562
563 if let Some(name_text) = self.ctx.arena.get_identifier_text(enum_data.name) {
565 match name_text {
566 "any" | "unknown" | "never" | "number" | "bigint" | "boolean" | "string"
567 | "symbol" | "void" | "object" | "undefined" => {
568 let name_node = self.ctx.arena.get(enum_data.name).unwrap();
569 self.ctx.error(
570 name_node.pos,
571 name_node.end - name_node.pos,
572 format!("Enum name cannot be '{name_text}'."),
573 diagnostic_codes::ENUM_NAME_CANNOT_BE,
574 );
575 }
576 _ => {}
577 }
578 }
579
580 for &member_idx in &enum_data.members.nodes {
582 if let Some(member_node) = self.ctx.arena.get(member_idx)
583 && let Some(member_data) = self.ctx.arena.get_enum_member(member_node)
584 && let Some(name_node) = self.ctx.arena.get(member_data.name)
585 && (name_node.kind == SyntaxKind::NumericLiteral as u16
586 || name_node.kind == SyntaxKind::BigIntLiteral as u16)
587 {
588 self.ctx.error(
589 name_node.pos,
590 name_node.end - name_node.pos,
591 "An enum member cannot have a numeric name.".to_string(),
592 diagnostic_codes::AN_ENUM_MEMBER_CANNOT_HAVE_A_NUMERIC_NAME,
593 );
594 }
595 }
596
597 let is_ambient = self
599 .ctx
600 .arena
601 .has_modifier(&enum_data.modifiers, SyntaxKind::DeclareKeyword);
602
603 if is_ambient {
604 for &member_idx in &enum_data.members.nodes {
606 if let Some(member_node) = self.ctx.arena.get(member_idx)
607 && let Some(member_data) = self.ctx.arena.get_enum_member(member_node)
608 && member_data.initializer.is_some()
609 {
610 if !self.is_constant_expression(member_data.initializer)
612 && let Some(init_node) = self.ctx.arena.get(member_data.initializer)
613 {
614 self.ctx.error(
615 init_node.pos,
616 init_node.end - init_node.pos,
617 "In ambient enum declarations member initializer must be constant expression.".to_string(),
618 diagnostic_codes::IN_AMBIENT_ENUM_DECLARATIONS_MEMBER_INITIALIZER_MUST_BE_CONSTANT_EXPRESSION,
619 );
620 }
621 }
622 }
623 }
624
625 let mut auto_incrementable = true;
627 for &member_idx in &enum_data.members.nodes {
628 if let Some(member_node) = self.ctx.arena.get(member_idx)
629 && let Some(member_data) = self.ctx.arena.get_enum_member(member_node)
630 {
631 if member_data.initializer.is_none() {
632 if !auto_incrementable {
633 let name_node = self.ctx.arena.get(member_data.name).unwrap_or(member_node);
634 self.ctx.error(
635 name_node.pos,
636 name_node.end - name_node.pos,
637 "Enum member must have initializer.".to_string(),
638 diagnostic_codes::ENUM_MEMBER_MUST_HAVE_INITIALIZER,
639 );
640 }
641 auto_incrementable = true;
642 } else {
643 auto_incrementable =
644 self.is_numeric_constant_enum_expr(member_data.initializer, enum_data, 0);
645 }
646
647 if member_data.initializer.is_some()
649 && let Some(member_name) = self.ctx.arena.get_identifier_text(member_data.name)
650 {
651 let enum_name_text = self.ctx.arena.get_identifier_text(enum_data.name);
652 self.check_enum_member_self_reference(
653 member_data.initializer,
654 member_name,
655 enum_name_text,
656 );
657 }
658 }
659 }
660 }
661
662 fn is_constant_expression(&self, expr_idx: NodeIndex) -> bool {
673 if expr_idx.is_none() {
674 return true;
675 }
676
677 let Some(node) = self.ctx.arena.get(expr_idx) else {
678 return false;
679 };
680
681 use tsz_scanner::SyntaxKind;
682
683 match node.kind {
684 k if k == SyntaxKind::NumericLiteral as u16 => true,
686 k if k == SyntaxKind::StringLiteral as u16 => true,
687 k if k == SyntaxKind::NoSubstitutionTemplateLiteral as u16 => true,
688 k if k == SyntaxKind::TrueKeyword as u16 => true,
689 k if k == SyntaxKind::FalseKeyword as u16 => true,
690 k if k == SyntaxKind::NullKeyword as u16 => true,
691
692 k if k == SyntaxKind::Identifier as u16 => true,
694
695 k if k == tsz_parser::parser::syntax_kind_ext::TEMPLATE_EXPRESSION => {
697 if let Some(template) = self.ctx.arena.get_template_expr(node) {
698 for &span_idx in &template.template_spans.nodes {
699 if let Some(span_node) = self.ctx.arena.get(span_idx)
700 && let Some(span) = self.ctx.arena.get_template_span(span_node)
701 && !self.is_constant_expression(span.expression)
702 {
703 return false;
704 }
705 }
706 true
707 } else {
708 false
709 }
710 }
711
712 _ => {
714 if let Some(unary) = self.ctx.arena.get_unary_expr(node) {
716 return self.is_constant_expression(unary.operand);
717 }
718
719 if let Some(binary) = self.ctx.arena.get_binary_expr(node) {
721 return self.is_constant_expression(binary.left)
722 && self.is_constant_expression(binary.right);
723 }
724
725 if let Some(paren) = self.ctx.arena.get_parenthesized(node) {
727 return self.is_constant_expression(paren.expression);
728 }
729
730 false
732 }
733 }
734 }
735
736 fn is_numeric_constant_enum_expr(
737 &self,
738 expr_idx: NodeIndex,
739 enum_data: &tsz_parser::parser::node::EnumData,
740 depth: u32,
741 ) -> bool {
742 if depth > 100 {
743 return false;
744 }
745 if expr_idx.is_none() {
746 return true;
747 }
748
749 let Some(node) = self.ctx.arena.get(expr_idx) else {
750 return false;
751 };
752
753 use tsz_parser::parser::node::NodeAccess;
754 use tsz_parser::parser::syntax_kind_ext;
755 use tsz_scanner::SyntaxKind;
756
757 match node.kind {
758 k if k == SyntaxKind::NumericLiteral as u16 => true,
759 k if k == SyntaxKind::Identifier as u16 => {
760 if let Some(name_text) = self.ctx.arena.get_identifier_text(expr_idx) {
761 for &member_idx in &enum_data.members.nodes {
762 if let Some(member_node) = self.ctx.arena.get(member_idx)
763 && let Some(member_data) = self.ctx.arena.get_enum_member(member_node)
764 && let Some(member_name_text) =
765 self.ctx.arena.get_identifier_text(member_data.name)
766 && member_name_text == name_text
767 {
768 if member_data.initializer.is_none() {
769 return true;
770 } else {
771 return self.is_numeric_constant_enum_expr(
772 member_data.initializer,
773 enum_data,
774 depth + 1,
775 );
776 }
777 }
778 }
779 }
780 false
781 }
782 k if k == syntax_kind_ext::PREFIX_UNARY_EXPRESSION => {
783 if let Some(unary) = self.ctx.arena.get_unary_expr(node) {
784 self.is_numeric_constant_enum_expr(unary.operand, enum_data, depth + 1)
785 } else {
786 false
787 }
788 }
789 k if k == syntax_kind_ext::BINARY_EXPRESSION => {
790 if let Some(binary) = self.ctx.arena.get_binary_expr(node) {
791 self.is_numeric_constant_enum_expr(binary.left, enum_data, depth + 1)
792 && self.is_numeric_constant_enum_expr(binary.right, enum_data, depth + 1)
793 } else {
794 false
795 }
796 }
797 k if k == syntax_kind_ext::PARENTHESIZED_EXPRESSION => {
798 if let Some(paren) = self.ctx.arena.get_parenthesized(node) {
799 self.is_numeric_constant_enum_expr(paren.expression, enum_data, depth + 1)
800 } else {
801 false
802 }
803 }
804 _ => false,
805 }
806 }
807
808 pub(crate) fn check_statement_in_ambient_context(&mut self, stmt_idx: NodeIndex) {
815 let Some(node) = self.ctx.arena.get(stmt_idx) else {
816 return;
817 };
818
819 let is_non_declaration = matches!(
821 node.kind,
822 k if k == syntax_kind_ext::EXPRESSION_STATEMENT
823 || k == syntax_kind_ext::IF_STATEMENT
824 || k == syntax_kind_ext::DO_STATEMENT
825 || k == syntax_kind_ext::WHILE_STATEMENT
826 || k == syntax_kind_ext::FOR_STATEMENT
827 || k == syntax_kind_ext::FOR_IN_STATEMENT
828 || k == syntax_kind_ext::FOR_OF_STATEMENT
829 || k == syntax_kind_ext::BREAK_STATEMENT
830 || k == syntax_kind_ext::CONTINUE_STATEMENT
831 || k == syntax_kind_ext::RETURN_STATEMENT
832 || k == syntax_kind_ext::WITH_STATEMENT
833 || k == syntax_kind_ext::SWITCH_STATEMENT
834 || k == syntax_kind_ext::THROW_STATEMENT
835 || k == syntax_kind_ext::TRY_STATEMENT
836 || k == syntax_kind_ext::DEBUGGER_STATEMENT
837 || k == syntax_kind_ext::EMPTY_STATEMENT
838 );
839
840 if is_non_declaration {
841 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
842 if let Some((pos, end)) = self.ctx.get_node_span(stmt_idx) {
843 self.ctx.error(
844 pos,
845 end - pos,
846 diagnostic_messages::STATEMENTS_ARE_NOT_ALLOWED_IN_AMBIENT_CONTEXTS.to_string(),
847 diagnostic_codes::STATEMENTS_ARE_NOT_ALLOWED_IN_AMBIENT_CONTEXTS,
848 );
849 }
850 }
851
852 if node.kind == syntax_kind_ext::CONTINUE_STATEMENT {
854 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
855 if let Some((pos, end)) = self.ctx.get_node_span(stmt_idx) {
856 self.ctx.error(
857 pos,
858 end - pos,
859 diagnostic_messages::A_CONTINUE_STATEMENT_CAN_ONLY_BE_USED_WITHIN_AN_ENCLOSING_ITERATION_STATEMENT.to_string(),
860 diagnostic_codes::A_CONTINUE_STATEMENT_CAN_ONLY_BE_USED_WITHIN_AN_ENCLOSING_ITERATION_STATEMENT,
861 );
862 }
863 }
864
865 if node.kind == syntax_kind_ext::RETURN_STATEMENT {
866 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
867 if let Some((pos, end)) = self.ctx.get_node_span(stmt_idx) {
868 self.ctx.error(
869 pos,
870 end - pos,
871 diagnostic_messages::A_RETURN_STATEMENT_CAN_ONLY_BE_USED_WITHIN_A_FUNCTION_BODY
872 .to_string(),
873 diagnostic_codes::A_RETURN_STATEMENT_CAN_ONLY_BE_USED_WITHIN_A_FUNCTION_BODY,
874 );
875 }
876 }
877
878 if node.kind == syntax_kind_ext::WITH_STATEMENT {
879 self.check_with_statement(stmt_idx);
880 }
881
882 if node.kind == syntax_kind_ext::VARIABLE_STATEMENT {
884 self.check_ambient_variable_type_annotations_for_index_signatures(stmt_idx);
885 }
886
887 if node.kind == syntax_kind_ext::LABELED_STATEMENT
889 && let Some(labeled) = self.ctx.arena.get_labeled_statement(node)
890 {
891 self.check_label_on_declaration(labeled.label, labeled.statement);
892 self.check_statement_in_ambient_context(labeled.statement);
893 }
894 }
895
896 fn check_ambient_variable_type_annotations_for_index_signatures(
897 &mut self,
898 stmt_idx: NodeIndex,
899 ) {
900 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
901
902 let Some(stmt_node) = self.ctx.arena.get(stmt_idx) else {
903 return;
904 };
905 let Some(var_stmt) = self.ctx.arena.get_variable(stmt_node) else {
906 return;
907 };
908
909 for &list_idx in &var_stmt.declarations.nodes {
910 let Some(list_node) = self.ctx.arena.get(list_idx) else {
911 continue;
912 };
913 let Some(decl_list) = self.ctx.arena.get_variable(list_node) else {
914 continue;
915 };
916 for &decl_idx in &decl_list.declarations.nodes {
917 let Some(decl_node) = self.ctx.arena.get(decl_idx) else {
918 continue;
919 };
920 let Some(var_decl) = self.ctx.arena.get_variable_declaration(decl_node) else {
921 continue;
922 };
923 if var_decl.type_annotation.is_none() {
924 continue;
925 }
926 let Some(type_node) = self.ctx.arena.get(var_decl.type_annotation) else {
927 continue;
928 };
929 if type_node.kind != syntax_kind_ext::TYPE_LITERAL {
930 continue;
931 }
932 let Some(type_lit) = self.ctx.arena.get_type_literal(type_node) else {
933 continue;
934 };
935 for &member_idx in &type_lit.members.nodes {
936 let Some(member_node) = self.ctx.arena.get(member_idx) else {
937 continue;
938 };
939 let Some(index_sig) = self.ctx.arena.get_index_signature(member_node) else {
940 continue;
941 };
942 let Some(¶m_idx) = index_sig.parameters.nodes.first() else {
943 continue;
944 };
945 let Some(param_node) = self.ctx.arena.get(param_idx) else {
946 continue;
947 };
948 let Some(param) = self.ctx.arena.get_parameter(param_node) else {
949 continue;
950 };
951 if param.type_annotation.is_none() {
952 continue;
953 }
954 let Some(type_node) = self.ctx.arena.get(param.type_annotation) else {
955 continue;
956 };
957 let is_valid = type_node.kind == tsz_scanner::SyntaxKind::StringKeyword as u16
958 || type_node.kind == tsz_scanner::SyntaxKind::NumberKeyword as u16
959 || type_node.kind == tsz_scanner::SyntaxKind::SymbolKeyword as u16
960 || type_node.kind == syntax_kind_ext::TEMPLATE_LITERAL_TYPE;
961 if !is_valid && let Some((pos, end)) = self.ctx.get_node_span(param_idx) {
962 self.ctx.error(
963 pos,
964 end - pos,
965 diagnostic_messages::AN_INDEX_SIGNATURE_PARAMETER_TYPE_MUST_BE_STRING_NUMBER_SYMBOL_OR_A_TEMPLATE_LIT.to_string(),
966 diagnostic_codes::AN_INDEX_SIGNATURE_PARAMETER_TYPE_MUST_BE_STRING_NUMBER_SYMBOL_OR_A_TEMPLATE_LIT,
967 );
968 }
969 }
970 }
971 }
972 }
973
974 fn is_strict_mode_for_node(&self, idx: NodeIndex) -> bool {
975 self.ctx.is_strict_mode_for_node(idx)
976 }
977
978 fn check_with_statement(&mut self, stmt_idx: NodeIndex) {
979 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
980
981 if let Some((pos, end)) = self.ctx.get_node_span(stmt_idx) {
982 self.ctx.error(
983 pos,
984 end - pos,
985 diagnostic_messages::THE_WITH_STATEMENT_IS_NOT_SUPPORTED_ALL_SYMBOLS_IN_A_WITH_BLOCK_WILL_HAVE_TYPE_A.to_string(),
986 diagnostic_codes::THE_WITH_STATEMENT_IS_NOT_SUPPORTED_ALL_SYMBOLS_IN_A_WITH_BLOCK_WILL_HAVE_TYPE_A,
987 );
988
989 if self.is_strict_mode_for_node(stmt_idx) {
990 self.ctx.error(
991 pos,
992 end - pos,
993 diagnostic_messages::WITH_STATEMENTS_ARE_NOT_ALLOWED_IN_STRICT_MODE.to_string(),
994 diagnostic_codes::WITH_STATEMENTS_ARE_NOT_ALLOWED_IN_STRICT_MODE,
995 );
996 }
997 }
998 }
999
1000 fn check_label_on_declaration(&mut self, label_idx: NodeIndex, statement_idx: NodeIndex) {
1001 if !self.ctx.compiler_options.target.supports_es2015() {
1002 return;
1003 }
1004 if !self.is_strict_mode_for_node(label_idx) {
1005 return;
1006 }
1007
1008 let Some(stmt_node) = self.ctx.arena.get(statement_idx) else {
1009 return;
1010 };
1011
1012 let is_declaration_or_variable = matches!(
1013 stmt_node.kind,
1014 syntax_kind_ext::FUNCTION_DECLARATION
1015 | syntax_kind_ext::CLASS_DECLARATION
1016 | syntax_kind_ext::INTERFACE_DECLARATION
1017 | syntax_kind_ext::TYPE_ALIAS_DECLARATION
1018 | syntax_kind_ext::ENUM_DECLARATION
1019 | syntax_kind_ext::MODULE_DECLARATION
1020 | syntax_kind_ext::IMPORT_DECLARATION
1021 | syntax_kind_ext::EXPORT_DECLARATION
1022 | syntax_kind_ext::VARIABLE_STATEMENT
1023 );
1024
1025 if is_declaration_or_variable && let Some((pos, end)) = self.ctx.get_node_span(label_idx) {
1026 self.ctx.error(
1027 pos,
1028 end - pos,
1029 "'A label is not allowed here.".to_string(),
1030 1344, );
1032 }
1033 }
1034
1035 pub fn check_parameter_properties(&mut self, parameters: &[NodeIndex]) {
1037 use crate::diagnostics::diagnostic_codes;
1038
1039 for ¶m_idx in parameters {
1040 let Some(node) = self.ctx.arena.get(param_idx) else {
1041 continue;
1042 };
1043
1044 if let Some(param) = self.ctx.arena.get_parameter(node) {
1045 let has_prop_modifier = if let Some(ref mods) = param.modifiers {
1049 mods.nodes.iter().any(|&mod_idx| {
1050 self.ctx.arena.get(mod_idx).is_some_and(|m| {
1051 use tsz_scanner::SyntaxKind;
1052 m.kind == SyntaxKind::PublicKeyword as u16
1053 || m.kind == SyntaxKind::PrivateKeyword as u16
1054 || m.kind == SyntaxKind::ProtectedKeyword as u16
1055 || m.kind == SyntaxKind::ReadonlyKeyword as u16
1056 })
1057 })
1058 } else {
1059 false
1060 };
1061 if has_prop_modifier && let Some((pos, end)) = self.ctx.get_node_span(param_idx) {
1062 self.ctx.error(
1063 pos,
1064 end - pos,
1065 "A parameter property is only allowed in a constructor implementation."
1066 .to_string(),
1067 diagnostic_codes::A_PARAMETER_PROPERTY_IS_ONLY_ALLOWED_IN_A_CONSTRUCTOR_IMPLEMENTATION,
1068 );
1069 }
1070 }
1071 }
1072 }
1073
1074 pub const fn check_function_implementations(&mut self, _nodes: &[NodeIndex]) {
1076 }
1079 fn check_enum_member_self_reference(
1080 &mut self,
1081 expr_idx: NodeIndex,
1082 member_name: &str,
1083 enum_name: Option<&str>,
1084 ) {
1085 if expr_idx.is_none() {
1086 return;
1087 }
1088 let Some(node) = self.ctx.arena.get(expr_idx) else {
1089 return;
1090 };
1091
1092 use crate::diagnostics::diagnostic_codes;
1093 use tsz_parser::parser::node::NodeAccess;
1094 use tsz_parser::parser::syntax_kind_ext;
1095 use tsz_scanner::SyntaxKind;
1096
1097 match node.kind {
1098 k if k == SyntaxKind::Identifier as u16 => {
1099 if let Some(text) = self.ctx.arena.get_identifier_text(expr_idx)
1100 && text == member_name
1101 {
1102 self.ctx.error(
1103 node.pos,
1104 node.end - node.pos,
1105 format!("Property '{text}' is used before being assigned."),
1106 diagnostic_codes::PROPERTY_IS_USED_BEFORE_BEING_ASSIGNED,
1107 );
1108 }
1109 }
1110 k if k == syntax_kind_ext::PROPERTY_ACCESS_EXPRESSION => {
1111 if let Some(prop) = self.ctx.arena.get_access_expr(node)
1112 && let Some(left_node) = self.ctx.arena.get(prop.expression)
1113 {
1114 let is_enum_ref = if left_node.kind == SyntaxKind::Identifier as u16 {
1115 if let Some(text) = self.ctx.arena.get_identifier_text(prop.expression) {
1116 Some(text) == enum_name
1117 } else {
1118 false
1119 }
1120 } else {
1121 false
1122 };
1123
1124 if is_enum_ref {
1125 if let Some(right_text) =
1126 self.ctx.arena.get_identifier_text(prop.name_or_argument)
1127 && right_text == member_name
1128 {
1129 self.ctx.error(
1130 node.pos,
1131 node.end - node.pos,
1132 format!("Property '{right_text}' is used before being assigned."),
1133 diagnostic_codes::PROPERTY_IS_USED_BEFORE_BEING_ASSIGNED,
1134 );
1135 }
1136 } else {
1137 self.check_enum_member_self_reference(
1138 prop.expression,
1139 member_name,
1140 enum_name,
1141 );
1142 }
1143 }
1144 }
1145 k if k == syntax_kind_ext::ELEMENT_ACCESS_EXPRESSION => {
1146 if let Some(elem) = self.ctx.arena.get_access_expr(node)
1147 && let Some(left_node) = self.ctx.arena.get(elem.expression)
1148 {
1149 let is_enum_ref = if left_node.kind == SyntaxKind::Identifier as u16 {
1150 if let Some(text) = self.ctx.arena.get_identifier_text(elem.expression) {
1151 Some(text) == enum_name
1152 } else {
1153 false
1154 }
1155 } else {
1156 false
1157 };
1158
1159 if is_enum_ref {
1160 if let Some(right_node) = self.ctx.arena.get(elem.name_or_argument) {
1161 if right_node.kind == SyntaxKind::StringLiteral as u16 {
1162 if let Some(lit) = self.ctx.arena.get_literal(right_node)
1163 && lit.text == member_name
1164 {
1165 self.ctx.error(
1166 node.pos,
1167 node.end - node.pos,
1168 format!(
1169 "Property '{}' is used before being assigned.",
1170 lit.text
1171 ),
1172 diagnostic_codes::PROPERTY_IS_USED_BEFORE_BEING_ASSIGNED,
1173 );
1174 }
1175 } else {
1176 self.check_enum_member_self_reference(
1177 elem.name_or_argument,
1178 member_name,
1179 enum_name,
1180 );
1181 }
1182 }
1183 } else {
1184 self.check_enum_member_self_reference(
1185 elem.expression,
1186 member_name,
1187 enum_name,
1188 );
1189 self.check_enum_member_self_reference(
1190 elem.name_or_argument,
1191 member_name,
1192 enum_name,
1193 );
1194 }
1195 }
1196 }
1197 k if k == syntax_kind_ext::PREFIX_UNARY_EXPRESSION => {
1198 if let Some(unary) = self.ctx.arena.get_unary_expr(node) {
1199 self.check_enum_member_self_reference(unary.operand, member_name, enum_name);
1200 }
1201 }
1202 k if k == syntax_kind_ext::POSTFIX_UNARY_EXPRESSION => {
1203 if let Some(unary) = self.ctx.arena.get_unary_expr(node) {
1204 self.check_enum_member_self_reference(unary.operand, member_name, enum_name);
1205 }
1206 }
1207 k if k == syntax_kind_ext::BINARY_EXPRESSION => {
1208 if let Some(bin) = self.ctx.arena.get_binary_expr(node) {
1209 self.check_enum_member_self_reference(bin.left, member_name, enum_name);
1210 self.check_enum_member_self_reference(bin.right, member_name, enum_name);
1211 }
1212 }
1213 k if k == syntax_kind_ext::PARENTHESIZED_EXPRESSION => {
1214 if let Some(paren) = self.ctx.arena.get_parenthesized(node) {
1215 self.check_enum_member_self_reference(paren.expression, member_name, enum_name);
1216 }
1217 }
1218 k if k == syntax_kind_ext::CONDITIONAL_EXPRESSION => {
1219 if let Some(cond) = self.ctx.arena.get_conditional_expr(node) {
1220 self.check_enum_member_self_reference(cond.condition, member_name, enum_name);
1221 self.check_enum_member_self_reference(cond.when_true, member_name, enum_name);
1222 self.check_enum_member_self_reference(cond.when_false, member_name, enum_name);
1223 }
1224 }
1225 _ => {}
1226 }
1227 }
1228}
1229
1230#[cfg(test)]
1231#[path = "../../tests/declarations.rs"]
1232mod tests;