1use crate::state::CheckerState;
4use crate::statements::StatementChecker;
5use tracing::{Level, span};
6use tsz_binder::symbol_flags;
7use tsz_parser::parser::NodeIndex;
8use tsz_parser::parser::node::NodeAccess;
9use tsz_parser::parser::syntax_kind_ext;
10use tsz_scanner::SyntaxKind;
11use web_time::Instant;
12
13pub(crate) fn is_strict_mode_reserved_name(name: &str) -> bool {
16 matches!(
17 name,
18 "implements"
19 | "interface"
20 | "let"
21 | "package"
22 | "private"
23 | "protected"
24 | "public"
25 | "static"
26 | "yield"
27 )
28}
29
30impl<'a> CheckerState<'a> {
31 fn needs_boxed_type_registration(&self) -> bool {
32 for idx in 0..self.ctx.arena.len() {
33 let node_idx = NodeIndex(idx as u32);
34 let Some(node) = self.ctx.arena.get(node_idx) else {
35 continue;
36 };
37 if node.kind == syntax_kind_ext::PROPERTY_ACCESS_EXPRESSION
38 || node.kind == syntax_kind_ext::ELEMENT_ACCESS_EXPRESSION
39 {
40 return true;
41 }
42 }
43 false
44 }
45
46 pub(crate) fn check_strict_mode_reserved_name_at(
49 &mut self,
50 name_idx: tsz_parser::parser::NodeIndex,
51 context_node: tsz_parser::parser::NodeIndex,
52 ) {
53 if name_idx.is_none() || !self.is_strict_mode_for_node(context_node) {
54 return;
55 }
56 let Some(name_node) = self.ctx.arena.get(name_idx) else {
57 return;
58 };
59 let Some(ident) = self.ctx.arena.get_identifier(name_node) else {
60 return;
61 };
62 if !is_strict_mode_reserved_name(&ident.escaped_text) {
63 return;
64 }
65 use crate::diagnostics::{diagnostic_codes, diagnostic_messages, format_message};
66 if self.ctx.enclosing_class.is_some() {
67 let message = format_message(
68 diagnostic_messages::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_IN_STRICT_MODE_CLASS_DEFINITIONS_ARE_AUTO,
69 &[&ident.escaped_text],
70 );
71 self.error_at_node(
72 name_idx,
73 &message,
74 diagnostic_codes::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_IN_STRICT_MODE_CLASS_DEFINITIONS_ARE_AUTO,
75 );
76 } else if self.ctx.binder.is_external_module() {
77 let message = format_message(
78 diagnostic_messages::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_IN_STRICT_MODE_MODULES_ARE_AUTOMATICALLY,
79 &[&ident.escaped_text],
80 );
81 self.error_at_node(
82 name_idx,
83 &message,
84 diagnostic_codes::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_IN_STRICT_MODE_MODULES_ARE_AUTOMATICALLY,
85 );
86 } else {
87 let message = format_message(
88 diagnostic_messages::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_IN_STRICT_MODE,
89 &[&ident.escaped_text],
90 );
91 self.error_at_node(
92 name_idx,
93 &message,
94 diagnostic_codes::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_IN_STRICT_MODE,
95 );
96 }
97 }
98
99 pub fn check_source_file(&mut self, root_idx: NodeIndex) {
151 let _span = span!(Level::INFO, "check_source_file", idx = ?root_idx).entered();
152
153 self.ctx.is_in_ambient_declaration_file = false;
155
156 let Some(node) = self.ctx.arena.get(root_idx) else {
157 return;
158 };
159
160 if let Some(sf) = self.ctx.arena.get_source_file(node) {
161 self.resolve_compiler_options_from_source(&sf.text);
162 if self.has_ts_nocheck_pragma(&sf.text) {
163 return;
164 }
165
166 self.ctx.application_symbols_resolved.clear();
168 self.ctx.application_symbols_resolution_set.clear();
169
170 let env_start = Instant::now();
174 let populated_env = self.build_type_environment();
175 tracing::trace!(target: "wasm::perf", phase = "build_type_environment", ms = env_start.elapsed().as_secs_f64() * 1000.0);
176 *self.ctx.type_env.borrow_mut() = populated_env.clone();
177 *self.ctx.type_environment.borrow_mut() = populated_env;
180
181 if self.needs_boxed_type_registration() {
186 self.register_boxed_types();
187 }
188
189 self.ctx.is_checking_statements = true;
194 let stmt_start = Instant::now();
195
196 let is_dts = self.ctx.file_name.ends_with(".d.ts")
200 || self.ctx.file_name.ends_with(".d.tsx")
201 || self.ctx.file_name.ends_with(".d.mts")
202 || self.ctx.file_name.ends_with(".d.cts");
203 if is_dts {
204 self.ctx.is_in_ambient_declaration_file = true;
205 }
206
207 let prev_unreachable = self.ctx.is_unreachable;
208 let prev_reported = self.ctx.has_reported_unreachable;
209 for &stmt_idx in &sf.statements.nodes {
210 if is_dts {
211 self.check_dts_statement_in_ambient_context(stmt_idx);
212 }
213 self.check_statement(stmt_idx);
214 if !self.statement_falls_through(stmt_idx) {
215 self.ctx.is_unreachable = true;
216 }
217 }
218 self.ctx.is_unreachable = prev_unreachable;
219 self.ctx.has_reported_unreachable = prev_reported;
220
221 self.check_reserved_await_identifier_in_module(root_idx);
222
223 tracing::trace!(target: "wasm::perf", phase = "check_statements", ms = stmt_start.elapsed().as_secs_f64() * 1000.0);
224
225 let post_start = Instant::now();
226 self.check_function_implementations(&sf.statements.nodes);
228
229 self.check_export_assignment(&sf.statements.nodes);
231
232 self.check_circular_import_aliases();
234
235 if matches!(
237 self.ctx.compiler_options.module,
238 tsz_common::common::ModuleKind::None
239 ) && !is_dts
240 {
241 self.check_module_none_statements(&sf.statements.nodes);
242 }
243
244 self.check_duplicate_identifiers();
246
247 self.check_missing_global_types();
250
251 if !self.ctx.compiler_options.no_resolve {
253 self.check_triple_slash_references(&sf.file_name, &sf.text);
254 }
255
256 self.check_amd_module_names(&sf.text);
258
259 if self.ctx.no_unused_locals() || self.ctx.no_unused_parameters() {
261 self.check_unused_declarations();
262 }
263 if self.is_js_file() {
265 let js_start = Instant::now();
266 self.check_js_grammar_statements(&sf.statements.nodes);
267 tracing::trace!(target: "wasm::perf", phase = "check_js_grammar", ms = js_start.elapsed().as_secs_f64() * 1000.0);
268 }
269
270 tracing::trace!(target: "wasm::perf", phase = "post_checks", ms = post_start.elapsed().as_secs_f64() * 1000.0);
271 }
272 }
273
274 fn has_ts_nocheck_pragma(&self, source: &str) -> bool {
275 source
276 .lines()
277 .take(20)
278 .any(|line| line.contains("@ts-nocheck"))
279 }
280
281 fn check_js_grammar_statements(&mut self, statements: &[NodeIndex]) {
288 for &stmt_idx in statements {
289 self.check_js_grammar_statement(stmt_idx);
290 }
291 }
292
293 fn check_js_grammar_statement(&mut self, stmt_idx: NodeIndex) {
295 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
296
297 let Some(node) = self.ctx.arena.get(stmt_idx) else {
298 return;
299 };
300
301 match node.kind {
302 syntax_kind_ext::TYPE_ALIAS_DECLARATION => {
304 self.error_at_node(
305 stmt_idx,
306 diagnostic_messages::TYPE_ALIASES_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
307 diagnostic_codes::TYPE_ALIASES_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
308 );
309 }
310
311 syntax_kind_ext::INTERFACE_DECLARATION => {
314 let error_node = self
315 .ctx
316 .arena
317 .get_interface(node)
318 .map_or(stmt_idx, |i| i.name);
319 self.error_ts_only_declaration("interface", error_node);
320 }
321
322 syntax_kind_ext::ENUM_DECLARATION => {
323 let error_node = self.ctx.arena.get_enum(node).map_or(stmt_idx, |e| e.name);
324 self.error_ts_only_declaration("enum", error_node);
325 }
326
327 syntax_kind_ext::MODULE_DECLARATION => {
328 let keyword = self.get_module_keyword(stmt_idx, node);
329 let error_node = self.ctx.arena.get_module(node).map_or(stmt_idx, |m| m.name);
330 self.error_ts_only_declaration(keyword, error_node);
331 }
332
333 syntax_kind_ext::IMPORT_EQUALS_DECLARATION => {
335 self.error_at_node(
336 stmt_idx,
337 diagnostic_messages::IMPORT_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
338 diagnostic_codes::IMPORT_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
339 );
340 }
341
342 syntax_kind_ext::EXPORT_ASSIGNMENT => {
344 self.error_at_node(
345 stmt_idx,
346 diagnostic_messages::EXPORT_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
347 diagnostic_codes::EXPORT_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
348 );
349 }
350
351 syntax_kind_ext::FUNCTION_DECLARATION => {
353 self.check_js_grammar_function(stmt_idx, node);
354 }
355
356 syntax_kind_ext::CLASS_DECLARATION => {
358 self.check_js_grammar_class(stmt_idx, node);
359 }
360
361 syntax_kind_ext::VARIABLE_STATEMENT => {
363 self.check_js_grammar_variable_statement(stmt_idx, node);
364 }
365
366 syntax_kind_ext::EXPORT_DECLARATION => {
368 if let Some(export_decl) = self.ctx.arena.get_export_decl_at(stmt_idx)
369 && export_decl.export_clause.is_some()
370 {
371 self.check_js_grammar_statement(export_decl.export_clause);
372 }
373 }
374
375 syntax_kind_ext::EXPRESSION_STATEMENT => {
377 if let Some(expr_stmt) = self.ctx.arena.get_expression_statement(node) {
378 self.check_js_grammar_expression(expr_stmt.expression);
379 }
380 }
381
382 _ => {}
383 }
384 }
385
386 fn check_js_grammar_expression(&mut self, expr_idx: NodeIndex) {
387 let Some(node) = self.ctx.arena.get(expr_idx) else {
388 return;
389 };
390
391 if node.is_function_like() {
392 self.check_js_grammar_function(expr_idx, node);
393 }
394
395 for child_idx in self.ctx.arena.get_children(expr_idx) {
396 if child_idx.is_some() {
397 self.check_js_grammar_expression(child_idx);
398 }
399 }
400 }
401
402 pub(crate) fn check_js_grammar_function(
404 &mut self,
405 func_idx: NodeIndex,
406 node: &tsz_parser::parser::node::Node,
407 ) {
408 let Some(func) = self.ctx.arena.get_function(node) else {
409 return;
410 };
411
412 self.error_if_ts_only_modifier(&func.modifiers, SyntaxKind::DeclareKeyword, "declare");
413 self.error_if_ts_only_type_params(&func.type_parameters);
414 self.error_if_ts_only_type_annotation(func.type_annotation);
415
416 let is_overload = func.body.is_none() && node.kind == syntax_kind_ext::FUNCTION_DECLARATION;
418 self.error_if_ts_only_signature_without_body(is_overload, func_idx);
419
420 self.check_js_grammar_parameters(&func.parameters.nodes);
422 }
423
424 fn check_js_grammar_class(
426 &mut self,
427 _class_idx: NodeIndex,
428 node: &tsz_parser::parser::node::Node,
429 ) {
430 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
431
432 let Some(class) = self.ctx.arena.get_class(node) else {
433 return;
434 };
435
436 self.error_if_ts_only_type_params(&class.type_parameters);
437
438 if let Some(ref heritage_clauses) = class.heritage_clauses {
440 for &clause_idx in &heritage_clauses.nodes {
441 if let Some(clause_node) = self.ctx.arena.get(clause_idx)
442 && clause_node.kind == syntax_kind_ext::HERITAGE_CLAUSE
443 && let Some(heritage) = self.ctx.arena.get_heritage_clause(clause_node)
444 && heritage.token == SyntaxKind::ImplementsKeyword as u16
445 {
446 self.error_at_node(
447 clause_idx,
448 diagnostic_messages::IMPLEMENTS_CLAUSES_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
449 diagnostic_codes::IMPLEMENTS_CLAUSES_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
450 );
451 }
452 }
453 }
454
455 self.error_if_ts_only_modifier(&class.modifiers, SyntaxKind::AbstractKeyword, "abstract");
456 self.error_if_ts_only_modifier(&class.modifiers, SyntaxKind::DeclareKeyword, "declare");
457
458 for &member_idx in &class.members.nodes {
460 self.check_js_grammar_class_member(member_idx);
461 }
462 }
463
464 fn error_if_ts_only_modifier(
466 &mut self,
467 modifiers: &Option<tsz_parser::parser::NodeList>,
468 kind: SyntaxKind,
469 name: &str,
470 ) {
471 use crate::diagnostics::{diagnostic_codes, diagnostic_messages, format_message};
472
473 if self.has_modifier_kind(modifiers, kind) {
474 let message = format_message(
475 diagnostic_messages::THE_MODIFIER_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
476 &[name],
477 );
478 if let Some(mod_idx) = self.get_modifier_index(modifiers, kind as u16) {
479 self.error_at_node(
480 mod_idx,
481 &message,
482 diagnostic_codes::THE_MODIFIER_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
483 );
484 }
485 }
486 }
487
488 fn error_if_ts_only_type_annotation(&mut self, type_annotation: NodeIndex) {
490 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
491
492 if type_annotation.is_some() {
493 self.error_at_node(
494 type_annotation,
495 diagnostic_messages::TYPE_ANNOTATIONS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
496 diagnostic_codes::TYPE_ANNOTATIONS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
497 );
498 }
499 }
500
501 fn error_if_ts_only_type_params(
503 &mut self,
504 type_parameters: &Option<tsz_parser::parser::NodeList>,
505 ) {
506 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
507
508 if let Some(type_params) = type_parameters
509 && !type_params.nodes.is_empty()
510 {
511 self.error_at_node(
512 type_params.nodes[0],
513 diagnostic_messages::TYPE_PARAMETER_DECLARATIONS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
514 diagnostic_codes::TYPE_PARAMETER_DECLARATIONS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
515 );
516 }
517 }
518
519 fn error_if_ts_only_signature_without_body(&mut self, has_no_body: bool, node_idx: NodeIndex) {
521 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
522
523 if has_no_body {
524 self.error_at_node(
525 node_idx,
526 diagnostic_messages::SIGNATURE_DECLARATIONS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
527 diagnostic_codes::SIGNATURE_DECLARATIONS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
528 );
529 }
530 }
531
532 fn error_if_ts_only_optional(&mut self, has_question_token: bool, node_idx: NodeIndex) {
534 use crate::diagnostics::{diagnostic_codes, diagnostic_messages, format_message};
535
536 if has_question_token {
537 let message = format_message(
538 diagnostic_messages::THE_MODIFIER_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
539 &["?"],
540 );
541 self.error_at_node(
542 node_idx,
543 &message,
544 diagnostic_codes::THE_MODIFIER_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
545 );
546 }
547 }
548
549 fn error_ts_only_declaration(&mut self, keyword: &str, node_idx: NodeIndex) {
551 use crate::diagnostics::{diagnostic_codes, diagnostic_messages, format_message};
552
553 let message = format_message(
554 diagnostic_messages::DECLARATIONS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
555 &[keyword],
556 );
557 self.error_at_node(
558 node_idx,
559 &message,
560 diagnostic_codes::DECLARATIONS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
561 );
562 }
563
564 fn get_module_keyword(
566 &self,
567 node_idx: NodeIndex,
568 node: &tsz_parser::parser::node::Node,
569 ) -> &'static str {
570 let Some(module) = self.ctx.arena.get_module(node) else {
571 return "module";
572 };
573
574 let Some(name_node) = self.ctx.arena.get(module.name) else {
575 return "module";
576 };
577
578 if name_node.kind == SyntaxKind::StringLiteral as u16 {
580 return "module";
581 }
582
583 let node_text = self.node_text(node_idx).unwrap_or_default();
585 if node_text.starts_with("namespace") || node_text.contains("namespace ") {
586 "namespace"
587 } else {
588 "module"
589 }
590 }
591
592 pub(crate) fn check_js_grammar_class_member(&mut self, member_idx: NodeIndex) {
594 let Some(node) = self.ctx.arena.get(member_idx) else {
595 return;
596 };
597
598 match node.kind {
599 syntax_kind_ext::METHOD_DECLARATION => {
600 if let Some(method) = self.ctx.arena.get_method_decl(node) {
601 self.check_js_grammar_accessibility_modifier(&method.modifiers, member_idx);
602 self.error_if_ts_only_modifier(
603 &method.modifiers,
604 SyntaxKind::AbstractKeyword,
605 "abstract",
606 );
607 self.error_if_ts_only_modifier(
608 &method.modifiers,
609 SyntaxKind::OverrideKeyword,
610 "override",
611 );
612 self.error_if_ts_only_type_params(&method.type_parameters);
613 self.error_if_ts_only_type_annotation(method.type_annotation);
614 self.error_if_ts_only_signature_without_body(method.body.is_none(), member_idx);
615 self.error_if_ts_only_optional(method.question_token, member_idx);
616 self.check_js_grammar_parameters(&method.parameters.nodes);
617 }
618 }
619
620 syntax_kind_ext::CONSTRUCTOR => {
621 if let Some(ctor) = self.ctx.arena.get_constructor(node) {
622 self.check_js_grammar_accessibility_modifier(&ctor.modifiers, member_idx);
623 self.error_if_ts_only_signature_without_body(ctor.body.is_none(), member_idx);
624 self.check_js_grammar_parameters(&ctor.parameters.nodes);
625 }
626 }
627
628 syntax_kind_ext::PROPERTY_DECLARATION => {
629 if let Some(prop) = self.ctx.arena.get_property_decl(node) {
630 self.error_if_ts_only_optional(prop.question_token, member_idx);
631 self.error_if_ts_only_modifier(
632 &prop.modifiers,
633 SyntaxKind::AbstractKeyword,
634 "abstract",
635 );
636 self.check_js_grammar_accessibility_modifier(&prop.modifiers, member_idx);
637 }
638 }
639
640 syntax_kind_ext::GET_ACCESSOR | syntax_kind_ext::SET_ACCESSOR => {
641 if let Some(accessor) = self.ctx.arena.get_accessor(node) {
642 self.check_js_grammar_accessibility_modifier(&accessor.modifiers, member_idx);
643 self.error_if_ts_only_type_annotation(accessor.type_annotation);
644 self.check_js_grammar_parameters(&accessor.parameters.nodes);
645 }
646 }
647
648 syntax_kind_ext::INDEX_SIGNATURE
649 | syntax_kind_ext::CALL_SIGNATURE
650 | syntax_kind_ext::CONSTRUCT_SIGNATURE => {
651 self.error_if_ts_only_signature_without_body(true, member_idx);
652 }
653
654 _ => {}
655 }
656 }
657
658 fn check_js_grammar_parameters(&mut self, param_nodes: &[NodeIndex]) {
660 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
661
662 for ¶m_idx in param_nodes {
663 let Some(param_node) = self.ctx.arena.get(param_idx) else {
664 continue;
665 };
666 let Some(param) = self.ctx.arena.get_parameter(param_node) else {
667 continue;
668 };
669
670 if param.type_annotation.is_some() {
672 self.error_at_node(
673 param.type_annotation,
674 diagnostic_messages::TYPE_ANNOTATIONS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
675 diagnostic_codes::TYPE_ANNOTATIONS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
676 );
677 }
678
679 if let Some(ref modifiers) = param.modifiers {
681 for &mod_idx in &modifiers.nodes {
682 if let Some(mod_node) = self.ctx.arena.get(mod_idx) {
683 match mod_node.kind {
684 k if k == SyntaxKind::PublicKeyword as u16
685 || k == SyntaxKind::PrivateKeyword as u16
686 || k == SyntaxKind::ProtectedKeyword as u16
687 || k == SyntaxKind::ReadonlyKeyword as u16 =>
688 {
689 self.error_at_node(
690 mod_idx,
691 diagnostic_messages::PARAMETER_MODIFIERS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
692 diagnostic_codes::PARAMETER_MODIFIERS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
693 );
694 }
695 _ => {}
696 }
697 }
698 }
699 }
700
701 if param.question_token {
703 let message = crate::diagnostics::format_message(
704 diagnostic_messages::THE_MODIFIER_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
705 &["?"],
706 );
707 self.error_at_node(
708 param_idx,
709 &message,
710 diagnostic_codes::THE_MODIFIER_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
711 );
712 }
713 }
714 }
715
716 fn check_js_grammar_accessibility_modifier(
718 &mut self,
719 modifiers: &Option<tsz_parser::parser::NodeList>,
720 _fallback_idx: NodeIndex,
721 ) {
722 use crate::diagnostics::{diagnostic_codes, diagnostic_messages, format_message};
723
724 if let Some(mods) = modifiers {
725 for &mod_idx in &mods.nodes {
726 if let Some(mod_node) = self.ctx.arena.get(mod_idx) {
727 let modifier_name = match mod_node.kind {
728 k if k == SyntaxKind::PublicKeyword as u16 => Some("public"),
729 k if k == SyntaxKind::PrivateKeyword as u16 => Some("private"),
730 k if k == SyntaxKind::ProtectedKeyword as u16 => Some("protected"),
731 _ => None,
732 };
733 if let Some(name) = modifier_name {
734 let message = format_message(
735 diagnostic_messages::THE_MODIFIER_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
736 &[name],
737 );
738 self.error_at_node(
739 mod_idx,
740 &message,
741 diagnostic_codes::THE_MODIFIER_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
742 );
743 }
744 }
745 }
746 }
747 }
748
749 fn check_js_grammar_variable_statement(
751 &mut self,
752 _stmt_idx: NodeIndex,
753 node: &tsz_parser::parser::node::Node,
754 ) {
755 use crate::diagnostics::{diagnostic_codes, diagnostic_messages, format_message};
756
757 let Some(var) = self.ctx.arena.get_variable(node) else {
759 return;
760 };
761
762 if self.has_declare_modifier(&var.modifiers) {
764 let message = format_message(
765 diagnostic_messages::THE_MODIFIER_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
766 &["declare"],
767 );
768 if let Some(mod_idx) =
769 self.get_modifier_index(&var.modifiers, SyntaxKind::DeclareKeyword as u16)
770 {
771 self.error_at_node(
772 mod_idx,
773 &message,
774 diagnostic_codes::THE_MODIFIER_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
775 );
776 }
777 }
778
779 for &list_idx in &var.declarations.nodes {
782 if let Some(list_node) = self.ctx.arena.get(list_idx)
783 && let Some(list) = self.ctx.arena.get_variable(list_node)
784 {
785 for &decl_idx in &list.declarations.nodes {
786 if let Some(decl_node) = self.ctx.arena.get(decl_idx)
787 && let Some(var_decl) = self.ctx.arena.get_variable_declaration(decl_node)
788 {
789 if var_decl.type_annotation.is_some() {
791 self.error_at_node(
792 var_decl.type_annotation,
793 diagnostic_messages::TYPE_ANNOTATIONS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
794 diagnostic_codes::TYPE_ANNOTATIONS_CAN_ONLY_BE_USED_IN_TYPESCRIPT_FILES,
795 );
796 }
797 }
798 }
799 }
800 }
801 }
802
803 fn get_modifier_index(
805 &self,
806 modifiers: &Option<tsz_parser::parser::NodeList>,
807 kind: u16,
808 ) -> Option<NodeIndex> {
809 if let Some(mods) = modifiers {
810 for &mod_idx in &mods.nodes {
811 if let Some(mod_node) = self.ctx.arena.get(mod_idx)
812 && mod_node.kind == kind
813 {
814 return Some(mod_idx);
815 }
816 }
817 }
818 None
819 }
820
821 fn has_static_modifier_in_arena(
822 &self,
823 arena: &tsz_parser::parser::NodeArena,
824 modifiers: &Option<tsz_parser::parser::NodeList>,
825 ) -> bool {
826 arena.has_modifier(modifiers, tsz_scanner::SyntaxKind::StaticKeyword)
827 }
828
829 pub(crate) fn declaration_symbol_flags(
830 &self,
831 arena: &tsz_parser::parser::NodeArena,
832 decl_idx: NodeIndex,
833 ) -> Option<u32> {
834 use tsz_parser::parser::node_flags;
835
836 let decl_idx = self.resolve_duplicate_decl_node(arena, decl_idx)?;
837 let node = arena.get(decl_idx)?;
838
839 match node.kind {
840 syntax_kind_ext::VARIABLE_DECLARATION => {
841 let mut decl_flags = node.flags as u32;
842 if (decl_flags & (node_flags::LET | node_flags::CONST)) == 0
843 && let Some(parent) = arena.get_extended(decl_idx).map(|ext| ext.parent)
844 && let Some(parent_node) = arena.get(parent)
845 && parent_node.kind == syntax_kind_ext::VARIABLE_DECLARATION_LIST
846 {
847 decl_flags |= parent_node.flags as u32;
848 }
849 if (decl_flags & (node_flags::LET | node_flags::CONST)) != 0 {
850 Some(symbol_flags::BLOCK_SCOPED_VARIABLE)
851 } else {
852 Some(symbol_flags::FUNCTION_SCOPED_VARIABLE)
853 }
854 }
855 syntax_kind_ext::FUNCTION_DECLARATION => Some(symbol_flags::FUNCTION),
856 syntax_kind_ext::CLASS_DECLARATION => Some(symbol_flags::CLASS),
857 syntax_kind_ext::INTERFACE_DECLARATION => Some(symbol_flags::INTERFACE),
858 syntax_kind_ext::TYPE_ALIAS_DECLARATION => Some(symbol_flags::TYPE_ALIAS),
859 syntax_kind_ext::ENUM_DECLARATION => {
860 let is_const_enum = arena
862 .get_enum(node)
863 .and_then(|enum_decl| enum_decl.modifiers.as_ref())
864 .is_some_and(|modifiers| {
865 modifiers.nodes.iter().any(|&mod_idx| {
866 arena.get(mod_idx).is_some_and(|mod_node| {
867 mod_node.kind == tsz_scanner::SyntaxKind::ConstKeyword as u16
868 })
869 })
870 });
871 if is_const_enum {
872 Some(symbol_flags::CONST_ENUM)
873 } else {
874 Some(symbol_flags::REGULAR_ENUM)
875 }
876 }
877 syntax_kind_ext::MODULE_DECLARATION => {
878 Some(symbol_flags::VALUE_MODULE | symbol_flags::NAMESPACE_MODULE)
880 }
881 syntax_kind_ext::GET_ACCESSOR => {
882 let mut flags = symbol_flags::GET_ACCESSOR;
883 if let Some(accessor) = arena.get_accessor(node)
884 && self.has_static_modifier_in_arena(arena, &accessor.modifiers)
885 {
886 flags |= symbol_flags::STATIC;
887 }
888 Some(flags)
889 }
890 syntax_kind_ext::SET_ACCESSOR => {
891 let mut flags = symbol_flags::SET_ACCESSOR;
892 if let Some(accessor) = arena.get_accessor(node)
893 && self.has_static_modifier_in_arena(arena, &accessor.modifiers)
894 {
895 flags |= symbol_flags::STATIC;
896 }
897 Some(flags)
898 }
899 syntax_kind_ext::METHOD_DECLARATION => {
900 let mut flags = symbol_flags::METHOD;
901 if let Some(method) = arena.get_method_decl(node)
902 && self.has_static_modifier_in_arena(arena, &method.modifiers)
903 {
904 flags |= symbol_flags::STATIC;
905 }
906 Some(flags)
907 }
908 syntax_kind_ext::PROPERTY_DECLARATION => {
909 let mut flags = symbol_flags::PROPERTY;
910 if let Some(prop) = arena.get_property_decl(node)
911 && self.has_static_modifier_in_arena(arena, &prop.modifiers)
912 {
913 flags |= symbol_flags::STATIC;
914 }
915 Some(flags)
916 }
917 syntax_kind_ext::CONSTRUCTOR => Some(symbol_flags::CONSTRUCTOR),
918 syntax_kind_ext::PARAMETER => Some(symbol_flags::FUNCTION_SCOPED_VARIABLE),
919 syntax_kind_ext::IMPORT_EQUALS_DECLARATION
920 | syntax_kind_ext::IMPORT_CLAUSE
921 | syntax_kind_ext::NAMESPACE_IMPORT
922 | syntax_kind_ext::IMPORT_SPECIFIER => Some(symbol_flags::ALIAS),
923 syntax_kind_ext::NAMESPACE_EXPORT_DECLARATION => {
924 Some(symbol_flags::FUNCTION_SCOPED_VARIABLE | symbol_flags::ALIAS)
927 }
928 _ => None,
929 }
930 }
931
932 fn check_reserved_await_identifier_in_module(&mut self, source_file_idx: NodeIndex) {
933 let Some(source_file_node) = self.ctx.arena.get(source_file_idx) else {
934 return;
935 };
936 let Some(source_file) = self.ctx.arena.get_source_file(source_file_node) else {
937 return;
938 };
939
940 let source_file_name = &source_file.file_name;
941 let is_declaration_file = source_file.is_declaration_file
942 || source_file_name.ends_with(".d.ts")
943 || source_file_name.ends_with(".d.tsx")
944 || source_file_name.ends_with(".d.mts")
945 || source_file_name.ends_with(".d.cts")
946 || self.ctx.file_name.ends_with(".d.ts")
947 || self.ctx.file_name.ends_with(".d.tsx")
948 || self.ctx.file_name.ends_with(".d.mts")
949 || self.ctx.file_name.ends_with(".d.cts");
950
951 if is_declaration_file {
952 return;
953 }
954
955 let is_external_module = if let Some(ref map) = self.ctx.is_external_module_by_file {
956 map.get(&self.ctx.file_name).copied().unwrap_or(false)
957 } else {
958 self.ctx.binder.is_external_module()
959 };
960
961 let has_module_indicator = self.source_file_has_module_indicator(source_file);
962 let force_js_module_check = self.is_js_like_file() && has_module_indicator;
963
964 if !is_external_module && !force_js_module_check {
965 return;
966 }
967
968 let Some(await_sym_id) = self.ctx.binder.file_locals.get("await") else {
969 return;
970 };
971
972 let Some(symbol) = self.ctx.binder.get_symbol(await_sym_id) else {
973 return;
974 };
975
976 let mut candidate_decls = symbol.declarations.clone();
977 if symbol.value_declaration.is_some() {
978 candidate_decls.push(symbol.value_declaration);
979 }
980
981 candidate_decls.sort_unstable_by_key(|node| node.0);
982 candidate_decls.dedup();
983
984 for decl_idx in candidate_decls {
985 let Some(node) = self.ctx.arena.get(decl_idx) else {
986 continue;
987 };
988
989 let is_disallowed_top_level_await_decl = matches!(
990 node.kind,
991 syntax_kind_ext::VARIABLE_DECLARATION
992 | syntax_kind_ext::BINDING_ELEMENT
993 | syntax_kind_ext::FUNCTION_DECLARATION
994 | syntax_kind_ext::CLASS_DECLARATION
995 | syntax_kind_ext::IMPORT_CLAUSE
996 | syntax_kind_ext::IMPORT_SPECIFIER
997 | syntax_kind_ext::NAMESPACE_IMPORT
998 );
999 if !is_disallowed_top_level_await_decl {
1000 continue;
1001 }
1002
1003 let is_plain_await_identifier = self
1004 .await_identifier_name_node_for_decl(decl_idx)
1005 .is_some_and(|name_idx| self.is_plain_await_identifier(source_file, name_idx));
1006
1007 if !is_plain_await_identifier {
1008 continue;
1009 }
1010
1011 let mut current = decl_idx;
1012 let mut is_top_level = false;
1013 while let Some(ext) = self.ctx.arena.get_extended(current) {
1014 let parent = ext.parent;
1015 if parent.is_none() {
1016 break;
1017 }
1018 if parent == source_file_idx {
1019 is_top_level = true;
1020 break;
1021 }
1022 current = parent;
1023 }
1024
1025 if !is_top_level {
1026 continue;
1027 }
1028
1029 let report_idx = self
1030 .await_identifier_name_node_for_decl(decl_idx)
1031 .unwrap_or(decl_idx);
1032 self.error_at_node(
1033 report_idx,
1034 "Identifier expected. 'await' is a reserved word at the top-level of a module.",
1035 crate::diagnostics::diagnostic_codes::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_AT_THE_TOP_LEVEL_OF_A_MODULE,
1036 );
1037 break;
1038 }
1039
1040 self.emit_top_level_await_text_fallback(source_file);
1041 }
1042
1043 fn await_identifier_name_node_for_decl(&self, decl_idx: NodeIndex) -> Option<NodeIndex> {
1044 let node = self.ctx.arena.get(decl_idx)?;
1045 match node.kind {
1046 syntax_kind_ext::VARIABLE_DECLARATION => self
1047 .ctx
1048 .arena
1049 .get_variable_declaration(node)
1050 .map(|decl| decl.name),
1051 syntax_kind_ext::BINDING_ELEMENT => self
1052 .ctx
1053 .arena
1054 .get_binding_element(node)
1055 .map(|decl| decl.name),
1056 syntax_kind_ext::FUNCTION_DECLARATION => {
1057 self.ctx.arena.get_function(node).map(|f| f.name)
1058 }
1059 syntax_kind_ext::CLASS_DECLARATION => self.ctx.arena.get_class(node).map(|c| c.name),
1060 syntax_kind_ext::IMPORT_CLAUSE => self
1061 .ctx
1062 .arena
1063 .get_import_clause(node)
1064 .map(|clause| clause.name),
1065 syntax_kind_ext::IMPORT_SPECIFIER => self
1066 .ctx
1067 .arena
1068 .get_specifier(node)
1069 .map(|specifier| specifier.name),
1070 syntax_kind_ext::NAMESPACE_IMPORT => self
1071 .ctx
1072 .arena
1073 .get_named_imports(node)
1074 .map(|named| named.name),
1075 _ => None,
1076 }
1077 }
1078
1079 fn is_plain_await_identifier(
1080 &self,
1081 source_file: &tsz_parser::parser::node::SourceFileData,
1082 node_idx: NodeIndex,
1083 ) -> bool {
1084 let Some(node) = self.ctx.arena.get(node_idx) else {
1085 return false;
1086 };
1087 if node.kind != SyntaxKind::Identifier as u16 {
1088 return false;
1089 }
1090 let Some((start, end)) = self.get_node_span(node_idx) else {
1091 return false;
1092 };
1093
1094 source_file
1095 .text
1096 .get(start as usize..end as usize)
1097 .is_some_and(|text| text == "await")
1098 }
1099
1100 fn source_file_has_module_indicator(
1101 &self,
1102 source_file: &tsz_parser::parser::node::SourceFileData,
1103 ) -> bool {
1104 source_file.statements.nodes.iter().any(|&stmt_idx| {
1105 let Some(stmt_node) = self.ctx.arena.get(stmt_idx) else {
1106 return false;
1107 };
1108
1109 matches!(
1110 stmt_node.kind,
1111 syntax_kind_ext::EXPORT_DECLARATION
1112 | syntax_kind_ext::EXPORT_ASSIGNMENT
1113 | syntax_kind_ext::IMPORT_DECLARATION
1114 )
1115 })
1116 }
1117
1118 fn emit_ts1262_at_first_await(&mut self, statement_start: u32, statement_text: &str) -> bool {
1119 let Some(offset) = statement_text.find("await") else {
1120 return false;
1121 };
1122
1123 self.error_at_position(
1124 statement_start + offset as u32,
1125 5,
1126 "Identifier expected. 'await' is a reserved word at the top-level of a module.",
1127 crate::diagnostics::diagnostic_codes::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_AT_THE_TOP_LEVEL_OF_A_MODULE,
1128 );
1129 true
1130 }
1131
1132 fn statement_contains_any(text: &str, patterns: &[&str]) -> bool {
1133 patterns.iter().any(|pattern| text.contains(pattern))
1134 }
1135
1136 fn is_js_like_file(&self) -> bool {
1137 self.ctx.file_name.ends_with(".js")
1138 || self.ctx.file_name.ends_with(".jsx")
1139 || self.ctx.file_name.ends_with(".mjs")
1140 || self.ctx.file_name.ends_with(".cjs")
1141 }
1142
1143 fn emit_top_level_await_text_fallback(
1144 &mut self,
1145 source_file: &tsz_parser::parser::node::SourceFileData,
1146 ) {
1147 let ts1262_code =
1148 crate::diagnostics::diagnostic_codes::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_AT_THE_TOP_LEVEL_OF_A_MODULE;
1149 if self
1150 .ctx
1151 .diagnostics
1152 .iter()
1153 .any(|diag| diag.code == ts1262_code)
1154 {
1155 return;
1156 }
1157
1158 let has_module_indicator = self.source_file_has_module_indicator(source_file);
1159 let is_js_like_file = self.is_js_like_file();
1160
1161 let import_patterns = [
1162 "import await from",
1163 "import * as await from",
1164 "import { await } from",
1165 "import { await as await } from",
1166 ];
1167 let binding_pattern_patterns = ["var {await}", "var [await]"];
1168 let js_variable_patterns = ["const await", "let await", "var await"];
1169
1170 for &stmt_idx in &source_file.statements.nodes {
1171 let Some(stmt_node) = self.ctx.arena.get(stmt_idx) else {
1172 continue;
1173 };
1174
1175 let Some((start, end)) = self.get_node_span(stmt_idx) else {
1176 continue;
1177 };
1178 let Some(stmt_text) = source_file.text.get(start as usize..end as usize) else {
1179 continue;
1180 };
1181
1182 match stmt_node.kind {
1183 syntax_kind_ext::IMPORT_DECLARATION => {
1184 if Self::statement_contains_any(stmt_text, &import_patterns)
1185 && self.emit_ts1262_at_first_await(start, stmt_text)
1186 {
1187 return;
1188 }
1189 }
1190 syntax_kind_ext::IMPORT_EQUALS_DECLARATION => {
1191 let has_await_import_equals = stmt_text.contains("import await =");
1192 let is_require_form = stmt_text.contains("require(");
1193 if has_await_import_equals
1194 && (is_require_form || has_module_indicator)
1195 && self.emit_ts1262_at_first_await(start, stmt_text)
1196 {
1197 return;
1198 }
1199 }
1200 syntax_kind_ext::VARIABLE_STATEMENT => {
1201 let has_binding_pattern_await =
1202 Self::statement_contains_any(stmt_text, &binding_pattern_patterns);
1203 let has_js_var_await = is_js_like_file
1204 && Self::statement_contains_any(stmt_text, &js_variable_patterns);
1205 if (has_binding_pattern_await || has_js_var_await)
1206 && self.emit_ts1262_at_first_await(start, stmt_text)
1207 {
1208 return;
1209 }
1210 }
1211 _ => {}
1212 }
1213 }
1214
1215 if has_module_indicator && let Some(offset) = source_file.text.find("const await") {
1216 self.error_at_position(
1217 offset as u32 + 6,
1218 5,
1219 "Identifier expected. 'await' is a reserved word at the top-level of a module.",
1220 ts1262_code,
1221 );
1222 }
1223 }
1224
1225 fn check_dts_statement_in_ambient_context(&mut self, stmt_idx: NodeIndex) {
1229 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
1230 use tsz_parser::parser::syntax_kind_ext;
1231
1232 let Some(node) = self.ctx.arena.get(stmt_idx) else {
1233 return;
1234 };
1235
1236 let is_non_declaration = matches!(
1237 node.kind,
1238 k if k == syntax_kind_ext::EXPRESSION_STATEMENT
1239 || k == syntax_kind_ext::IF_STATEMENT
1240 || k == syntax_kind_ext::DO_STATEMENT
1241 || k == syntax_kind_ext::WHILE_STATEMENT
1242 || k == syntax_kind_ext::FOR_STATEMENT
1243 || k == syntax_kind_ext::FOR_IN_STATEMENT
1244 || k == syntax_kind_ext::FOR_OF_STATEMENT
1245 || k == syntax_kind_ext::BREAK_STATEMENT
1246 || k == syntax_kind_ext::CONTINUE_STATEMENT
1247 || k == syntax_kind_ext::RETURN_STATEMENT
1248 || k == syntax_kind_ext::WITH_STATEMENT
1249 || k == syntax_kind_ext::SWITCH_STATEMENT
1250 || k == syntax_kind_ext::THROW_STATEMENT
1251 || k == syntax_kind_ext::TRY_STATEMENT
1252 || k == syntax_kind_ext::DEBUGGER_STATEMENT
1253 || k == syntax_kind_ext::LABELED_STATEMENT
1254 );
1255
1256 if is_non_declaration && let Some((pos, end)) = self.ctx.get_node_span(stmt_idx) {
1257 self.ctx.error(
1258 pos,
1259 end - pos,
1260 diagnostic_messages::STATEMENTS_ARE_NOT_ALLOWED_IN_AMBIENT_CONTEXTS.to_string(),
1261 diagnostic_codes::STATEMENTS_ARE_NOT_ALLOWED_IN_AMBIENT_CONTEXTS,
1262 );
1263 }
1264 }
1265
1266 fn check_module_none_statements(&mut self, stmts: &[NodeIndex]) {
1268 use crate::diagnostics::{diagnostic_codes, diagnostic_messages};
1269 use tsz_parser::parser::syntax_kind_ext;
1270
1271 for &stmt_idx in stmts {
1272 let Some(node) = self.ctx.arena.get(stmt_idx) else {
1273 continue;
1274 };
1275 let mut is_error = false;
1276
1277 match node.kind {
1278 syntax_kind_ext::IMPORT_DECLARATION
1279 | syntax_kind_ext::EXPORT_DECLARATION
1280 | syntax_kind_ext::EXPORT_ASSIGNMENT => {
1281 is_error = true;
1282 }
1283 syntax_kind_ext::IMPORT_EQUALS_DECLARATION => {
1284 if let Some(import_decl) = self.ctx.arena.get_import_decl(node)
1288 && let Some(module_ref_node) =
1289 self.ctx.arena.get(import_decl.module_specifier)
1290 && module_ref_node.kind == tsz_scanner::SyntaxKind::StringLiteral as u16
1291 {
1292 is_error = true;
1293 }
1294 }
1295 syntax_kind_ext::MODULE_DECLARATION => {
1296 if self.is_declaration_exported(self.ctx.arena, stmt_idx) {
1297 is_error = true;
1298 } else if let Some(module) = self.ctx.arena.get_module(node)
1299 && let Some(name_node) = self.ctx.arena.get(module.name)
1300 && name_node.kind == tsz_scanner::SyntaxKind::StringLiteral as u16
1301 {
1302 is_error = true;
1303 }
1304 }
1305 k if k == syntax_kind_ext::VARIABLE_STATEMENT
1307 || k == syntax_kind_ext::FUNCTION_DECLARATION
1308 || k == syntax_kind_ext::CLASS_DECLARATION
1309 || k == syntax_kind_ext::INTERFACE_DECLARATION
1310 || k == syntax_kind_ext::TYPE_ALIAS_DECLARATION
1311 || k == syntax_kind_ext::ENUM_DECLARATION =>
1312 {
1313 if self.is_declaration_exported(self.ctx.arena, stmt_idx) {
1314 is_error = true;
1315 }
1316 }
1317 _ => {}
1318 }
1319
1320 if is_error {
1321 self.error_at_node(
1322 stmt_idx,
1323 diagnostic_messages::CANNOT_USE_IMPORTS_EXPORTS_OR_MODULE_AUGMENTATIONS_WHEN_MODULE_IS_NONE,
1324 diagnostic_codes::CANNOT_USE_IMPORTS_EXPORTS_OR_MODULE_AUGMENTATIONS_WHEN_MODULE_IS_NONE,
1325 );
1326 }
1327 }
1328 }
1329
1330 pub(crate) fn check_statement(&mut self, stmt_idx: NodeIndex) {
1336 StatementChecker::check(stmt_idx, self);
1337 }
1338}