1#![doc = include_str!("../examples/parser.rs")]
41#![doc = include_str!("../examples/parser_tsx.rs")]
46use std::any::Any;
68
69mod ast_builder;
70pub mod config;
71mod context;
72mod cursor;
73mod error_handler;
74mod modifiers;
75mod module_record;
76mod state;
77
78mod js;
79mod jsx;
80mod ts;
81
82mod diagnostics;
83
84#[cfg(not(feature = "benchmarking"))]
86mod lexer;
87#[cfg(feature = "benchmarking")]
88#[doc(hidden)]
89pub mod lexer;
90
91use oxc_allocator::{Allocator, ArenaBox, ArenaVec, Dummy, GetAllocator};
92use oxc_ast::{
93 ast::{Expression, Program, Statement},
94 builder::{AstBuilder, GetAstBuilder},
95};
96use oxc_diagnostics::Diagnostics;
97use oxc_span::{SourceType, Span};
98use oxc_syntax::module_record::ModuleRecord;
99
100pub use crate::lexer::{Kind, Token};
101use crate::{
102 ast_builder::ParserAstBuilder,
103 config::{
104 LexerConfig, NoTokensParserConfig, ParserConfig, RuntimeParserConfig, TokensParserConfig,
105 },
106 context::{Context, StatementContext},
107 diagnostics::ParserDiagnostic,
108 error_handler::FatalError,
109 lexer::Lexer,
110 module_record::ModuleRecordBuilder,
111 state::ParserState,
112};
113
114pub(crate) const MAX_LEN: usize = if size_of::<usize>() >= 8 {
132 u32::MAX as usize - 256
134} else {
135 isize::MAX as usize
137};
138
139#[non_exhaustive]
165pub struct ParserReturn<'a> {
166 pub program: Program<'a>,
182
183 pub module_record: ModuleRecord<'a>,
185
186 pub diagnostics: Diagnostics,
192
193 pub irregular_whitespaces: Box<[Span]>,
195
196 pub tokens: ArenaVec<'a, Token>,
200
201 pub fatal_error: bool,
213
214 pub is_flow_language: bool,
216}
217
218#[derive(Debug, Clone, Copy)]
222pub struct ParseOptions {
223 #[cfg(feature = "regular_expression")]
227 pub parse_regular_expression: bool,
228
229 pub allow_return_outside_function: bool,
237
238 pub preserve_parens: bool,
249
250 pub allow_v8_intrinsics: bool,
257
258 pub enable_ident_hashes: bool,
270}
271
272impl Default for ParseOptions {
273 fn default() -> Self {
274 Self {
275 #[cfg(feature = "regular_expression")]
276 parse_regular_expression: false,
277 allow_return_outside_function: false,
278 preserve_parens: true,
279 allow_v8_intrinsics: false,
280 enable_ident_hashes: true,
281 }
282 }
283}
284
285pub struct Parser<'a, C: ParserConfig = NoTokensParserConfig> {
289 allocator: &'a Allocator,
290 source_text: &'a str,
291 source_type: SourceType,
292 options: ParseOptions,
293 config: C,
294}
295
296impl<'a> Parser<'a> {
297 pub fn new(allocator: &'a Allocator, source_text: &'a str, source_type: SourceType) -> Self {
304 let options = ParseOptions::default();
305 Self { allocator, source_text, source_type, options, config: NoTokensParserConfig }
306 }
307}
308
309impl<'a, C: ParserConfig> Parser<'a, C> {
310 #[must_use]
312 pub fn with_options(mut self, options: ParseOptions) -> Self {
313 self.options = options;
314 self
315 }
316
317 #[must_use]
321 pub fn with_config<Config: ParserConfig>(self, config: Config) -> Parser<'a, Config> {
322 Parser {
323 allocator: self.allocator,
324 source_text: self.source_text,
325 source_type: self.source_type,
326 options: self.options,
327 config,
328 }
329 }
330}
331
332mod parser_parse {
333 use super::*;
334
335 pub struct UniquePromise(());
350
351 impl UniquePromise {
352 #[inline]
353 fn new() -> Self {
354 Self(())
355 }
356
357 #[cfg(any(test, feature = "benchmarking"))]
361 pub fn new_for_tests_and_benchmarks() -> Self {
362 Self(())
363 }
364 }
365
366 impl<'a, C: ParserConfig> Parser<'a, C> {
367 pub fn parse(self) -> ParserReturn<'a> {
384 let config: &dyn Any = &self.config;
385 if config.is::<NoTokensParserConfig>() {
386 parse_with_no_tokens_config(
387 self.allocator,
388 self.source_text,
389 self.source_type,
390 self.options,
391 )
392 } else if config.is::<TokensParserConfig>() {
393 parse_with_tokens_config(
394 self.allocator,
395 self.source_text,
396 self.source_type,
397 self.options,
398 )
399 } else if let Some(&config) = config.downcast_ref::<RuntimeParserConfig>() {
400 parse_with_runtime_config(
401 self.allocator,
402 self.source_text,
403 self.source_type,
404 self.options,
405 config,
406 )
407 } else {
408 ParserImpl::<C>::new(
411 self.allocator,
412 self.source_text,
413 self.source_type,
414 self.options,
415 self.config,
416 UniquePromise::new(),
417 )
418 .parse()
419 }
420 }
421
422 pub fn parse_expression(self) -> Result<Expression<'a>, Diagnostics> {
448 let config: &dyn Any = &self.config;
449 if config.is::<NoTokensParserConfig>() {
450 parse_expression_with_no_tokens_config(
451 self.allocator,
452 self.source_text,
453 self.source_type,
454 self.options,
455 )
456 } else if config.is::<TokensParserConfig>() {
457 parse_expression_with_tokens_config(
458 self.allocator,
459 self.source_text,
460 self.source_type,
461 self.options,
462 )
463 } else if let Some(&config) = config.downcast_ref::<RuntimeParserConfig>() {
464 parse_expression_with_runtime_config(
465 self.allocator,
466 self.source_text,
467 self.source_type,
468 self.options,
469 config,
470 )
471 } else {
472 ParserImpl::<C>::new(
473 self.allocator,
474 self.source_text,
475 self.source_type,
476 self.options,
477 self.config,
478 UniquePromise::new(),
479 )
480 .parse_expression()
481 }
482 }
483 }
484
485 #[inline(never)]
513 fn parse_with_no_tokens_config<'a>(
514 allocator: &'a Allocator,
515 source_text: &'a str,
516 source_type: SourceType,
517 options: ParseOptions,
518 ) -> ParserReturn<'a> {
519 ParserImpl::<NoTokensParserConfig>::new(
520 allocator,
521 source_text,
522 source_type,
523 options,
524 NoTokensParserConfig,
525 UniquePromise::new(),
526 )
527 .parse()
528 }
529
530 #[inline(never)]
531 fn parse_with_tokens_config<'a>(
532 allocator: &'a Allocator,
533 source_text: &'a str,
534 source_type: SourceType,
535 options: ParseOptions,
536 ) -> ParserReturn<'a> {
537 ParserImpl::<TokensParserConfig>::new(
538 allocator,
539 source_text,
540 source_type,
541 options,
542 TokensParserConfig,
543 UniquePromise::new(),
544 )
545 .parse()
546 }
547
548 #[inline(never)]
549 fn parse_with_runtime_config<'a>(
550 allocator: &'a Allocator,
551 source_text: &'a str,
552 source_type: SourceType,
553 options: ParseOptions,
554 config: RuntimeParserConfig,
555 ) -> ParserReturn<'a> {
556 ParserImpl::<RuntimeParserConfig>::new(
557 allocator,
558 source_text,
559 source_type,
560 options,
561 config,
562 UniquePromise::new(),
563 )
564 .parse()
565 }
566
567 #[inline(never)]
568 fn parse_expression_with_no_tokens_config<'a>(
569 allocator: &'a Allocator,
570 source_text: &'a str,
571 source_type: SourceType,
572 options: ParseOptions,
573 ) -> Result<Expression<'a>, Diagnostics> {
574 ParserImpl::<NoTokensParserConfig>::new(
575 allocator,
576 source_text,
577 source_type,
578 options,
579 NoTokensParserConfig,
580 UniquePromise::new(),
581 )
582 .parse_expression()
583 }
584
585 #[inline(never)]
586 fn parse_expression_with_tokens_config<'a>(
587 allocator: &'a Allocator,
588 source_text: &'a str,
589 source_type: SourceType,
590 options: ParseOptions,
591 ) -> Result<Expression<'a>, Diagnostics> {
592 ParserImpl::<TokensParserConfig>::new(
593 allocator,
594 source_text,
595 source_type,
596 options,
597 TokensParserConfig,
598 UniquePromise::new(),
599 )
600 .parse_expression()
601 }
602
603 #[inline(never)]
604 fn parse_expression_with_runtime_config<'a>(
605 allocator: &'a Allocator,
606 source_text: &'a str,
607 source_type: SourceType,
608 options: ParseOptions,
609 config: RuntimeParserConfig,
610 ) -> Result<Expression<'a>, Diagnostics> {
611 ParserImpl::<RuntimeParserConfig>::new(
612 allocator,
613 source_text,
614 source_type,
615 options,
616 config,
617 UniquePromise::new(),
618 )
619 .parse_expression()
620 }
621}
622use parser_parse::UniquePromise;
623
624struct ParserImpl<'a, C: ParserConfig> {
627 options: ParseOptions,
629
630 pub(crate) lexer: Lexer<'a, C::LexerConfig>,
631
632 source_type: SourceType,
634
635 source_text: &'a str,
637
638 errors: Vec<ParserDiagnostic<'a>>,
643
644 deferred_script_errors: Vec<ParserDiagnostic<'a>>,
650
651 fatal_error: Option<FatalError<'a>>,
652
653 token: Token,
655
656 prev_token_end: u32,
658
659 state: ParserState<'a>,
661
662 ctx: Context,
664
665 ast: ParserAstBuilder<'a>,
667
668 module_record_builder: ModuleRecordBuilder<'a>,
670
671 is_ts: bool,
673}
674
675impl<'a, C: ParserConfig> ParserImpl<'a, C> {
676 #[inline]
681 #[expect(clippy::needless_pass_by_value)]
682 pub fn new(
683 allocator: &'a Allocator,
684 source_text: &'a str,
685 source_type: SourceType,
686 options: ParseOptions,
687 config: C,
688 unique: UniquePromise,
689 ) -> Self {
690 Self {
691 options,
692 lexer: Lexer::new(allocator, source_text, source_type, config.lexer_config(), unique),
693 source_type,
694 source_text,
695 errors: vec![],
696 deferred_script_errors: vec![],
697 fatal_error: None,
698 token: Token::default(),
699 prev_token_end: 0,
700 state: ParserState::new(),
701 ctx: Self::default_context(source_type, options),
702 ast: ParserAstBuilder::new(allocator),
703 module_record_builder: ModuleRecordBuilder::new(allocator, source_type),
704 is_ts: source_type.is_typescript(),
705 }
706 }
707
708 #[inline]
713 pub fn parse(mut self) -> ParserReturn<'a> {
714 let mut program = self.parse_program();
715 let mut has_fatal_error = false;
716
717 if let Some(fatal_error) = self.fatal_error.take() {
718 has_fatal_error = true;
719 self.errors.truncate(fatal_error.errors_len);
720 if !self.lexer.errors.is_empty() && self.cur_kind().is_eof() {
721 } else {
723 self.error(fatal_error.error);
724 }
725
726 program = Program::dummy(self.allocator());
727 program.source_type = self.source_type;
728 program.source_text = self.source_text;
729 }
730
731 self.check_unfinished_errors();
732
733 if let Some(overlong_error) = self.overlong_error() {
734 has_fatal_error = true;
735 self.lexer.errors.clear();
736 self.errors.clear();
737 self.error(overlong_error);
738 }
739
740 let mut is_flow_language = false;
741 let mut errors = Diagnostics::new();
742 if (!self.lexer.errors.is_empty() || !self.errors.is_empty())
744 && let Some(error) = self.flow_error()
745 {
746 is_flow_language = true;
747 errors.push(error.into_diagnostic());
748 }
749 let (module_record, mut module_record_errors) = self.module_record_builder.build();
750 if errors.len() != 1 {
751 errors
752 .reserve(self.lexer.errors.len() + self.errors.len() + module_record_errors.len());
753 errors.extend(self.lexer.errors.drain(..).map(ParserDiagnostic::into_diagnostic));
754 errors.extend(self.errors.drain(..).map(ParserDiagnostic::into_diagnostic));
755 errors.append(&mut module_record_errors);
756 }
757 let irregular_whitespaces =
758 std::mem::take(&mut self.lexer.trivia_builder.irregular_whitespaces).into_boxed_slice();
759
760 let source_type = program.source_type;
761 if source_type.is_unambiguous() {
762 if module_record.has_module_syntax {
763 program.source_type = source_type.with_module(true);
766 errors.extend(
767 self.lexer
768 .deferred_module_errors
769 .drain(..)
770 .map(ParserDiagnostic::into_diagnostic),
771 );
772 } else {
773 program.source_type = source_type.with_script(true);
776 errors.extend(
777 self.deferred_script_errors.into_iter().map(ParserDiagnostic::into_diagnostic),
778 );
779 }
780 }
781
782 let tokens = if has_fatal_error {
783 ArenaVec::new_in(&self.ast)
784 } else {
785 self.lexer.finalize_tokens()
786 };
787
788 program.comments = self.lexer.trivia_builder.comments;
789
790 ParserReturn {
791 program,
792 module_record,
793 diagnostics: errors,
794 irregular_whitespaces,
795 tokens,
796 fatal_error: has_fatal_error,
797 is_flow_language,
798 }
799 }
800
801 pub fn parse_expression(mut self) -> Result<Expression<'a>, Diagnostics> {
802 self.bump_any();
804 let expr = self.parse_expr();
805 if !self.at(Kind::Eof) {
806 self.set_unexpected();
807 }
808 if let Some(FatalError { error, .. }) = self.fatal_error.take() {
809 return Err(error.into_diagnostic().into());
810 }
811 self.check_unfinished_errors();
812 let errors = self
813 .lexer
814 .errors
815 .into_iter()
816 .chain(self.errors)
817 .map(ParserDiagnostic::into_diagnostic)
818 .collect::<Diagnostics>();
819 if !errors.is_empty() {
820 return Err(errors);
821 }
822 Ok(expr)
823 }
824
825 #[expect(clippy::cast_possible_truncation)]
826 fn parse_program(&mut self) -> Program<'a> {
827 self.token = self.lexer.first_token();
830
831 let hashbang = self.parse_hashbang();
832 self.ctx |= Context::TopLevel;
833 let (directives, mut statements) =
834 self.parse_directives_and_statements(false);
835
836 if self.fatal_error.is_none()
842 && self.source_type.is_unambiguous()
843 && self.module_record_builder.has_module_syntax()
844 && !self.state.potential_await_reparse.is_empty()
845 {
846 self.reparse_potential_top_level_awaits(&mut statements);
847 }
848
849 let span = Span::new(0, self.source_text.len() as u32);
850 Program::new(
851 span,
852 self.source_type,
853 self.source_text,
854 [],
856 hashbang,
857 directives,
858 statements,
859 &AstBuilder::new(self.allocator()),
862 )
863 }
864
865 fn reparse_potential_top_level_awaits(&mut self, statements: &mut ArenaVec<'a, Statement<'a>>) {
871 let original_tokens =
872 if self.lexer.config.tokens() { Some(self.lexer.take_tokens()) } else { None };
873 let original = original_tokens.as_ref().map_or(&[][..], |tokens| tokens.as_slice());
874 let mut original_index = 0;
875 let mut replacements = ArenaVec::new_in(self);
876
877 let checkpoints = std::mem::take(&mut self.state.potential_await_reparse);
878 let mut edits = ArenaVec::with_capacity_in(
880 if self.lexer.config.tokens() { checkpoints.len() } else { 0 },
881 self,
882 );
883 for (stmt_index, checkpoint) in checkpoints {
884 self.rewind(checkpoint);
885 let replacement_start = replacements.len();
886
887 if self.lexer.config.tokens() {
888 original_index += original[original_index..]
889 .iter()
890 .position(|token| token.start() >= self.token.start())
891 .unwrap_or(original.len() - original_index);
892 replacements.push(self.token);
894 self.lexer.set_tokens(std::mem::replace(&mut replacements, ArenaVec::new_in(self)));
895 }
896 let edit_start = original_index;
897
898 let stmt = self.context_add(Context::Await, |p| {
900 p.parse_statement_list_item(StatementContext::Program)
901 });
902
903 if stmt_index < statements.len() {
905 statements[stmt_index] = stmt;
906 }
907
908 if self.lexer.config.tokens() {
909 replacements = self.lexer.take_tokens();
910 replacements.pop();
912 let end = replacements[..replacement_start].last().map_or(0, Token::end);
915 let overlap_len =
916 replacements[replacement_start..].partition_point(|token| token.start() < end);
917 if overlap_len != 0 {
918 replacements.copy_within(replacement_start + overlap_len.., replacement_start);
919 let len = replacements.len() - overlap_len;
920 replacements.truncate(len);
921 }
922 original_index += original[original_index..]
923 .iter()
924 .position(|token| token.start() >= self.token.start())
925 .unwrap_or(original.len() - original_index);
926 edits.push((edit_start..original_index, replacement_start..replacements.len()));
927 }
928 }
929
930 if let Some(mut tokens) = original_tokens {
931 let mut read = 0;
932 if edits.iter().all(|(range, replacement)| replacement.len() <= range.len()) {
933 let mut write = 0;
936 for (range, replacement) in edits {
937 tokens.copy_within(read..range.start, write);
938 write += range.start - read;
939 let replacement = &replacements[replacement];
940 tokens[write..write + replacement.len()].copy_from_slice(replacement);
941 write += replacement.len();
942 read = range.end;
943 }
944 let tail_len = tokens.len() - read;
945 tokens.copy_within(read.., write);
946 tokens.truncate(write + tail_len);
947 self.lexer.set_tokens(tokens);
948 } else {
949 let len = edits.iter().fold(tokens.len(), |len, (range, replacement)| {
952 len - range.len() + replacement.len()
953 });
954 let mut output = ArenaVec::with_capacity_in(len, self);
955 for (range, replacement) in edits {
956 output.extend_from_slice(&tokens[read..range.start]);
957 output.extend_from_slice(&replacements[replacement]);
958 read = range.end;
959 }
960 output.extend_from_slice(&tokens[read..]);
961 self.lexer.set_tokens(output);
962 }
963 }
964 }
965
966 fn default_context(source_type: SourceType, options: ParseOptions) -> Context {
967 let mut ctx = Context::default().and_ambient(source_type.is_typescript_definition());
968 if source_type.is_module() {
969 ctx = ctx.and_await(true);
971 }
972 if options.allow_return_outside_function || source_type.is_commonjs() {
975 ctx = ctx.and_return(true);
976 }
977 if source_type.is_commonjs() {
978 ctx = ctx.and_new_target(true);
979 }
980 ctx
981 }
982
983 fn flow_error(&mut self) -> Option<ParserDiagnostic<'a>> {
986 if !self.source_type.is_javascript() {
987 return None;
988 }
989 let span = self.lexer.trivia_builder.comments.first()?.span;
990 if span.source_text(self.source_text).contains("@flow") {
991 self.errors.clear();
992 Some(diagnostics::flow(span))
993 } else {
994 None
995 }
996 }
997
998 fn check_unfinished_errors(&mut self) {
999 use oxc_span::GetSpan;
1000 for expr in self.state.cover_initialized_name.values() {
1003 self.errors.push(diagnostics::cover_initialized_name(expr.span()));
1004 }
1005 }
1006
1007 #[cold]
1010 fn overlong_error(&self) -> Option<ParserDiagnostic<'a>> {
1011 if self.source_text.len() > MAX_LEN {
1012 return Some(diagnostics::overlong_source());
1013 }
1014 None
1015 }
1016
1017 #[inline]
1018 fn alloc<T>(&self, value: T) -> ArenaBox<'a, T> {
1019 ArenaBox::new_in(value, self)
1020 }
1021}
1022
1023impl<'a, C: ParserConfig> GetAllocator<'a> for ParserImpl<'a, C> {
1024 #[inline]
1025 fn allocator(&self) -> &'a Allocator {
1026 self.ast.allocator()
1027 }
1028}
1029
1030impl<'a, C: ParserConfig> GetAstBuilder<'a> for ParserImpl<'a, C> {
1031 type Builder = ParserAstBuilder<'a>;
1032
1033 #[inline]
1034 fn builder(&self) -> &ParserAstBuilder<'a> {
1035 &self.ast
1036 }
1037}
1038
1039#[cfg(test)]
1040mod test {
1041 use std::path::Path;
1042
1043 use oxc_ast::ast::{
1044 CommentKind, Expression, Statement, TSNamespaceDeclarationBody, TSNamespaceDeclarationKind,
1045 };
1046 use oxc_span::GetSpan;
1047
1048 use super::*;
1049
1050 #[test]
1051 fn parse_program_smoke_test() {
1052 let allocator = Allocator::default();
1053 let source_type = SourceType::default();
1054 let source = "";
1055 let ret = Parser::new(&allocator, source, source_type).parse();
1056 assert!(ret.program.is_empty());
1057 assert!(ret.diagnostics.is_empty());
1058 assert!(!ret.is_flow_language);
1059 }
1060
1061 #[test]
1062 fn parse_expression_smoke_test() {
1063 let allocator = Allocator::default();
1064 let source_type = SourceType::default();
1065 let source = "a";
1066 let expr = Parser::new(&allocator, source, source_type).parse_expression().unwrap();
1067 assert!(matches!(expr, Expression::Identifier(_)));
1068 }
1069
1070 #[test]
1071 fn parse_expression_rejects_trailing_tokens() {
1072 let allocator = Allocator::default();
1073 let source_type = SourceType::default();
1074 for source in ["a b", "a;", "let x = 1"] {
1075 assert!(Parser::new(&allocator, source, source_type).parse_expression().is_err());
1076 }
1077 }
1078
1079 #[test]
1080 fn flow_error() {
1081 let allocator = Allocator::default();
1082 let source_type = SourceType::default();
1083 let sources = [
1084 "// @flow\nasdf adsf",
1085 "/* @flow */\n asdf asdf",
1086 "/**
1087 * @flow
1088 */
1089 asdf asdf
1090 ",
1091 "/* @flow */ super;",
1092 ];
1093 for source in sources {
1094 let ret = Parser::new(&allocator, source, source_type).parse();
1095 assert!(ret.is_flow_language);
1096 assert_eq!(ret.diagnostics.len(), 1);
1097 assert_eq!(ret.diagnostics.first().unwrap().to_string(), "Flow is not supported");
1098 }
1099 }
1100
1101 #[test]
1102 fn ts_module_declaration() {
1103 let allocator = Allocator::default();
1104 let source_type = SourceType::from_path(Path::new("module.ts")).unwrap();
1105 let source =
1106 "declare module 'test'; namespace Foo.Bar {} module Baz {} declare module 'raw' {}";
1107 let ret = Parser::new(&allocator, source, source_type).parse();
1108 assert!(ret.diagnostics.is_empty());
1109
1110 let Statement::TSExternalModuleDeclaration(external) = &ret.program.body[0] else {
1111 panic!("expected external module declaration");
1112 };
1113 assert_eq!(external.id.value, "test");
1114 assert!(external.body.is_none());
1115 assert!(external.declare);
1116
1117 let Statement::TSNamespaceDeclaration(namespace) = &ret.program.body[1] else {
1118 panic!("expected namespace declaration");
1119 };
1120 assert_eq!(namespace.id.name, "Foo");
1121 assert_eq!(namespace.kind, TSNamespaceDeclarationKind::Namespace);
1122 let TSNamespaceDeclarationBody::TSNamespaceDeclaration(inner) = &namespace.body else {
1123 panic!("expected dotted namespace declaration");
1124 };
1125 assert_eq!(inner.id.name, "Bar");
1126
1127 let Statement::TSNamespaceDeclaration(module) = &ret.program.body[2] else {
1128 panic!("expected identifier module declaration");
1129 };
1130 assert_eq!(module.id.name, "Baz");
1131 assert_eq!(module.kind, TSNamespaceDeclarationKind::Module);
1132
1133 let Statement::TSExternalModuleDeclaration(external) = &ret.program.body[3] else {
1134 panic!("expected external module declaration");
1135 };
1136 assert_eq!(external.id.value, "raw");
1137 assert!(external.body.is_some());
1138 assert!(external.declare);
1139 }
1140
1141 #[test]
1142 fn surrogate_halves_join_across_escape_forms() {
1143 let allocator = Allocator::default();
1144 let source_type = SourceType::default();
1145 let escapes = [
1147 r"\uD83D\uDE00",
1148 r"\u{D83D}\u{DE00}",
1149 r"\uD83D\u{DE00}",
1150 r"\u{D83D}\uDE00",
1151 "\\uD83D\\\n\\uDE00",
1153 ];
1154 for escape in escapes {
1155 let source = format!("'{escape}'");
1156 let expr = Parser::new(&allocator, &source, source_type).parse_expression().unwrap();
1157 let Expression::StringLiteral(literal) = expr else { panic!("{source}") };
1158 assert_eq!(literal.value.as_str(), Some("😀"), "{source}");
1159
1160 let source = format!("`{escape}`");
1161 let expr = Parser::new(&allocator, &source, source_type).parse_expression().unwrap();
1162 let Expression::TemplateLiteral(template) = expr else { panic!("{source}") };
1163 let cooked = template.quasis[0].value.cooked.and_then(oxc_str::JSStr::as_str);
1164 assert_eq!(cooked, Some("😀"), "{source}");
1165 }
1166 }
1167
1168 #[test]
1169 fn directives() {
1170 let allocator = Allocator::default();
1171 let source_type = SourceType::default();
1172 let sources = [
1173 ("import x from 'foo'; 'use strict';", 2),
1174 ("export {x} from 'foo'; 'use strict';", 2),
1175 (";'use strict';", 2),
1176 ];
1177 for (source, body_length) in sources {
1178 let ret = Parser::new(&allocator, source, source_type).parse();
1179 assert!(ret.program.directives.is_empty(), "{source}");
1180 assert_eq!(ret.program.body.len(), body_length, "{source}");
1181 }
1182 }
1183
1184 #[test]
1185 fn v8_intrinsics() {
1186 let allocator = Allocator::default();
1187 let source_type = SourceType::default();
1188 {
1189 let source = "%DebugPrint('Raging against the Dying Light')";
1190 let opts = ParseOptions { allow_v8_intrinsics: true, ..ParseOptions::default() };
1191 let ret = Parser::new(&allocator, source, source_type).with_options(opts).parse();
1192 assert!(ret.diagnostics.is_empty());
1193
1194 if let Some(Statement::ExpressionStatement(expr_stmt)) = ret.program.body.first() {
1195 if let Expression::V8IntrinsicExpression(expr) = &expr_stmt.expression {
1196 assert_eq!(expr.span().source_text(source), source);
1197 } else {
1198 panic!("Expected V8IntrinsicExpression");
1199 }
1200 } else {
1201 panic!("Expected ExpressionStatement");
1202 }
1203 }
1204 {
1205 let source = "%DebugPrint(...illegalSpread)";
1206 let opts = ParseOptions { allow_v8_intrinsics: true, ..ParseOptions::default() };
1207 let ret = Parser::new(&allocator, source, source_type).with_options(opts).parse();
1208 assert_eq!(ret.diagnostics.len(), 1);
1209 assert_eq!(
1210 ret.diagnostics[0].to_string(),
1211 "V8 runtime calls cannot have spread elements as arguments"
1212 );
1213 }
1214 {
1215 let source = "%DebugPrint('~~')";
1216 let ret = Parser::new(&allocator, source, source_type).parse();
1217 assert_eq!(ret.diagnostics.len(), 1);
1218 assert_eq!(ret.diagnostics[0].to_string(), "Unexpected token");
1219 }
1220 {
1221 let source = "interface Props extends %enuProps {}";
1223 let source_type = SourceType::default().with_typescript(true);
1224 let opts = ParseOptions { allow_v8_intrinsics: true, ..ParseOptions::default() };
1226 let ret = Parser::new(&allocator, source, source_type).with_options(opts).parse();
1227 assert_eq!(ret.diagnostics.len(), 1);
1228 let ret = Parser::new(&allocator, source, source_type).parse();
1229 assert_eq!(ret.diagnostics.len(), 1);
1230 }
1231 }
1232
1233 #[test]
1234 fn comments() {
1235 let allocator = Allocator::default();
1236 let source_type = SourceType::default().with_typescript(true);
1237 let sources = [
1238 ("// line comment", CommentKind::Line),
1239 ("/* line comment */", CommentKind::SingleLineBlock),
1240 (
1241 "type Foo = ( /* Require properties which are not generated automatically. */ 'bar')",
1242 CommentKind::SingleLineBlock,
1243 ),
1244 ];
1245 for (source, kind) in sources {
1246 let ret = Parser::new(&allocator, source, source_type).parse();
1247 let comments = &ret.program.comments;
1248 assert_eq!(comments.len(), 1, "{source}");
1249 assert_eq!(comments.first().unwrap().kind, kind, "{source}");
1250 }
1251 }
1252
1253 #[test]
1254 fn hashbang() {
1255 let allocator = Allocator::default();
1256 let source_type = SourceType::default();
1257 let source = "#!/usr/bin/node\n;";
1258 let ret = Parser::new(&allocator, source, source_type).parse();
1259 assert_eq!(ret.program.hashbang.unwrap().value.as_str(), "/usr/bin/node");
1260 }
1261
1262 #[test]
1263 fn unambiguous() {
1264 let allocator = Allocator::default();
1265 let source_type = SourceType::unambiguous();
1266 assert!(source_type.is_unambiguous());
1267 let sources = ["import x from 'foo';", "export {x} from 'foo';", "import.meta"];
1268 for source in sources {
1269 let ret = Parser::new(&allocator, source, source_type).parse();
1270 assert!(ret.program.source_type.is_module());
1271 }
1272
1273 let sources = ["", "import('foo')"];
1274 for source in sources {
1275 let ret = Parser::new(&allocator, source, source_type).parse();
1276 assert!(ret.program.source_type.is_script());
1277 }
1278 }
1279
1280 #[test]
1281 fn binary_file() {
1282 let allocator = Allocator::default();
1283 let source_type = SourceType::default();
1284
1285 let ret = Parser::new(&allocator, "\u{FFFD}", source_type).parse();
1287 assert!(ret.program.is_empty());
1288 assert_eq!(ret.diagnostics.len(), 1);
1289 assert_eq!(ret.diagnostics[0].to_string(), "File appears to be binary.");
1290
1291 let ret = Parser::new(&allocator, "\"oops \u{FFFD} oops\";", source_type).parse();
1293 assert!(!ret.program.is_empty());
1294 assert!(ret.diagnostics.is_empty());
1295 }
1296
1297 #[test]
1298 fn memory_leak() {
1299 let allocator = Allocator::default();
1300 let source_type = SourceType::default();
1301 let sources = ["2n", ";'1234567890123456789012345678901234567890'"];
1302 for source in sources {
1303 let ret = Parser::new(&allocator, source, source_type).parse();
1304 assert!(!ret.program.body.is_empty());
1305 }
1306 }
1307
1308 #[cfg(target_pointer_width = "64")]
1312 #[cfg(not(miri))]
1313 #[test]
1314 fn overlong_source() {
1315 use std::{
1316 alloc::{self, Layout},
1317 ptr::NonNull,
1318 slice, str,
1319 };
1320
1321 struct ZeroedString {
1327 ptr: NonNull<u8>,
1328 }
1329
1330 impl ZeroedString {
1331 const LEN: usize = MAX_LEN + 1;
1332 const PAGE_SIZE: usize = 4096;
1333 const LAYOUT: Layout = match Layout::from_size_align(Self::LEN, Self::PAGE_SIZE) {
1334 Ok(layout) => layout,
1335 Err(_) => panic!("Failed to create layout"),
1336 };
1337
1338 fn new() -> Self {
1339 let ptr = unsafe { alloc::alloc_zeroed(Self::LAYOUT) };
1341 let Some(ptr) = NonNull::new(ptr) else {
1342 panic!("Failed to allocate {} bytes", Self::LEN);
1343 };
1344 Self { ptr }
1345 }
1346
1347 fn as_str(&self) -> &str {
1348 unsafe {
1351 str::from_utf8_unchecked(slice::from_raw_parts(self.ptr.as_ptr(), Self::LEN))
1352 }
1353 }
1354 }
1355
1356 impl Drop for ZeroedString {
1357 fn drop(&mut self) {
1358 unsafe { alloc::dealloc(self.ptr.as_ptr(), Self::LAYOUT) };
1360 }
1361 }
1362
1363 let zeroed_string = ZeroedString::new();
1365 let source_text = zeroed_string.as_str();
1366
1367 let allocator = Allocator::default();
1369 let ret = Parser::new(&allocator, source_text, SourceType::default()).parse();
1370
1371 assert!(ret.program.is_empty());
1373 assert!(ret.fatal_error);
1374 assert_eq!(ret.diagnostics.len(), 1);
1375 assert_eq!(
1376 ret.diagnostics.first().unwrap().to_string(),
1377 "Source length exceeds 4 GiB limit"
1378 );
1379 }
1380
1381 #[cfg(not(debug_assertions))]
1386 #[cfg(not(miri))]
1387 #[test]
1388 fn legal_length_source() {
1389 let head = "const x = 1;\n/*";
1391 let foot = "*/\nconst y = 2;\n";
1392 let mut source = "x".repeat(MAX_LEN);
1393 source.replace_range(..head.len(), head);
1394 source.replace_range(MAX_LEN - foot.len().., foot);
1395 assert_eq!(source.len(), MAX_LEN);
1396
1397 let allocator = Allocator::default();
1398 let ret = Parser::new(&allocator, &source, SourceType::default()).parse();
1399 assert!(!ret.fatal_error);
1400 assert!(ret.diagnostics.is_empty());
1401 assert_eq!(ret.program.body.len(), 2);
1402 }
1403
1404 #[test]
1408 fn tokens_when_lexing_ends_in_error() {
1409 let allocator = Allocator::default();
1410 let ret = Parser::new(&allocator, "foo();\n'unterminated", SourceType::default())
1411 .with_config(config::TokensParserConfig)
1412 .parse();
1413
1414 assert!(!ret.fatal_error);
1415 assert_eq!(ret.diagnostics.len(), 1);
1416 assert_eq!(ret.diagnostics.first().unwrap().to_string(), "Unterminated string");
1417
1418 let tokens = ret
1419 .tokens
1420 .iter()
1421 .map(|token| (token.kind(), token.start(), token.end()))
1422 .collect::<Vec<_>>();
1423 assert_eq!(
1424 tokens,
1425 [
1426 (Kind::Ident, 0, 3),
1427 (Kind::LParen, 3, 4),
1428 (Kind::RParen, 4, 5),
1429 (Kind::Semicolon, 5, 6),
1430 ]
1431 );
1432 }
1433
1434 #[test]
1435 fn tokens_after_unambiguous_await_reparse() {
1436 for source in [
1437 "await /x/u; export {};",
1438 "await /a/ /b/g; export {};",
1440 "before(); await /a/ /b/g; between(); await /x/u; export {}; tail();",
1441 "before(); await /x/u; after(); export {}; tail();",
1442 "before(); await /x/u; between(); await /y/g; export {}; tail();",
1443 "await /x/u\nawait /y/g\nexport {};",
1444 "await\nawait /x/u; export {};",
1445 "before(); await\nawait /x/u; between(); await\nawait /y/g; export {}; tail();",
1446 "const x = await /x/u; between(); const y = await /y/g; export {};",
1447 "/before/.test(await /x/u); /after/.test('after'); export {};",
1448 "await /x/u, import.meta;",
1449 "#!/usr/bin/env node\n'use strict';\nawait /* regexp */ /x/u; export {};",
1450 "export {}; await /x/u;",
1451 ] {
1452 let allocator = Allocator::default();
1453 let parse = |source_type| {
1454 Parser::new(&allocator, source, source_type)
1455 .with_config(config::TokensParserConfig)
1456 .parse()
1457 };
1458 let unambiguous = parse(SourceType::unambiguous());
1459 let module = parse(SourceType::mjs());
1460 assert_eq!(unambiguous.tokens.as_slice(), module.tokens.as_slice(), "{source}");
1461 }
1462 }
1463
1464 #[test]
1468 fn tokens_when_template_substitution_fails() {
1469 let allocator = Allocator::default();
1470 let ret = Parser::new(&allocator, "`${}`", SourceType::default())
1471 .with_config(config::TokensParserConfig)
1472 .parse();
1473
1474 assert!(ret.fatal_error);
1475 assert!(ret.tokens.is_empty());
1476 }
1477}