From 5c1622ade227ebdff6f28fdd41e5490624f3dbd3 Mon Sep 17 00:00:00 2001 From: Boshen Date: Sat, 11 Feb 2023 20:37:10 +0800 Subject: [PATCH] feat(parser): add parser --- Cargo.toml | 1 + crates/oxc_ast/src/ast/js.rs | 26 +- crates/oxc_ast/src/ast/jsdoc.rs | 2 +- crates/oxc_ast/src/ast/jsx.rs | 3 +- crates/oxc_ast/src/ast/ts.rs | 8 +- crates/oxc_ast/src/lib.rs | 11 +- crates/oxc_ast/src/node.rs | 3 +- crates/oxc_ast/src/source_type.rs | 12 + .../oxc_ast/src/syntax_directed_operations.rs | 291 +++++ crates/oxc_diagnostics/src/lib.rs | 518 +++++++- crates/oxc_parser/Cargo.toml | 3 + crates/oxc_parser/src/cursor.rs | 311 +++++ crates/oxc_parser/src/js/binding.rs | 134 +++ crates/oxc_parser/src/js/class.rs | 462 +++++++ crates/oxc_parser/src/js/declaration.rs | 104 ++ crates/oxc_parser/src/js/expression.rs | 1010 ++++++++++++++++ crates/oxc_parser/src/js/function.rs | 491 ++++++++ crates/oxc_parser/src/js/grammar.rs | 180 +++ crates/oxc_parser/src/js/list.rs | 479 ++++++++ crates/oxc_parser/src/js/mod.rs | 16 + crates/oxc_parser/src/js/module.rs | 412 +++++++ crates/oxc_parser/src/js/object.rs | 260 ++++ crates/oxc_parser/src/js/operator.rs | 145 +++ crates/oxc_parser/src/js/statement.rs | 507 ++++++++ crates/oxc_parser/src/jsx/mod.rs | 363 ++++++ crates/oxc_parser/src/lexer/mod.rs | 2 +- crates/oxc_parser/src/lib.rs | 176 ++- crates/oxc_parser/src/list.rs | 74 ++ crates/oxc_parser/src/state.rs | 17 + crates/oxc_parser/src/ts/list.rs | 161 +++ crates/oxc_parser/src/ts/mod.rs | 5 + crates/oxc_parser/src/ts/statement.rs | 512 ++++++++ crates/oxc_parser/src/ts/types.rs | 1070 +++++++++++++++++ 33 files changed, 7729 insertions(+), 40 deletions(-) create mode 100644 crates/oxc_ast/src/syntax_directed_operations.rs create mode 100644 crates/oxc_parser/src/cursor.rs create mode 100644 crates/oxc_parser/src/js/binding.rs create mode 100644 crates/oxc_parser/src/js/class.rs create mode 100644 crates/oxc_parser/src/js/declaration.rs create mode 100644 crates/oxc_parser/src/js/expression.rs create mode 100644 crates/oxc_parser/src/js/function.rs create mode 100644 crates/oxc_parser/src/js/grammar.rs create mode 100644 crates/oxc_parser/src/js/list.rs create mode 100644 crates/oxc_parser/src/js/mod.rs create mode 100644 crates/oxc_parser/src/js/module.rs create mode 100644 crates/oxc_parser/src/js/object.rs create mode 100644 crates/oxc_parser/src/js/operator.rs create mode 100644 crates/oxc_parser/src/js/statement.rs create mode 100644 crates/oxc_parser/src/jsx/mod.rs create mode 100644 crates/oxc_parser/src/list.rs create mode 100644 crates/oxc_parser/src/state.rs create mode 100644 crates/oxc_parser/src/ts/list.rs create mode 100644 crates/oxc_parser/src/ts/mod.rs create mode 100644 crates/oxc_parser/src/ts/statement.rs create mode 100644 crates/oxc_parser/src/ts/types.rs diff --git a/Cargo.toml b/Cargo.toml index a362cab4816af..775e6f72dee2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ bumpalo = "3.12.0" compact_str = "0.6.1" miette = "5.5.0" rayon = "1.6.1" +rustc-hash = "1.1.0" serde = "1.0.152" serde_json = "1.0.93" thiserror = "1.0.38" diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 946b6908c1bd8..4de94e04e1a79 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -3,17 +3,8 @@ use std::fmt::Display; use oxc_allocator::{Box, Vec}; use serde::Serialize; -use super::{ - AssignmentOperator, BigintLiteral, BinaryOperator, BooleanLiteral, Decorator, - ImportOrExportKind, JSXElement, JSXFragment, LogicalOperator, NullLiteral, NumberLiteral, - RegExpLiteral, StringLiteral, TSAbstractMethodDefinition, TSAbstractPropertyDefinition, - TSAccessibility, TSAsExpression, TSClassImplements, TSEnumDeclaration, TSExportAssignment, - TSImportEqualsDeclaration, TSIndexSignature, TSInstantiationExpression, TSInterfaceDeclaration, - TSModuleDeclaration, TSNamespaceExportDeclaration, TSNonNullExpression, TSTypeAliasDeclaration, - TSTypeAnnotation, TSTypeAssertion, TSTypeParameterDeclaration, TSTypeParameterInstantiation, - UnaryOperator, UpdateOperator, -}; -use crate::{Atom, Node, SourceType}; +#[allow(clippy::wildcard_imports)] +use crate::{ast::*, Atom, Node, SourceType}; #[derive(Debug, PartialEq, Hash)] pub struct Program<'a> { @@ -912,7 +903,6 @@ pub struct VariableDeclarator<'a> { pub kind: VariableDeclarationKind, pub id: BindingPattern<'a>, pub init: Option>, - #[serde(skip_serializing_if = "crate::is_false")] pub definite: bool, } @@ -1124,7 +1114,6 @@ pub struct BindingPattern<'a> { pub kind: BindingPatternKind<'a>, #[serde(skip_serializing_if = "Option::is_none")] pub type_annotation: Option>, - #[serde(skip_serializing_if = "crate::is_false")] pub optional: bool, } @@ -1205,7 +1194,6 @@ pub struct Function<'a> { pub params: FormalParameters<'a>, #[serde(skip_serializing_if = "Option::is_none")] pub body: Option>>, - #[serde(skip_serializing_if = "crate::is_false")] pub declare: bool, #[serde(skip_serializing_if = "Option::is_none")] pub type_parameters: Option>>, @@ -1262,7 +1250,6 @@ pub struct FormalParameter<'a> { pub pattern: BindingPattern<'a>, #[serde(skip_serializing_if = "Option::is_none")] pub accessibility: Option, - #[serde(skip_serializing_if = "crate::is_false")] pub readonly: bool, #[serde(skip_serializing_if = "Option::is_none")] pub decorators: Option>>, @@ -1341,11 +1328,9 @@ pub struct Class<'a> { pub super_type_parameters: Option>>, #[serde(skip_serializing_if = "Option::is_none")] pub implements: Option>>>, - #[serde(skip_serializing_if = "crate::is_false")] pub r#abstract: bool, #[serde(skip_serializing_if = "Option::is_none")] pub decorators: Option>>, - #[serde(skip_serializing_if = "crate::is_false")] pub declare: bool, } @@ -1461,9 +1446,7 @@ pub struct MethodDefinition<'a> { pub kind: MethodDefinitionKind, pub computed: bool, pub r#static: bool, - #[serde(skip_serializing_if = "crate::is_false")] pub r#override: bool, - #[serde(skip_serializing_if = "crate::is_false")] pub optional: bool, #[serde(skip_serializing_if = "Option::is_none")] pub accessibility: Option, @@ -1481,15 +1464,10 @@ pub struct PropertyDefinition<'a> { pub value: Option>, pub computed: bool, pub r#static: bool, - #[serde(skip_serializing_if = "crate::is_false")] pub declare: bool, - #[serde(skip_serializing_if = "crate::is_false")] pub r#override: bool, - #[serde(skip_serializing_if = "crate::is_false")] pub optional: bool, - #[serde(skip_serializing_if = "crate::is_false")] pub definite: bool, - #[serde(skip_serializing_if = "crate::is_false")] pub readonly: bool, #[serde(skip_serializing_if = "Option::is_none")] pub type_annotation: Option>, diff --git a/crates/oxc_ast/src/ast/jsdoc.rs b/crates/oxc_ast/src/ast/jsdoc.rs index 726be7494597a..5fcb3bec48336 100644 --- a/crates/oxc_ast/src/ast/jsdoc.rs +++ b/crates/oxc_ast/src/ast/jsdoc.rs @@ -2,7 +2,7 @@ use serde::Serialize; -use crate::{Node, TSType}; +use crate::{ast::TSType, Node}; #[derive(Debug, Serialize, PartialEq, Hash)] #[serde(tag = "type", rename_all = "camelCase")] diff --git a/crates/oxc_ast/src/ast/jsx.rs b/crates/oxc_ast/src/ast/jsx.rs index 86eff93f0302a..4be797ec3417d 100644 --- a/crates/oxc_ast/src/ast/jsx.rs +++ b/crates/oxc_ast/src/ast/jsx.rs @@ -3,7 +3,8 @@ use oxc_allocator::{Box, Vec}; use serde::Serialize; -use crate::{Atom, Expression, Node, StringLiteral, TSTypeParameterInstantiation}; +#[allow(clippy::wildcard_imports)] +use crate::{ast::*, Atom, Node}; // 1.2 JSX Elements diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index b2ed62f575724..e37ffa13a78f3 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -3,12 +3,8 @@ use oxc_allocator::{Box, Vec}; use serde::Serialize; -use crate::{ - Atom, BigintLiteral, BindingIdentifier, BooleanLiteral, Expression, FormalParameters, - IdentifierName, JSDocNullableType, JSDocUnknownType, MethodDefinition, Node, NullLiteral, - NumberLiteral, PropertyDefinition, PropertyKey, RegExpLiteral, Statement, StringLiteral, - TemplateElement, TemplateLiteral, UnaryExpression, -}; +#[allow(clippy::wildcard_imports)] +use crate::{ast::*, Atom, Node}; #[allow(clippy::trivially_copy_pass_by_ref)] #[must_use] diff --git a/crates/oxc_ast/src/lib.rs b/crates/oxc_ast/src/lib.rs index 875679db87732..54d0aca9ed42a 100644 --- a/crates/oxc_ast/src/lib.rs +++ b/crates/oxc_ast/src/lib.rs @@ -11,13 +11,13 @@ pub mod ast_builder; pub mod context; pub mod node; pub mod source_type; +pub mod syntax_directed_operations; pub use num_bigint::BigUint; -pub use self::ast::*; -pub use self::ast_builder::*; -pub use self::node::*; -pub use self::source_type::*; +pub use crate::ast_builder::*; +pub use crate::node::*; +pub use crate::source_type::*; pub type Atom = compact_str::CompactString; @@ -47,6 +47,9 @@ pub type Atom = compact_str::CompactString; #[test] fn no_bloat_enum_sizes() { use std::mem::size_of; + + #[allow(clippy::wildcard_imports)] + use crate::ast::*; assert_eq!(size_of::(), 16); assert_eq!(size_of::(), 16); assert_eq!(size_of::(), 16); diff --git a/crates/oxc_ast/src/node.rs b/crates/oxc_ast/src/node.rs index 2cde33c64d5b3..821622d9a2db4 100644 --- a/crates/oxc_ast/src/node.rs +++ b/crates/oxc_ast/src/node.rs @@ -5,7 +5,8 @@ use std::{ use serde::Serialize; -use crate::{context::Context, Argument, AssignmentTarget, AssignmentTargetPattern, BindingPattern, BindingPatternKind, ClassElement, Declaration, ExportDefaultDeclarationKind, Expression, ForStatementInit, ImportAttributeKey, JSXElementName, MemberExpression, ModuleExportName, ObjectPatternProperty, ObjectProperty, PropertyKey, PropertyValue, SimpleAssignmentTarget, Statement, TSModuleDeclarationName, TSSignature, TSType}; +#[allow(clippy::wildcard_imports)] +use crate::{ast::*, context::Context}; pub type Span = Range; diff --git a/crates/oxc_ast/src/source_type.rs b/crates/oxc_ast/src/source_type.rs index 598b52f35c96f..877ad137b6848 100644 --- a/crates/oxc_ast/src/source_type.rs +++ b/crates/oxc_ast/src/source_type.rs @@ -2,6 +2,8 @@ use std::path::Path; use thiserror::Error; +use crate::context::Context; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct SourceType { /// JavaScript or TypeScript, default JavaScript @@ -51,6 +53,16 @@ impl Default for SourceType { pub const VALID_EXTENSIONS: [&str; 8] = ["js", "mjs", "cjs", "jsx", "ts", "mts", "cts", "tsx"]; impl SourceType { + #[must_use] + pub fn default_context(&self) -> Context { + let ctx = Context::default().and_ambient(self.is_typescript_definition()); + match self.module_kind { + ModuleKind::Script => ctx, + // for [top-level-await](https://tc39.es/proposal-top-level-await/) + ModuleKind::Module => ctx.and_await(true), + } + } + #[must_use] pub fn builder() -> SourceTypeBuilder { SourceTypeBuilder::default() diff --git a/crates/oxc_ast/src/syntax_directed_operations.rs b/crates/oxc_ast/src/syntax_directed_operations.rs new file mode 100644 index 0000000000000..81ee6b5a3e7f8 --- /dev/null +++ b/crates/oxc_ast/src/syntax_directed_operations.rs @@ -0,0 +1,291 @@ +//! [Syntax-Directed Operations](https://tc39.es/ecma262/#sec-syntax-directed-operations) + +#[allow(clippy::wildcard_imports)] +use crate::{ast::*, Node}; + +/// [`BoundName`](https://tc39.es/ecma262/#sec-static-semantics-boundnames) +pub trait BoundName { + fn bound_name(&self) -> Option<&BindingIdentifier>; +} + +pub trait BoundNames { + fn bound_names(&self) -> Vec<&BindingIdentifier>; +} + +impl<'a> BoundNames for BindingPattern<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + match &self.kind { + BindingPatternKind::BindingIdentifier(ident) => ident.bound_names(), + BindingPatternKind::ArrayPattern(array) => array.bound_names(), + BindingPatternKind::ObjectPattern(object) => object.bound_names(), + BindingPatternKind::AssignmentPattern(assignment) => assignment.bound_names(), + BindingPatternKind::RestElement(rest) => rest.argument.bound_names(), + } + } +} + +impl<'a> BoundNames for Option> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + self.as_ref().map_or(vec![], BoundNames::bound_names) + } +} + +impl BoundNames for Option { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + self.as_ref().map_or(vec![], |ident| vec![ident]) + } +} + +impl BoundNames for BindingIdentifier { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + vec![self] + } +} + +impl<'a> BoundNames for ArrayPattern<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + self.elements.iter().flat_map(BoundNames::bound_names).collect() + } +} + +impl<'a> BoundNames for ObjectPattern<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + self.properties + .iter() + .flat_map(|p| match p { + ObjectPatternProperty::Property(property) => { + if let PropertyValue::Pattern(pattern) = &property.value { + pattern.bound_names() + } else { + vec![] + } + } + ObjectPatternProperty::RestElement(rest) => rest.argument.bound_names(), + }) + .collect() + } +} + +impl<'a> BoundNames for AssignmentPattern<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + self.left.bound_names() + } +} + +impl<'a> BoundNames for RestElement<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + self.argument.bound_names() + } +} + +impl<'a> BoundNames for FormalParameters<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + self.items.iter().flat_map(BoundNames::bound_names).collect() + } +} + +impl<'a> BoundNames for Declaration<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + match self { + Declaration::VariableDeclaration(decl) => decl.bound_names(), + Declaration::FunctionDeclaration(func) => func.bound_names(), + Declaration::ClassDeclaration(decl) => decl.bound_names(), + _ => vec![], + } + } +} + +impl<'a> BoundNames for VariableDeclaration<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + self.declarations.iter().flat_map(|declarator| declarator.id.bound_names()).collect() + } +} + +impl<'a> BoundName for Function<'a> { + fn bound_name(&self) -> Option<&BindingIdentifier> { + self.id.as_ref() + } +} + +impl<'a> BoundNames for Function<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + self.bound_name().map_or(vec![], |name| vec![name]) + } +} + +impl<'a> BoundName for Class<'a> { + fn bound_name(&self) -> Option<&BindingIdentifier> { + self.id.as_ref() + } +} + +impl<'a> BoundNames for Class<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + self.bound_name().map_or(vec![], |name| vec![name]) + } +} + +impl<'a> BoundNames for FormalParameter<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + self.pattern.bound_names() + } +} + +impl<'a> BoundNames for ModuleDeclaration<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + match &self.kind { + ModuleDeclarationKind::ImportDeclaration(decl) => decl.bound_names(), + // ModuleDeclarationKind::ExportNamedDeclaration(decl) => decl.bound_names(), + _ => vec![], + } + } +} + +impl<'a> BoundNames for ImportDeclaration<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + self.specifiers + .iter() + .map(|specifier| match specifier { + ImportDeclarationSpecifier::ImportSpecifier(specifier) => &specifier.local, + ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => &specifier.local, + ImportDeclarationSpecifier::ImportNamespaceSpecifier(specifier) => &specifier.local, + }) + .collect() + } +} + +impl<'a> BoundNames for ExportNamedDeclaration<'a> { + fn bound_names(&self) -> Vec<&BindingIdentifier> { + self.declaration.as_ref().map_or(vec![], BoundNames::bound_names) + } +} + +/// [`IsSimpleParameterList`](https://tc39.es/ecma262/#sec-static-semantics-issimpleparameterlist) +pub trait IsSimpleParameterList { + fn is_simple_parameter_list(&self) -> bool; +} + +impl<'a> IsSimpleParameterList for FormalParameters<'a> { + fn is_simple_parameter_list(&self) -> bool { + self.items + .iter() + .all(|pat| matches!(pat.pattern.kind, BindingPatternKind::BindingIdentifier(_))) + } +} + +/// [`PropName`](https://tc39.es/ecma262/#sec-static-semantics-propname) +pub trait PropName { + fn prop_name(&self) -> Option<(&str, Node)>; +} + +impl<'a> PropName for ObjectProperty<'a> { + fn prop_name(&self) -> Option<(&str, Node)> { + match self { + ObjectProperty::Property(prop) => prop.prop_name(), + ObjectProperty::SpreadProperty(_) => None, + } + } +} + +impl<'a> PropName for Property<'a> { + fn prop_name(&self) -> Option<(&str, Node)> { + if self.kind != PropertyKind::Init || self.method || self.shorthand || self.computed { + return None; + } + self.key.prop_name() + } +} + +impl<'a> PropName for PropertyKey<'a> { + fn prop_name(&self) -> Option<(&str, Node)> { + match self { + PropertyKey::Identifier(ident) => Some((&ident.name, ident.node)), + PropertyKey::PrivateIdentifier(_) => None, + PropertyKey::Expression(expr) => match &expr { + Expression::Identifier(ident) => Some((&ident.name, ident.node)), + Expression::StringLiteral(lit) => Some((&lit.value, lit.node)), + _ => None, + }, + } + } +} + +impl<'a> PropName for ClassElement<'a> { + fn prop_name(&self) -> Option<(&str, Node)> { + match self { + ClassElement::MethodDefinition(def) => def.prop_name(), + ClassElement::TSAbstractMethodDefinition(def) => def.method_definition.prop_name(), + ClassElement::PropertyDefinition(def) => def.prop_name(), + ClassElement::TSAbstractPropertyDefinition(def) => def.property_definition.prop_name(), + _ => None, + } + } +} + +impl<'a> PropName for MethodDefinition<'a> { + fn prop_name(&self) -> Option<(&str, Node)> { + if self.computed { + return None; + } + self.key.prop_name() + } +} + +impl<'a> PropName for PropertyDefinition<'a> { + fn prop_name(&self) -> Option<(&str, Node)> { + if self.computed { + return None; + } + self.key.prop_name() + } +} + +/// [`PrivateBoundIdentifiers`](https://tc39.es/ecma262/#sec-static-semantics-privateboundidentifiers) +pub trait PrivateBoundIdentifiers { + fn private_bound_identifiers(&self) -> Option; +} + +impl<'a> PrivateBoundIdentifiers for ClassElement<'a> { + fn private_bound_identifiers(&self) -> Option { + match self { + ClassElement::StaticBlock(_) | ClassElement::TSIndexSignature(_) => None, + ClassElement::MethodDefinition(def) => def.private_bound_identifiers(), + ClassElement::PropertyDefinition(def) => def.private_bound_identifiers(), + ClassElement::AccessorProperty(def) => def.private_bound_identifiers(), + ClassElement::TSAbstractMethodDefinition(def) => { + def.method_definition.private_bound_identifiers() + } + ClassElement::TSAbstractPropertyDefinition(def) => { + def.property_definition.private_bound_identifiers() + } + } + } +} + +impl<'a> PrivateBoundIdentifiers for MethodDefinition<'a> { + fn private_bound_identifiers(&self) -> Option { + self.value.body.as_ref()?; + if let PropertyKey::PrivateIdentifier(ident) = &self.key { + return Some((*ident).clone()); + } + None + } +} + +impl<'a> PrivateBoundIdentifiers for PropertyDefinition<'a> { + fn private_bound_identifiers(&self) -> Option { + if let PropertyKey::PrivateIdentifier(ident) = &self.key { + return Some((*ident).clone()); + } + None + } +} + +impl<'a> PrivateBoundIdentifiers for AccessorProperty<'a> { + fn private_bound_identifiers(&self) -> Option { + if let PropertyKey::PrivateIdentifier(ident) = &self.key { + return Some((*ident).clone()); + } + None + } +} diff --git a/crates/oxc_diagnostics/src/lib.rs b/crates/oxc_diagnostics/src/lib.rs index 37cbabfb2fbc2..ac0f6a3cbe9d7 100644 --- a/crates/oxc_diagnostics/src/lib.rs +++ b/crates/oxc_diagnostics/src/lib.rs @@ -2,7 +2,7 @@ use std::{cell::RefCell, ops::Deref, rc::Rc}; -use oxc_ast::Span; +use oxc_ast::{Atom, Span}; use thiserror::Error; pub type Result = std::result::Result; @@ -32,6 +32,10 @@ pub enum Diagnostic { #[diagnostic()] Panic(#[label("")] Span), + #[error("Flow is not supported")] + #[diagnostic()] + Flow(#[label("")] Span), + /* Lexer */ #[error("Syntax Error")] #[diagnostic()] @@ -89,4 +93,516 @@ pub enum Diagnostic { #[error("Keywords cannot contain escape characters")] #[diagnostic()] EscapedKeyword(#[label("keyword cannot contain escape characters")] Span), + /* Syntax Errors */ + #[error("Automatic Semicolon Insertion")] + #[diagnostic(help("Try insert a semicolon here"))] + AutoSemicolonInsertion( + #[label("Expected a semicolon or an implicit semicolon after a statement, but found none")] + Span, + ), + + #[error("Octal literals are not allowed in strict mode")] + #[diagnostic(help("for octal literals use the '0o' prefix instead"))] + LegacyOctal( + #[label("'0'-prefixed octal literals and octal escape sequences are deprecated")] Span, + ), + + #[error("Decimals with leading zeros are not allowed in strict mode")] + #[diagnostic(help("remove the leading zero"))] + LeadingZeroDecimal(#[label("Decimals with leading zeros are not allowed in strict mode")] Span), + + #[error("Line terminator not permitted before arrow")] + #[diagnostic()] + LineterminatorBeforeArrow(#[label("Line terminator not permitted before arrow")] Span), + + #[error("Unexpected new.target expression")] + #[diagnostic(help( + "new.target is only allowed in constructors and functions invoked using thew `new` operator" + ))] + NewTarget(#[label("new.target expression is not allowed here")] Span), + + #[error("The only valid meta property for new is new.target")] + #[diagnostic()] + NewTargetProperty(#[label("The only valid meta property for new is new.target")] Span), + + #[error("Unexpected import.meta expression")] + #[diagnostic(help("import.meta is only allowed in module code"))] + ImportMeta(#[label("import.meta expression is not allowed here")] Span), + + #[error("The only valid meta property for import is import.meta")] + #[diagnostic()] + ImportMetaProperty(#[label("The only valid meta property for import is import.meta")] Span), + + #[error("Illegal break statement")] + #[diagnostic(help( + "A `break` statement can only be used within an enclosing iteration or switch statement." + ))] + InvalidBreak(#[label("break statement is not allowed here")] Span), + + #[error("Illegal continue statement: no surrounding iteration statement")] + #[diagnostic(help( + "A `continue` statement can only be used within an enclosing `for`, `while` or `do while` " + ))] + InvalidContinue(#[label("continue statement is not allowed here")] Span), + + #[error( + "A `{0}` statement can only jump to a label of an enclosing `for`, `while` or `do while` statement." + )] + #[diagnostic()] + InvalidLabelNonIteration( + &'static str, + #[label("This is an non-iteration statement")] Span, + #[label("for this label")] Span, + ), + + #[error("Use of undefined label")] + #[diagnostic()] + InvalidLabelTarget(#[label("This label is used, but not defined")] Span), + + #[error("Jump target cannot cross function boundary.")] + #[diagnostic()] + InvalidLabelJumpTarget(#[label("Jump target cannot cross function boundary.")] Span), + + #[error("Unexpected '{0}' strict mode")] + #[diagnostic()] + UnexpectedIdentifierAssign(Atom, #[label("Cannot assign to '{0}' in strict mode")] Span), + + #[error("Invalid left-hand side in assignment")] + #[diagnostic()] + UnexpectedLhsAssign(#[label("Invalid left-hand side in assignment")] Span), + + #[error("The keyword '{0}' is reserved")] + #[diagnostic()] + ReservedKeyword(Atom, #[label("{0} is reserved")] Span), + + #[error("Identifier `{0}` has already been declared")] + #[diagnostic()] + Redeclaration( + Atom, + #[label("`{0}` has already been declared here")] Span, + #[label("It can not be redeclared here")] Span, + ), + + #[error("{0} is disallowed as a lexically bound name")] + #[diagnostic()] + DisallowedLexicalName(Atom, #[label("{0} is disallowed as a lexically bound name")] Span), + + #[error("`let` cannot be declared as a variable name inside of a `{0}` declaration")] + #[diagnostic()] + InvalidLetDeclaration(String, #[label("Rename the let identifier here")] Span), + + #[error("Missing initializer in destructuring declaration")] + #[diagnostic()] + InvalidDestrucuringDeclaration( + #[label("Missing initializer in destructuring declaration")] Span, + ), + + #[error("Missing initializer in const declaration")] + #[diagnostic()] + MissinginitializerInConst(#[label("const declaration need an initializer")] Span), + + #[error("Functions cannot be labelled")] + #[diagnostic(help("This is not allowed in strict mode starting with ECMAScript 2015."))] + FunctionsCannotBeLabelled(#[label("Functions cannot be labelled")] Span), + + #[error("Cannot use {0} outside a method")] + MethodCode(&'static str, #[label("Cannot use {0} outside a method")] Span), + + #[error("Cannot use {0} outside a module")] + #[diagnostic()] + ModuleCode(&'static str, #[label("Cannot use {0} outside a module")] Span), + + #[error("Lexical declaration cannot appear in a single-statement context")] + #[diagnostic(help("Wrap this declaration in a block statement"))] + LexicalDeclarationSingleStatement(#[label("Lexical declaration is not allowed here")] Span), + + #[error("Invalid function declaration")] + #[diagnostic(help( + "In strict mode code, functions can only be declared at top level or inside a block" + ))] + FunctionDeclarationStrict(#[label("function declaration is not allowed here")] Span), + + #[error("Async functions can only be declared at the top level or inside a block")] + #[diagnostic()] + AsyncFunctionDeclaration( + #[label("Async functions can only be declared at the top level or inside a block")] Span, + ), + + #[error("Generators can only be declared at the top level or inside a block")] + #[diagnostic()] + GeneratorFunctionDeclaration( + #[label("Generators can only be declared at the top level or inside a block")] Span, + ), + + #[error("Invalid function declaration")] + #[diagnostic(help( + "In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement" + ))] + FunctionDeclarationNonStrict(#[label("function declaration is not allowed here")] Span), + + #[error("`await` is only allowed within async functions and at the top levels of modules")] + #[diagnostic()] + AwaitExpression( + #[label("`await` is only allowed within async functions and at the top levels of modules")] + Span, + ), + + #[error("A 'yield' expression is only allowed in a generator body.")] + #[diagnostic()] + YieldExpression(#[label("A 'yield' expression is only allowed in a generator body.")] Span), + + #[error("Invalid class declaration")] + #[diagnostic()] + ClassDeclaration(#[label("Classes can only be declared at top level or inside a block")] Span), + + #[error("Rest element must be last element")] + #[diagnostic()] + RestElement(#[label("Rest element must be last element")] Span), + + #[error("Spread must be last element")] + #[diagnostic()] + SpreadLastElement(#[label("Spread must be last element")] Span), + + #[error("Unexpected trailing comma after rest element")] + #[diagnostic()] + RestElementTraillingComma(#[label("Unexpected trailing comma after rest element")] Span), + + #[error("Invalid rest argument")] + #[diagnostic(help("Expected identifier in rest argument"))] + InvalidRestArgument(#[label("Invalid rest argument")] Span), + + #[error("Invalid parenthesized parameter")] + #[diagnostic(help("remove the parentheses"))] + InvalidParenthesizedParameter(#[label("Invliad parenthesized parameter")] Span), + + #[error("Invalid parenthesized pattern")] + #[diagnostic()] + InvalidParenthesizedPattern(#[label("Invliad parenthesized pattern")] Span), + + #[error("Invalid assignment")] + #[diagnostic()] + InvalidAssignment(#[label("Cannot assign to this expression")] Span), + + #[error("Optional chaining cannot appear in the callee of new expressions")] + #[diagnostic()] + NewOptionalChain( + #[label("Optional chaining cannot appear in the callee of new expressions")] Span, + ), + + #[error("The left-hand side of a `for...of` statement may not be `async`")] + #[diagnostic()] + ForLoopAsyncOf( + #[label("The left-hand side of a `for...of` statement may not be `async`")] Span, + ), + + #[error("await can only be used in conjunction with `for...of` statements")] + #[diagnostic()] + ForAwait(#[label("await can only be used in conjunction with `for...of` statements")] Span), + + #[error("Cannot use new with dynamic import")] + #[diagnostic()] + NewDynamicImport(#[label("Cannot use new with dynamic import")] Span), + + #[error("'{0}' declaration can only be used at the top level of a module")] + #[diagnostic()] + TopLevel(&'static str, #[label("'{0}' declaration can only appear at the top level")] Span), + + #[error("Duplicated export '{0}'")] + #[diagnostic()] + DuplicateExport( + Atom, + #[label("Export has already been declared here")] Span, + #[label("It cannot be redeclared here")] Span, + ), + + #[error("Unexpected private field")] + #[diagnostic(help( + "Private names are only allowed in property accesses (`obj.#field`) or in `in` expressions (`#field in obj`)." + ))] + UnexpectedPrivateIdentifier(#[label("Unexpected private field")] Span), + + #[error("Classes can't have an element named '#constructor'")] + #[diagnostic()] + PrivateNameConstructor(#[label("Classes can't have an element named '#constructor'")] Span), + + #[error("Private field '{0}' must be declared in an enclosing class")] + #[diagnostic()] + PrivateFieldUndeclared( + Atom, + #[label("Private field '{0}' must be declared in an enclosing class")] Span, + ), + + #[error("Unexpected private identifier")] + #[diagnostic()] + PrivateNotInClass( + Atom, + #[label("Private identifier '#{0}' is not allowed outside class bodies")] Span, + ), + + #[error("Classes may not have a static property named prototype")] + #[diagnostic()] + StaticPrototype(#[label("Classes may not have a static property named prototype")] Span), + + #[error("Constructor can't have get/set modifier")] + #[diagnostic()] + ConstructorGetterSetter(#[label("Constructor can't have get/set modifier")] Span), + + #[error("Constructor can't be an async method")] + #[diagnostic()] + ConstructorAsync(#[label("Constructor can't be an async method")] Span), + + #[error("Cannot use `{0}` as an identifier in an async context")] + #[diagnostic()] + IdentifierAsync(&'static str, #[label("{0} cannot be used here")] Span), + + #[error("Cannot use `{0}` as an identifier in a generator context")] + #[diagnostic()] + IdentifierGenerator(&'static str, #[label("{0} cannot be used here")] Span), + + #[error("Constructor can't be a generator")] + #[diagnostic()] + ConstructorGenerator(#[label("Constructor can't be a generator")] Span), + + #[error("Classes can't have a field named 'constructor'")] + #[diagnostic()] + FieldConstructor(#[label("Classes can't have a field named 'constructor'")] Span), + + #[error("Multiple constructor implementations are not allowed.")] + #[diagnostic()] + DuplicateConstructor( + #[label("constructor has already been declared here")] Span, + #[label("it cannot be redeclared here")] Span, + ), + + #[error("An export name cannot include a unicode lone surrogate")] + #[diagnostic()] + ExportLoneSurrogate(#[label("An export name cannot include a unicode lone surrogate")] Span), + + #[error("A string literal cannot be used as an exported binding without `from`")] + #[diagnostic(help("Did you mean `export {{ '{0}' as '{1}' }} from 'some-module'`?"))] + ExportNamedString( + Atom, + Atom, + #[label("A string literal cannot be used as an exported binding without `from`")] Span, + ), + + #[error("Bad escape sequence in untagged template literal")] + #[diagnostic()] + TemplateLiteral(#[label("Bad escape sequence in untagged template literal")] Span), + + #[error("Delete of an unqualified identifier in strict mode.")] + #[diagnostic()] + DeleteOfUnqualified(#[label("Delete of an unqualified identifier in strict mode")] Span), + + #[error("'with' statements are not allowed")] + #[diagnostic()] + WithStatement(#[label("'with' statements are not allowed")] Span), + + #[error("Private fields can not be deleted")] + #[diagnostic()] + DeletePrivateField(#[label("Private fields can not be deleted")] Span), + + #[error("Empty parenthesized expression")] + #[diagnostic()] + EmptyParenthesizedExpression(#[label("Expected an expression here")] Span), + + #[error("Undefined export")] + #[diagnostic()] + UndefinedExport(Atom, #[label("Export '{0}' is not defined")] Span), + + #[error("Logical expressions and coalesce expressions cannot be mixed")] + #[diagnostic(help("Wrap either expression by parentheses"))] + MixedCoalesce(#[label("Logical expressions and coalesce expressions cannot be mixed")] Span), + + #[error("'Unexpected `{0}`")] + #[diagnostic()] + UnexpectedKeyword(&'static str, #[label("'{0}' keyword is unexpected here")] Span), + + #[error("{0} loop variable declaration may not have an initializer")] + #[diagnostic()] + UnexpectedInitializerInForLoopHead( + &'static str, + #[label("{0} loop variable declaration may not have an initializer")] Span, + ), + + #[error("Only a single declaration is allowed in a `for...{0}` statement")] + #[diagnostic()] + MultipleDeclarationInForLoopHead( + &'static str, + #[label("Only a single declaration is allowed in a `for...{0}` statement")] Span, + ), + + #[error("Illegal newline after {0}")] + #[diagnostic()] + IllegalNewline( + &'static str, + #[label("{0} starts here")] Span, + #[label("A newline is not expected here")] Span, + ), + + #[error("Duplicate parameter name not allowed in this context")] + #[diagnostic()] + DuplicateParameter(#[label("Duplicate parameter name not allowed in this context")] Span), + + #[error("Illegal 'use strict' directive in function with non-simple parameter list")] + #[diagnostic()] + IllegalUseStrict( + #[label("Illegal 'use strict' directive in function with non-simple parameter list")] Span, + ), + + #[error("'arguments' is not allowed in {0}")] + #[diagnostic()] + UnexpectedArguments(&'static str, #[label("'arguments' is not allowed in {0}")] Span), + + #[error("Unexpected {0} expression")] + #[diagnostic()] + UnexpectedExpression(&'static str, #[label("Unexpected {0} expression")] Span), + + #[error("Unexpected exponentiation expression")] + #[diagnostic(help("Wrap {0} expression in parentheses to enforce operator precedence"))] + UnexpectedExponential(&'static str, #[label("Unexpected exponentiation expression")] Span), + + #[error("Tagged template expressions are not permitted in an optional chain")] + #[diagnostic()] + OptionalChainTaggedTemplate( + #[label("Tagged template expressions are not permitted in an optional chain")] Span, + ), + + #[error("A 'get' accessor must not have any formal parameters.")] + #[diagnostic()] + GetterParameters(#[label("A 'get' accessor must not have any formal parameters.")] Span), + + #[error("A 'set' accessor must have exactly one parameter.")] + #[diagnostic()] + SetterParameters(#[label("A 'set' accessor must have exactly one parameter.")] Span), + + #[error("A 'set' accessor function argument must not be a rest parameter")] + #[diagnostic()] + SetterParametersRestPattern( + #[label("A 'set' accessor function argument must not be a rest parameter")] Span, + ), + + #[error("{0} expression not allowed in formal parameter")] + #[diagnostic()] + AwaitOrYieldInParameter( + &'static str, + #[label("{0} expression not allowed in formal parameter")] Span, + ), + + #[error("Invalid assignment in object literal")] + #[diagnostic(help( + "Did you mean to use a ':'? An '=' can only follow a property name when the containing object literal is part of a destructuring pattern." + ))] + CoverInitializedNameError(#[label("Assignment is not allowed here")] Span), + + #[error("Super calls are not permitted outside constructors or in nested functions inside constructors. +")] + #[diagnostic()] + UnexpectedSuperCall( + #[label( + "Super calls are not permitted outside constructors or in nested functions inside constructors." + )] + Span, + ), + + #[error("'super' can only be referenced in members of derived classes or object literal expressions. +")] + #[diagnostic()] + UnexpectedSuperReference( + #[label("'super' can only be referenced in members of derived classes or object literal expressions. +")] + Span, + ), + + #[error("'super' can only be used with function calls or in property accesses")] + #[diagnostic(help("replace with `super()` or `super.prop` or `super[prop]`"))] + UnexpectedSuper( + #[label("'super' can only be used with function calls or in property accesses ")] Span, + ), + + #[error("'super' can only be referenced in a derived class.")] + #[diagnostic(help("either remove this super, or extend the class"))] + SuperWithoutDerivedClass( + #[label("'super' can only be referenced in a derived class.")] Span, + #[label("class does not have `extends`")] Span, + ), + + #[error("Private fields cannot be accessed on super")] + #[diagnostic()] + SuperPrivate(#[label("Private fields cannot be accessed on super")] Span), + + #[error("Expected function name")] + #[diagnostic(help("Function name is required in function declaration or named export"))] + ExpectFunctionName(#[label("Function name is required here")] Span), + + #[error("Missing catch or finally clause")] + #[diagnostic()] + ExpectCatchFinally(#[label("Expected `catch` or `finally` here")] Span), + + #[error("Cannot assign to '{0}' because it is a {1}")] + #[diagnostic()] + CannotAssignTo( + Atom, + &'static str, + #[label("Cannot assign to '{0}' because this is a {1}")] Span, + ), + + #[error("A rest parameter cannot have an initializer")] + #[diagnostic()] + ARestParameterCannotHaveAnInitializer( + #[label("A rest parameter cannot have an initializer")] Span, + ), + + /* TypeScript */ + #[error("TS1015: Parameter cannot have question mark and initializer")] + #[diagnostic()] + ParameterCannotHaveQuestionMarkAndInitializer( + #[label("Parameter cannot have question mark and initializer")] Span, + ), + + #[error("TS1047: A rest parameter cannot be optional")] + #[diagnostic()] + ARestParameterCannotBeOptional(#[label("A rest parameter cannot be optional")] Span), + + #[error("TS1095: A 'set' accessor cannot have a return type annotation")] + #[diagnostic()] + ASetAccessorCannotHaveAReturnTypeAnnotation( + #[label("A 'set' accessor cannot have a return type annotation")] Span, + ), + + #[error("TS1098: Type parameter list cannot be empty")] + #[diagnostic()] + TypeParameterListCannotBeEmpty(#[label("Type parameter list cannot be empty")] Span), + + #[error("TS1099: Type argument list cannot be empty")] + #[diagnostic()] + TypeArgumentListCannotBeEmpty(#[label("Type argument list cannot be empty")] Span), + + #[error("TS1108: A 'return' statement can only be used within a function body")] + #[diagnostic()] + ReturnStatementOnlyInFunctionBody( + #[label("A 'return' statement can only be used within a function body.")] Span, + ), + + #[error("TS1164: Computed property names are not allowed in enums")] + #[diagnostic()] + ComputedPropertyNamesAreNotAllowedInEnums( + #[label("Computed property names are not allowed in enums")] Span, + ), + + #[error("TS1313: The body of an 'if' statement cannot be the empty statement")] + #[diagnostic()] + TheBodyOfAnIfStatementCannotBeTheEmptyStatement( + #[label("The body of an 'if' statement cannot be the empty statement")] Span, + ), + + #[error("TS1317: A parameter property cannot be declared using a rest parameter")] + #[diagnostic()] + AParameterPropertyCannotBeDeclaredUsingARestParameter( + #[label("A parameter property cannot be declared using a rest parameter")] Span, + ), + + #[error("TS2452: An enum member cannot have a numeric name")] + #[diagnostic()] + AnEnumMemberCannotHaveANumericName(#[label("An enum member cannot have a numeric name")] Span), } diff --git a/crates/oxc_parser/Cargo.toml b/crates/oxc_parser/Cargo.toml index 31e2f86cde3cb..ea9da0b08d343 100644 --- a/crates/oxc_parser/Cargo.toml +++ b/crates/oxc_parser/Cargo.toml @@ -14,5 +14,8 @@ oxc_allocator = { path = "../oxc_allocator" } oxc_ast = { path = "../oxc_ast" } oxc_diagnostics = { path = "../oxc_diagnostics" } +bitflags = { workspace = true } +rustc-hash = { workspace = true } + unicode-id-start = "1.0.3" num-bigint = "0.4.3" diff --git a/crates/oxc_parser/src/cursor.rs b/crates/oxc_parser/src/cursor.rs new file mode 100644 index 0000000000000..fe8cf023bcc63 --- /dev/null +++ b/crates/oxc_parser/src/cursor.rs @@ -0,0 +1,311 @@ +//! Code related to navigating `Token`s from the lexer + +use oxc_ast::{context::Context, Atom, Node}; +use oxc_diagnostics::{Diagnostic, Result}; + +use crate::lexer::{Kind, LexerCheckpoint, LexerContext, Token}; +use crate::Parser; + +pub struct ParserCheckpoint<'a> { + lexer: LexerCheckpoint<'a>, + cur_token: Token, + prev_node_end: usize, + errors_pos: usize, +} + +impl<'a> Parser<'a> { + #[must_use] + pub const fn start_node(&self) -> Node { + let token = self.cur_token(); + Node::new(token.start, 0, self.ctx) + } + + #[must_use] + pub const fn end_node(&self, node: Node) -> Node { + let mut node = node; + node.end = self.prev_token_end; + node + } + + /// Get current token + #[must_use] + pub const fn cur_token(&self) -> &Token { + &self.token + } + + /// Get current Kind + #[must_use] + pub const fn cur_kind(&self) -> Kind { + self.token.kind + } + + /// Get current source text + #[must_use] + pub fn cur_src(&self) -> &'a str { + unsafe { self.source.get_unchecked(self.cur_token().range()) } + } + + /// Get current atom + #[must_use] + pub const fn cur_atom(&self) -> Option<&Atom> { + self.cur_token().value.get_atom() + } + + /// Peek next token, returns EOF for final peek + #[must_use] + pub fn peek_token(&mut self) -> &Token { + self.lexer.lookahead(1) + } + + /// Peek next kind, returns EOF for final peek + #[must_use] + pub fn peek_kind(&mut self) -> Kind { + self.peek_token().kind + } + + /// Peek at kind + #[must_use] + pub fn peek_at(&mut self, kind: Kind) -> bool { + self.peek_token().kind == kind + } + + /// Peek nth token + pub fn nth(&mut self, n: usize) -> &Token { + if n == 0 { + return self.cur_token(); + } + self.lexer.lookahead(n) + } + + /// Peek at nth kind + pub fn nth_at(&mut self, n: usize, kind: Kind) -> bool { + self.nth(n).kind == kind + } + + /// Peek nth kind + pub fn nth_kind(&mut self, n: usize) -> Kind { + self.nth(n).kind + } + + /// Checks if the current index has token `Kind` + #[must_use] + pub fn at(&self, kind: Kind) -> bool { + self.cur_kind() == kind + } + + /// Move to the next token + /// Checks if the current token is escaped if it is a keyword + fn advance(&mut self, kind: Kind) { + // StringValue of IdentifierName normalizes any Unicode escape sequences + // in IdentifierName hence such escapes cannot be used to write an Identifier + // whose code point sequence is the same as a ReservedWord. + if self.cur_token().escaped && kind.is_all_keyword() { + let range = self.cur_token().range(); + self.error(Diagnostic::EscapedKeyword(range)); + } + self.prev_token_end = self.token.end; + self.token = self.lexer.next_token(); + } + + /// Advance and return true if we are at `Kind`, return false otherwise + #[must_use] + pub fn eat(&mut self, kind: Kind) -> bool { + if self.at(kind) { + self.advance(kind); + return true; + } + false + } + + /// Advance and return true if we are at `Kind` + pub fn bump(&mut self, kind: Kind) { + if self.at(kind) { + self.advance(kind); + } + } + + /// Advance any token + pub fn bump_any(&mut self) { + self.advance(self.cur_kind()); + } + + /// Advance and change token type, useful for changing keyword to ident + pub fn bump_remap(&mut self, kind: Kind) { + self.advance(kind); + } + + /// Automatic Semicolon Insertion + /// `https://tc39.es/ecma262/#sec-automatic-semicolon-insertion` + /// # Errors + pub fn asi(&mut self) -> Result<()> { + if !self.can_insert_semicolon() { + let range = self.prev_token_end..self.cur_token().start; + return Err(Diagnostic::AutoSemicolonInsertion(range)); + } + if self.at(Kind::Semicolon) { + self.advance(Kind::Semicolon); + } + Ok(()) + } + + #[must_use] + pub fn can_insert_semicolon(&self) -> bool { + let kind = self.cur_kind(); + if kind == Kind::Semicolon { + return true; + } + kind == Kind::RCurly || kind == Kind::Eof || self.cur_token().is_on_new_line + } + + /// Expect a `Kind` or return error + /// # Errors + pub fn expect(&mut self, kind: Kind) -> Result<()> { + if !self.at(kind) { + let range = self.current_range(); + return Err(Diagnostic::ExpectToken(kind.to_str(), self.cur_kind().to_str(), range)); + } + self.advance(kind); + Ok(()) + } + + #[must_use] + pub const fn current_range(&self) -> std::ops::Range { + let cur_token = self.cur_token(); + match self.cur_kind() { + Kind::Eof => { + if self.prev_token_end < cur_token.end { + self.prev_token_end..self.prev_token_end + } else { + self.prev_token_end - 1..self.prev_token_end + } + } + _ => cur_token.range(), + } + } + + /// Expect the next next token to be a `JsxChild`, i.e. `<` or `{` or `JSXText` + /// # Errors + pub fn expect_jsx_child(&mut self, kind: Kind) -> Result<()> { + self.lexer.context = LexerContext::JsxChild; + self.expect(kind)?; + self.lexer.context = LexerContext::Regular; + Ok(()) + } + + /// Expect the next next token to be a `JsxString` or any other token + /// # Errors + pub fn expect_jsx_attribute_value(&mut self, kind: Kind) -> Result<()> { + self.lexer.context = LexerContext::JsxAttributeValue; + self.expect(kind)?; + self.lexer.context = LexerContext::Regular; + Ok(()) + } + + /// Tell lexer to read a regex + pub fn read_regex(&mut self) { + self.token = self.lexer.next_regex(self.cur_kind()); + } + + /// Tell lexer to read a template substitution tail + pub fn re_lex_template_substitution_tail(&mut self) { + if self.at(Kind::RCurly) { + self.token = self.lexer.next_template_substitution_tail(); + } + } + + /// Tell lexer to re-read a jsx identifier + pub fn re_lex_jsx_identifier(&mut self) { + self.token = self.lexer.next_jsx_identifier(self.prev_token_end); + } + + pub fn re_lex_right_angle(&mut self) -> Kind { + let kind = self.cur_kind(); + if kind == Kind::RAngle { + self.token = self.lexer.next_right_angle(); + self.token.kind + } else { + kind + } + } + + pub fn re_lex_ts_l_angle(&mut self) { + let kind = self.cur_kind(); + if matches!(kind, Kind::ShiftLeft | Kind::ShiftLeftEq | Kind::LtEq) { + self.token = self.lexer.re_lex_as_typescript_l_angle(kind); + } + } + + pub fn re_lex_ts_r_angle(&mut self) { + let kind = self.cur_kind(); + if matches!( + kind, + Kind::ShiftRight + | Kind::ShiftRight3 + | Kind::ShiftRightEq + | Kind::ShiftRight3Eq + | Kind::GtEq + ) { + self.token = self.lexer.re_lex_as_typescript_r_angle(kind); + } + } + + #[must_use] + pub fn checkpoint(&self) -> ParserCheckpoint<'a> { + ParserCheckpoint { + lexer: self.lexer.checkpoint(), + cur_token: self.token.clone(), + prev_node_end: self.prev_token_end, + errors_pos: self.errors.borrow().len(), + } + } + + pub fn rewind(&mut self, checkpoint: ParserCheckpoint<'a>) { + let ParserCheckpoint { lexer, cur_token, prev_node_end, errors_pos: errors_lens } = + checkpoint; + + self.lexer.rewind(lexer); + self.token = cur_token; + self.prev_token_end = prev_node_end; + self.errors.borrow_mut().truncate(errors_lens); + } + + /// # Errors + pub fn try_parse(&mut self, func: impl FnOnce(&mut Parser<'a>) -> Result) -> Result { + let checkpoint = self.checkpoint(); + let ctx = self.ctx; + let result = func(self); + if result.is_err() { + self.ctx = ctx; + self.rewind(checkpoint); + } + result + } + + pub fn without_context(&mut self, flags: Context, cb: F) -> T + where + F: FnOnce(&mut Self) -> T, + { + let context_flags_to_clear = flags & self.ctx; + if !context_flags_to_clear.is_empty() { + self.ctx &= !context_flags_to_clear; + let result = cb(self); + self.ctx |= context_flags_to_clear; + return result; + } + cb(self) + } + + pub fn with_context(&mut self, flags: Context, cb: F) -> T + where + F: FnOnce(&mut Self) -> T, + { + let context_flags_to_set = flags & !self.ctx; + if !context_flags_to_set.is_empty() { + self.ctx |= context_flags_to_set; + let result = cb(self); + self.ctx &= !context_flags_to_set; + return result; + } + cb(self) + } +} diff --git a/crates/oxc_parser/src/js/binding.rs b/crates/oxc_parser/src/js/binding.rs new file mode 100644 index 0000000000000..5b8aec7c3f7d3 --- /dev/null +++ b/crates/oxc_parser/src/js/binding.rs @@ -0,0 +1,134 @@ +use oxc_allocator::Box; +use oxc_ast::{ast::*, Node}; +use oxc_diagnostics::{Diagnostic, Result}; + +use super::list::{ArrayPatternList, ObjectPatternProperties}; +use crate::lexer::Kind; +use crate::list::SeparatedList; +use crate::Parser; + +impl<'a> Parser<'a> { + /// Destructuring Binding Patterns + /// `LexicalBinding` + /// `BindingIdentifier` `Initializer_opt` + /// `BindingPattern` Initializer + /// `BindingPattern`: + /// `ObjectBindingPattern` + /// `ArrayBindingPattern` + pub fn parse_binding_pattern(&mut self) -> Result<(BindingPattern<'a>, bool)> { + let kind = match self.cur_kind() { + Kind::LCurly => self.parse_object_binding_pattern(), + Kind::LBrack => self.parse_array_binding_pattern(), + _ => self.parse_binding_pattern_identifier(), + }?; + if self.ts_enabled() { + let optional = self.eat(Kind::Question); + let (type_annotation, definite) = self.parse_ts_variable_annotation()?; + Ok((self.ast.binding_pattern(kind, type_annotation, optional), definite)) + } else { + Ok((self.ast.binding_pattern(kind, None, false), false)) + } + } + + fn parse_binding_pattern_identifier(&mut self) -> Result> { + self.parse_binding_identifier().map(|ident| self.ast.binding_identifier(ident)) + } + + /// Section 14.3.3 Object Binding Pattern + fn parse_object_binding_pattern(&mut self) -> Result> { + let node = self.start_node(); + let properties = ObjectPatternProperties::parse(self)?.elements; + Ok(self.ast.object_pattern(self.end_node(node), properties)) + } + + /// Section 14.3.3 Array Binding Pattern + fn parse_array_binding_pattern(&mut self) -> Result> { + let node = self.start_node(); + let elements = ArrayPatternList::parse(self)?.elements; + Ok(self.ast.array_pattern(self.end_node(node), elements)) + } + + /// Section 14.3.3 Binding Rest Property + pub fn parse_rest_element(&mut self) -> Result>> { + let node = self.start_node(); + self.bump_any(); // advance `...` + let argument = self.parse_binding_element()?; + let node = self.end_node(node); + + if self.at(Kind::Comma) { + let error = if self.peek_at(Kind::RBrack) { + Diagnostic::RestElementTraillingComma(self.cur_token().range()) + } else { + Diagnostic::RestElement(node.range()) + }; + self.error(error); + } + + Ok(self.ast.rest_element(node, argument)) + } + + /// `BindingElement` + /// `SingleNameBinding` + /// `BindingPattern` Initializer + pub fn parse_binding_element(&mut self) -> Result> { + let node = self.start_node(); + let pattern = self.parse_binding_pattern()?.0; + self.parse_initializer(node, pattern) + } + + // object pattern property only has kind: init and method: false + // https://github.com/oxc_ast/oxc_ast/blob/master/es2015.md#objectpattern + pub fn parse_object_pattern_property(&mut self) -> Result> { + let node = self.start_node(); + + let mut shorthand = false; + let is_binding_identifier = self.cur_kind().is_binding_identifier(); + let (key, computed) = self.parse_property_name()?; + + let value = if is_binding_identifier && !self.at(Kind::Colon) { + // let { a = b } = c + // let { a } = b + // ^ BindingIdentifier + if let PropertyKey::Identifier(ident) = &key { + shorthand = true; + let binding_identifier = + BindingIdentifier { node: ident.node, name: ident.name.clone() }; + let identifier = self.ast.binding_identifier(binding_identifier); + let left = self.ast.binding_pattern(identifier, None, false); + PropertyValue::Pattern(self.parse_initializer(node, left)?) + } else { + return self.unexpected(); + } + } else { + // let { a: b } = c + // ^ IdentifierReference + self.expect(Kind::Colon)?; + PropertyValue::Pattern(self.parse_binding_element()?) + }; + + Ok(Property { + node: self.end_node(node), + key, + value, + kind: PropertyKind::Init, + method: false, + shorthand, + computed, + }) + } + + /// Initializer[In, Yield, Await] : + /// = `AssignmentExpression`[?In, ?Yield, ?Await] + fn parse_initializer( + &mut self, + node: Node, + left: BindingPattern<'a>, + ) -> Result> { + if self.eat(Kind::Eq) { + let expr = self.parse_assignment_expression_base()?; + Ok(self.ast.assignment_pattern(self.end_node(node), left, expr)) + } else { + Ok(left) + } + } +} diff --git a/crates/oxc_parser/src/js/class.rs b/crates/oxc_parser/src/js/class.rs new file mode 100644 index 0000000000000..eac4dea58311e --- /dev/null +++ b/crates/oxc_parser/src/js/class.rs @@ -0,0 +1,462 @@ +use oxc_allocator::{Box, Vec}; +use oxc_ast::{ast::*, context::StatementContext, syntax_directed_operations::PropName, Node}; +use oxc_diagnostics::{Diagnostic, Result}; + +use super::list::ClassElements; +use crate::lexer::Kind; +use crate::list::NormalList; +use crate::Parser; + +type Extends<'a> = + Vec<'a, (Expression<'a>, Option>>, Node)>; + +type Implements<'a> = Vec<'a, Box<'a, TSClassImplements<'a>>>; + +/// Section 15.7 Class Definitions +impl<'a> Parser<'a> { + pub fn parse_class_statement(&mut self, stmt_ctx: StatementContext) -> Result> { + let decl = self.parse_class_declaration(/* declare */ false)?; + + if stmt_ctx.is_single_statement() { + self.error(Diagnostic::ClassDeclaration(decl.node.start..decl.body.node.start)); + } + + Ok(self.ast.class_declaration(decl)) + } + + /// Section 15.7 Class Definitions + pub fn parse_class_declaration(&mut self, declare: bool) -> Result>> { + self.parse_class(declare, ClassType::ClassDeclaration) + } + + /// Section Class Definitions `https://tc39.es/ecma262/#prod-ClassExpression` + /// `ClassExpression`[Yield, Await] : + /// class `BindingIdentifier`[?Yield, ?Await]opt `ClassTail`[?Yield, ?Await] + pub fn parse_class_expression(&mut self) -> Result> { + let class = self.parse_class(/* declare */ false, ClassType::ClassExpression)?; + Ok(self.ast.class_expression(class)) + } + + fn parse_class(&mut self, declare: bool, r#type: ClassType) -> Result>> { + let node = self.start_node(); + + let r#abstract = self.eat(Kind::Abstract); + + self.bump_any(); // advance `class` + + let decorators = self.state.consume_decorators(); + + let id = if self.cur_kind().is_binding_identifier() && !self.at(Kind::Implements) { + Some(self.parse_binding_identifier()?) + } else { + None + }; + + let type_parameters = + if self.ts_enabled() { self.parse_ts_type_parameters()? } else { None }; + let (extends, implements) = self.parse_heritage_clause()?; + let mut super_class = None; + let mut super_type_parameters = None; + if let Some(mut extends) = extends { + if !extends.is_empty() { + let first_extends = extends.remove(0); + super_class = Some(first_extends.0); + super_type_parameters = first_extends.1; + } + } + let body = self.parse_class_body()?; + + Ok(self.ast.class( + r#type, + self.end_node(node), + id, + super_class, + body, + type_parameters, + super_type_parameters, + implements, + r#abstract, + decorators, + declare, + )) + } + + pub fn parse_heritage_clause( + &mut self, + ) -> Result<(Option>, Option>)> { + let mut extends = None; + let mut implements = None; + + loop { + match self.cur_kind() { + Kind::Extends => { + extends = Some(self.parse_extends_clause()?); + } + Kind::Implements => { + implements = Some(self.parse_ts_implements_clause()?); + } + _ => break, + } + } + + Ok((extends, implements)) + } + + /// `ClassHeritage` + /// extends `LeftHandSideExpression`[?Yield, ?Await] + fn parse_extends_clause(&mut self) -> Result> { + self.bump_any(); // bump `extends` + let mut extends = self.ast.new_vec(); + + let node = self.start_node(); + let mut first_extends = self.parse_lhs_expression()?; + let first_type_argument; + if let Expression::TSInstantiationExpression(expr) = first_extends { + let expr = expr.unbox(); + first_extends = expr.expression; + first_type_argument = Some(expr.type_parameters); + } else { + first_type_argument = self.parse_ts_type_arguments()?; + } + extends.push((first_extends, first_type_argument, self.end_node(node))); + + while self.eat(Kind::Comma) { + let node = self.start_node(); + let mut extend = self.parse_lhs_expression()?; + let type_argument; + if let Expression::TSInstantiationExpression(expr) = extend { + let expr = expr.unbox(); + extend = expr.expression; + type_argument = Some(expr.type_parameters); + } else { + type_argument = self.parse_ts_type_arguments()?; + } + + extends.push((extend, type_argument, self.end_node(node))); + } + + Ok(extends) + } + + fn parse_class_body(&mut self) -> Result> { + let node = self.start_node(); + + let mut class_elements = ClassElements::new(self); + class_elements.parse(self)?; + let body = class_elements.elements; + + Ok(ClassBody { node: self.end_node(node), body }) + } + + #[allow(clippy::too_many_lines)] + pub fn parse_class_element(&mut self) -> Result> { + let node = self.start_node(); + + self.eat_decorators()?; + + let mut kind = MethodDefinitionKind::Method; + let mut r#async = false; + let mut generator = false; + + let mut key_name = None; + + let modifier = self.parse_class_element_modifiers(false); + + let accessor = self.peek_at(Kind::Ident) && self.eat(Kind::Accessor); + + let accessibility = modifier.accessibility(); + + let declare = modifier.declare(); + let readonly = modifier.readonly(); + let r#override = modifier.r#override(); + let r#abstract = modifier.r#abstract(); + let mut r#static = modifier.r#static(); + + if self.at(Kind::Static) { + // static { block } + if self.peek_at(Kind::LCurly) { + self.bump(Kind::Static); + return self.parse_class_static_block(node); + } + + // static ... + if self.peek_kind().is_class_element_name_start() || self.peek_at(Kind::Star) { + self.bump(Kind::Static); + r#static = true; + } else { + key_name = Some(self.parse_class_element_name()?); + } + } + + // async ... + if key_name.is_none() && self.at(Kind::Async) && !self.peek_at(Kind::Question) { + if self.peek_kind().is_class_element_name_start() || self.peek_at(Kind::Star) { + self.bump(Kind::Async); + r#async = true; + } else { + key_name = Some(self.parse_class_element_name()?); + } + } + + if self.is_at_ts_index_signature_member() { + if let TSSignature::TSIndexSignature(sig) = self.parse_ts_index_signature_member()? { + return Ok(ClassElement::TSIndexSignature(sig)); + } + } + + // * ... + if key_name.is_none() && self.eat(Kind::Star) { + generator = true; + } + + if key_name.is_none() && !r#async && !generator { + // get ... / set ... + let peeked_class_element = self.peek_kind().is_class_element_name_start(); + key_name = match self.cur_kind() { + Kind::Get if peeked_class_element => { + self.bump(Kind::Get); + kind = MethodDefinitionKind::Get; + Some(self.parse_class_element_name()?) + } + Kind::Set if peeked_class_element => { + self.bump(Kind::Set); + kind = MethodDefinitionKind::Set; + Some(self.parse_class_element_name()?) + } + kind if kind.is_class_element_name_start() => { + Some(self.parse_class_element_name()?) + } + _ => return self.unexpected(), + } + } + + let (key, computed) = + if let Some(result) = key_name { result } else { self.parse_class_element_name()? }; + + let optional = self.eat(Kind::Question); + let definite = self.eat(Kind::Bang); + + if let PropertyKey::PrivateIdentifier(private_ident) = &key { + if private_ident.name == "constructor" { + self.error(Diagnostic::PrivateNameConstructor(private_ident.node.range())); + } + } + + if accessor { + return self.parse_class_accessor_property(node, key, computed, r#static); + } + + // LAngle for start of type parameters `foo` + // ^ + if self.at(Kind::LParen) || self.at(Kind::LAngle) || r#async || generator { + let definition = self.parse_class_method_definition( + node, + kind, + key, + computed, + r#static, + r#async, + generator, + r#override, + r#abstract, + accessibility, + optional, + )?; + if let Some((name, node)) = definition.prop_name() { + if r#static && name == "prototype" { + self.error(Diagnostic::StaticPrototype(node.range())); + } + if !r#static && name == "constructor" { + if kind == MethodDefinitionKind::Get || kind == MethodDefinitionKind::Set { + self.error(Diagnostic::ConstructorGetterSetter(node.range())); + } + if r#async { + self.error(Diagnostic::ConstructorAsync(node.range())); + } + if generator { + self.error(Diagnostic::ConstructorGenerator(node.range())); + } + } + } + Ok(definition) + } else { + let definition = self.parse_class_property_definition( + node, + key, + computed, + r#static, + declare, + r#override, + readonly, + r#abstract, + accessibility, + optional, + definite, + )?; + if let Some((name, node)) = definition.prop_name() { + if name == "constructor" { + self.error(Diagnostic::FieldConstructor(node.range())); + } + if r#static && name == "prototype" { + self.error(Diagnostic::StaticPrototype(node.range())); + } + } + Ok(definition) + } + } + + fn parse_class_element_name(&mut self) -> Result<(PropertyKey<'a>, bool)> { + match self.cur_kind() { + Kind::PrivateIdentifier => { + let private_ident = self.parse_private_identifier(); + Ok((PropertyKey::PrivateIdentifier(private_ident), false)) + } + _ => self.parse_property_name(), + } + } + + #[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)] + fn parse_class_method_definition( + &mut self, + node: Node, + kind: MethodDefinitionKind, + key: PropertyKey<'a>, + computed: bool, + r#static: bool, + r#async: bool, + generator: bool, + r#override: bool, + r#abstract: bool, + accessibility: Option, + optional: bool, + ) -> Result> { + let kind = if !r#static + && !computed + && key.prop_name().map_or(false, |(name, _)| name == "constructor") + { + MethodDefinitionKind::Constructor + } else { + kind + }; + + let decorators = self.state.consume_decorators(); + + let value = self.parse_method(r#async, generator)?; + + if kind == MethodDefinitionKind::Get && !value.params.is_empty() { + self.error(Diagnostic::GetterParameters(value.params.node.range())); + } + + if kind == MethodDefinitionKind::Set { + if value.params.items.len() != 1 { + self.error(Diagnostic::SetterParameters(value.params.node.range())); + } + + if value.params.items.len() == 1 { + if let BindingPatternKind::RestElement(elem) = &value.params.items[0].pattern.kind { + self.error(Diagnostic::SetterParametersRestPattern(elem.node.range())); + } + } + } + + let method_definition = MethodDefinition { + node: self.end_node(node), + key, + value, + kind, + computed, + r#static, + r#override, + accessibility, + optional, + decorators, + }; + + if r#abstract { + Ok(ClassElement::TSAbstractMethodDefinition( + self.ast.alloc(TSAbstractMethodDefinition { method_definition }), + )) + } else { + Ok(ClassElement::MethodDefinition(self.ast.alloc(method_definition))) + } + } + + /// `FieldDefinition`[?Yield, ?Await] ; + #[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)] + fn parse_class_property_definition( + &mut self, + node: Node, + key: PropertyKey<'a>, + computed: bool, + r#static: bool, + declare: bool, + r#override: bool, + readonly: bool, + r#abstract: bool, + accessibility: Option, + optional: bool, + definite: bool, + ) -> Result> { + let type_annotation = + if self.ts_enabled() { self.parse_ts_type_annotation()? } else { None }; + let value = if self.eat(Kind::Eq) { + // let current_flags = self.scope.current_flags(); + // self.scope.set_current_flags(self.scope.current_flags()); + let expr = self.parse_expression()?; + // self.scope.set_current_flags(current_flags); + Some(expr) + } else { + None + }; + self.asi()?; + + let property_definition = PropertyDefinition { + node: self.end_node(node), + key, + value, + computed, + r#static, + declare, + r#override, + readonly, + type_annotation, + accessibility, + optional, + definite, + decorators: self.state.consume_decorators(), + }; + + if r#abstract { + Ok(ClassElement::TSAbstractPropertyDefinition( + self.ast.alloc(TSAbstractPropertyDefinition { property_definition }), + )) + } else { + Ok(ClassElement::PropertyDefinition(self.ast.alloc(property_definition))) + } + } + + /// `ClassStaticBlockStatementList` : + /// `StatementList`[~Yield, +Await, ~Return] + fn parse_class_static_block(&mut self, node: Node) -> Result> { + let has_await = self.ctx.has_await(); + let has_yield = self.ctx.has_yield(); + let has_return = self.ctx.has_return(); + self.ctx = self.ctx.and_await(true).and_yield(false).and_return(false); + let block = self.parse_block()?; + self.ctx = self.ctx.and_await(has_await).and_yield(has_yield).and_return(has_return); + Ok(self.ast.static_block(self.end_node(node), block.unbox().body)) + } + + /// `https://github.com/tc39/proposal-decorators` + fn parse_class_accessor_property( + &mut self, + node: Node, + key: PropertyKey<'a>, + computed: bool, + r#static: bool, + ) -> Result> { + let value = + self.eat(Kind::Eq).then(|| self.parse_assignment_expression_base()).transpose()?; + Ok(self.ast.accessor_property(self.end_node(node), key, value, computed, r#static)) + } +} diff --git a/crates/oxc_parser/src/js/declaration.rs b/crates/oxc_parser/src/js/declaration.rs new file mode 100644 index 0000000000000..d03feac85b392 --- /dev/null +++ b/crates/oxc_parser/src/js/declaration.rs @@ -0,0 +1,104 @@ +use oxc_allocator::Box; +use oxc_ast::{ast::*, context::StatementContext, GetNode}; +use oxc_diagnostics::{Diagnostic, Result}; + +use crate::lexer::Kind; +use crate::Parser; + +#[derive(Clone, Debug, Copy, Eq, PartialEq)] +pub enum VariableDeclarationParent { + For, + Statement, + Clause, +} + +#[derive(Clone, Debug, Copy, Eq, PartialEq)] +pub struct VariableDeclarationContext { + pub parent: VariableDeclarationParent, +} + +impl VariableDeclarationContext { + pub const fn new(parent: VariableDeclarationParent) -> Self { + Self { parent } + } +} + +impl<'a> Parser<'a> { + pub fn parse_let(&mut self, stmt_ctx: StatementContext) -> Result> { + let node = self.start_node(); + let peeked = self.peek_kind(); + // let = foo, let instanceof x, let + 1 + if peeked.is_assignment_operator() || peeked.is_binary_operator() { + let expr = self.parse_assignment_expression_base()?; + self.parse_expression_statement(node, expr) + // single statement let declaration: while (0) let + } else if (stmt_ctx.is_single_statement() && peeked != Kind::LBrack) + || peeked == Kind::Semicolon + { + let expr = self.parse_identifier_expression()?; + self.parse_expression_statement(node, expr) + } else { + self.parse_variable_statement(stmt_ctx) + } + } + + pub fn parse_variable_declaration( + &mut self, + decl_ctx: VariableDeclarationContext, + ) -> Result>> { + let node = self.start_node(); + let kind = match self.cur_kind() { + Kind::Var => VariableDeclarationKind::Var, + Kind::Const => VariableDeclarationKind::Const, + Kind::Let => VariableDeclarationKind::Let, + _ => return self.unexpected(), + }; + self.bump_any(); + + let mut declarations = self.ast.new_vec(); + loop { + let declaration = self.parse_variable_declarator(decl_ctx, kind)?; + declarations.push(declaration); + if !self.eat(Kind::Comma) { + break; + } + } + + if matches!( + decl_ctx.parent, + VariableDeclarationParent::Statement | VariableDeclarationParent::Clause + ) { + self.asi()?; + } + + Ok(self.ast.variable_declaration(self.end_node(node), kind, declarations)) + } + + fn parse_variable_declarator( + &mut self, + decl_ctx: VariableDeclarationContext, + kind: VariableDeclarationKind, + ) -> Result> { + let node = self.start_node(); + + let (id, definite) = self.parse_binding_pattern()?; + + let init = + self.eat(Kind::Eq).then(|| self.parse_assignment_expression_base()).transpose()?; + + if init.is_none() && decl_ctx.parent == VariableDeclarationParent::Statement { + // LexicalBinding[In, Yield, Await] : + // BindingIdentifier[?Yield, ?Await] Initializer[?In, ?Yield, ?Await] opt + // BindingPattern[?Yield, ?Await] Initializer[?In, ?Yield, ?Await] + // the grammar forbids `let []`, `let {}` + if !matches!(id.kind, BindingPatternKind::BindingIdentifier(_)) { + self.error(Diagnostic::InvalidDestrucuringDeclaration(id.node().range())); + } else if kind == VariableDeclarationKind::Const && !self.ctx.has_ambient() { + // It is a Syntax Error if Initializer is not present and IsConstantDeclaration of the LexicalDeclaration containing this LexicalBinding is true. + self.error(Diagnostic::MissinginitializerInConst(id.node().range())); + } + } + + Ok(self.ast.variable_declarator(self.end_node(node), kind, id, init, definite)) + } +} diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs new file mode 100644 index 0000000000000..addf0ffa52c2a --- /dev/null +++ b/crates/oxc_parser/src/js/expression.rs @@ -0,0 +1,1010 @@ +use oxc_allocator::Box; +use oxc_ast::{ast::*, Atom, Node}; +use oxc_diagnostics::{Diagnostic, Result}; + +use super::function::IsParenthesizedArrowFunction; +use super::grammar::CoverGrammar; +use super::list::{ArrayExpressionList, CallArguments, SequenceExpressionList}; +use super::operator::{ + map_assignment_operator, map_binary_operator, map_logical_operator, map_unary_operator, + map_update_operator, BindingPower, +}; +use crate::lexer::{Kind, TokenValue}; +use crate::list::SeparatedList; +use crate::Parser; + +impl<'a> Parser<'a> { + pub fn parse_paren_expression(&mut self) -> Result> { + self.expect(Kind::LParen)?; + let expression = self.parse_expression()?; + self.expect(Kind::RParen)?; + Ok(expression) + } + + /// Section Expression `https://tc39.es/ecma262/#sec-ecmascript-language-expressions` + pub fn parse_expression(&mut self) -> Result> { + let node = self.start_node(); + + // clear the decorator context when parsing Expression, as it should be unambiguous when parsing a decorator + let save_decorator_context = self.ctx.has_decorator(); + if save_decorator_context { + self.ctx = self.ctx.and_decorator(false); + } + + let lhs = self.parse_assignment_expression_base()?; + if !self.at(Kind::Comma) { + return Ok(lhs); + } + + let expr = self.parse_sequence_expression(node, lhs)?; + + if save_decorator_context { + self.ctx = self.ctx.and_decorator(true); + } + + Ok(expr) + } + + /// `PrimaryExpression`: Identifier Reference + pub fn parse_identifier_expression(&mut self) -> Result> { + let ident = self.parse_identifier_reference()?; + Ok(self.ast.identifier_expression(ident)) + } + + pub fn parse_identifier_reference(&mut self) -> Result { + // allow `await` and `yield`, let semantic analysis report error + if !self.cur_kind().is_identifier_reference(false, false) { + return self.unexpected(); + } + let (node, name) = self.parse_identifier_kind(Kind::Ident); + Ok(IdentifierReference { node, name }) + } + + /// `BindingIdentifier` : Identifier + pub fn parse_binding_identifier(&mut self) -> Result { + if !self.cur_kind().is_binding_identifier() { + return self.unexpected(); + } + let (node, name) = self.parse_identifier_kind(Kind::Ident); + Ok(BindingIdentifier { node, name }) + } + + pub fn parse_label_identifier(&mut self) -> Result { + if !self.cur_kind().is_label_identifier(self.ctx.has_yield(), self.ctx.has_await()) { + return self.unexpected(); + } + let (node, name) = self.parse_identifier_kind(Kind::Ident); + Ok(LabelIdentifier { node, name }) + } + + pub fn parse_identifier_name(&mut self) -> Result { + if !self.cur_kind().is_identifier_name() { + return self.unexpected(); + } + let (node, name) = self.parse_identifier_kind(Kind::Ident); + Ok(IdentifierName { node, name }) + } + + /// Parse keyword kind as identifier + pub fn parse_keyword_identifier(&mut self, kind: Kind) -> IdentifierName { + let (node, name) = self.parse_identifier_kind(kind); + IdentifierName { node, name } + } + + pub fn parse_identifier_kind(&mut self, kind: Kind) -> (Node, Atom) { + let node = self.start_node(); + let name = match std::mem::take(&mut self.token.value) { + TokenValue::String(value) => value, + _ => "".into(), + }; + self.bump_remap(kind); + (self.end_node(node), name) + } + + /// Section `https://tc39.es/ecma262/#prod-PrivateIdentifier` + /// `PrivateIdentifier` :: + /// # `IdentifierName` + /// # Panics + pub fn parse_private_identifier(&mut self) -> PrivateIdentifier { + let node = self.start_node(); + let name = self.cur_atom().unwrap().clone(); + self.bump_any(); + PrivateIdentifier { node: self.end_node(node), name } + } + + /// Section Primary Expression `https://tc39.es/ecma262/#sec-primary-expression` + /// `PrimaryExpression`[Yield, Await] : + /// this + /// `IdentifierReference`[?Yield, ?Await] + /// Literal + /// `ArrayLiteral`[?Yield, ?Await] + /// `ObjectLiteral`[?Yield, ?Await] + /// `FunctionExpression` + /// `ClassExpression`[?Yield, ?Await] + /// `GeneratorExpression` + /// `AsyncFunctionExpression` + /// `AsyncGeneratorExpression` + /// `RegularExpressionLiteral` + /// `TemplateLiteral`[?Yield, ?Await, ~Tagged] + /// `CoverParenthesizedExpressionAndArrowParameterList`[?Yield, ?Await] + fn parse_primary_expression(&mut self) -> Result> { + let node = self.start_node(); + + // AsyncFunctionExpression + // AsyncGeneratorExpression + if self.at_function_with_async() { + let r#async = self.eat(Kind::Async); + return self.parse_function_expression(node, r#async); + } + + match &self.cur_kind() { + Kind::Ident => self.parse_identifier_expression(), // fast path, keywords are checked at the end + // Literal, RegularExpressionLiteral + kind if kind.is_literal() => self.parse_literal_expression(), + // ArrayLiteral + Kind::LBrack => self.parse_array_expression(), + // ObjectLiteral + Kind::LCurly => self.parse_object_expression(), + // FunctionExpession, GeneratorExpression + Kind::Function => self.parse_function_expression(node, false), + // ClassExpression + Kind::Class => self.parse_class_expression(), + // This + Kind::This => Ok(self.parse_this_expression()), + // TemplateLiteral + Kind::NoSubstitutionTemplate | Kind::TemplateHead => { + self.parse_template_literal_expression(false) + } + Kind::New => self.parse_new_expression(), + Kind::Super => Ok(self.parse_super()), + Kind::Import => { + let node = self.start_node(); + let identifier = self.parse_keyword_identifier(Kind::Import); + match self.cur_kind() { + Kind::Dot => self.parse_meta_property(node, identifier), + Kind::LParen => self.parse_import_expression(node), + _ => self.unexpected(), + } + } + Kind::LParen => self.parse_parenthesized_expression(node), + Kind::Slash | Kind::SlashEq => { + self.read_regex(); + self.parse_literal_regexp() + .map(|literal| self.ast.literal_regexp_expression(literal)) + } + // JSXElement, JSXFragment + Kind::LAngle if self.source_type.is_jsx() => self.parse_jsx_expression(), + _ => self.parse_identifier_expression(), + } + } + + fn parse_parenthesized_expression(&mut self, node: Node) -> Result> { + let has_in = self.ctx.has_in(); + self.ctx = self.ctx.and_in(true); + let list = SequenceExpressionList::parse(self)?; + self.ctx = self.ctx.and_in(has_in); + + let mut expressions = list.elements; + + // ParenthesizedExpression is from acorn --preserveParens + let expression = if expressions.len() == 1 { + expressions.remove(0) + } else { + if expressions.is_empty() { + self.error(Diagnostic::EmptyParenthesizedExpression(list.node.range())); + } + self.ast.sequence_expression(list.node, expressions) + }; + + Ok(self.ast.parenthesized_expression(self.end_node(node), expression)) + } + + /// Section 13.2.2 This Expression + fn parse_this_expression(&mut self) -> Expression<'a> { + let node = self.start_node(); + self.bump_any(); + self.ast.this_expression(self.end_node(node)) + } + + /// [Literal Expression](https://tc39.es/ecma262/#prod-Literal) + /// parses string | true | false | null | number + pub fn parse_literal_expression(&mut self) -> Result> { + match self.cur_kind() { + Kind::Str => self + .parse_literal_string() + .map(|literal| self.ast.literal_string_expression(literal)), + Kind::True | Kind::False => self + .parse_literal_boolean() + .map(|literal| self.ast.literal_boolean_expression(literal)), + Kind::Null => { + let literal = self.parse_literal_null(); + Ok(self.ast.literal_null_expression(literal)) + } + Kind::RegExp => self + .parse_literal_regexp() + .map(|literal| self.ast.literal_regexp_expression(literal)), + kind if kind.is_number() => { + if self.cur_src().ends_with('n') { + self.parse_literal_bigint() + .map(|literal| self.ast.literal_bigint_expression(literal)) + } else { + self.parse_literal_number() + .map(|literal| self.ast.literal_number_expression(literal)) + } + } + _ => self.unexpected(), + } + } + + pub fn parse_literal_boolean(&mut self) -> Result { + let node = self.start_node(); + let value = match self.cur_kind() { + Kind::True => true, + Kind::False => false, + _ => return self.unexpected(), + }; + self.bump_any(); + Ok(BooleanLiteral { node: self.end_node(node), value }) + } + + pub fn parse_literal_null(&mut self) -> NullLiteral { + let node = self.start_node(); + self.bump_any(); // bump `null` + NullLiteral { node: self.end_node(node) } + } + + pub fn parse_literal_number(&mut self) -> Result> { + let node = self.start_node(); + let base = match self.cur_kind() { + Kind::Float | Kind::Decimal => NumberBase::Decimal, + Kind::Binary => NumberBase::Binary, + Kind::Octal => NumberBase::Octal, + Kind::Hex => NumberBase::Hex, + _ => return self.unexpected(), + }; + let value = self.cur_token().value.as_number(); + let raw = self.cur_src(); + self.bump_any(); + Ok(NumberLiteral::new(self.end_node(node), value, raw, base)) + } + + pub fn parse_literal_bigint(&mut self) -> Result { + let node = self.start_node(); + let value = match self.cur_kind() { + kind if kind.is_number() => self.cur_token().value.as_bigint(), + _ => return self.unexpected(), + }; + self.bump_any(); + Ok(BigintLiteral { node: self.end_node(node), value }) + } + + pub fn parse_literal_regexp(&mut self) -> Result { + let node = self.start_node(); + let r = match self.cur_kind() { + Kind::RegExp => self.cur_token().value.as_regex(), + _ => return self.unexpected(), + }; + self.bump_any(); + Ok(RegExpLiteral { + node: self.end_node(node), + value: EmptyObject {}, + regex: RegExp { pattern: r.pattern, flags: r.flags }, + }) + } + + pub fn parse_literal_string(&mut self) -> Result { + if !self.at(Kind::Str) { + return self.unexpected(); + } + let TokenValue::String(value) = std::mem::take(&mut self.token.value) else { + unreachable!() + }; + let node = self.start_node(); + self.bump_any(); + Ok(StringLiteral { node: self.end_node(node), value }) + } + + /// Section Array Expression `https://tc39.es/ecma262/#prod-ArrayLiteral` + /// `ArrayLiteral`[Yield, Await]: + /// [ Elision opt ] + /// [ `ElementList`[?Yield, ?Await] ] + /// [ `ElementList`[?Yield, ?Await] , Elisionopt ] + pub fn parse_array_expression(&mut self) -> Result> { + let node = self.start_node(); + let has_in = self.ctx.has_in(); + self.ctx = self.ctx.and_in(true); + let list = ArrayExpressionList::parse(self)?; + self.ctx = self.ctx.and_in(has_in); + Ok(self.ast.array_expression(self.end_node(node), list.elements, list.trailing_comma)) + } + + /// Section Template Literal `https://tc39.es/ecma262/#prod-TemplateLiteral` + /// `TemplateLiteral`[Yield, Await, Tagged] : + /// `NoSubstitutionTemplate` + /// `SubstitutionTemplate`[?Yield, ?Await, ?Tagged] + fn parse_template_literal(&mut self, tagged: bool) -> Result> { + let node = self.start_node(); + let mut expressions = self.ast.new_vec(); + let mut quasis = self.ast.new_vec(); + match self.cur_kind() { + Kind::NoSubstitutionTemplate => { + quasis.push(self.parse_template_element(tagged)); + } + Kind::TemplateHead => { + quasis.push(self.parse_template_element(tagged)); + expressions.push(self.parse_expression()?); + self.re_lex_template_substitution_tail(); + loop { + match self.cur_kind() { + Kind::Eof => self.expect(Kind::TemplateTail)?, + Kind::TemplateTail => { + quasis.push(self.parse_template_element(tagged)); + break; + } + Kind::TemplateMiddle => { + quasis.push(self.parse_template_element(tagged)); + } + _ => { + expressions.push(self.parse_expression()?); + self.re_lex_template_substitution_tail(); + } + } + } + } + _ => unreachable!("parse_template_literal"), + } + Ok(TemplateLiteral { node: self.end_node(node), quasis, expressions }) + } + + fn parse_template_literal_expression(&mut self, tagged: bool) -> Result> { + self.parse_template_literal(tagged) + .map(|template_literal| self.ast.template_literal_expression(template_literal)) + } + + fn parse_tagged_template( + &mut self, + node: Node, + lhs: Expression<'a>, + in_optional_chain: bool, + type_parameters: Option>>, + ) -> Result> { + let quasi = self.parse_template_literal(true)?; + let node = self.end_node(node); + // OptionalChain : + // ?. TemplateLiteral + // OptionalChain TemplateLiteral + // It is a Syntax Error if any source text is matched by this production. + // https://tc39.es/ecma262/#sec-left-hand-side-expressions-static-semantics-early-errors + if in_optional_chain { + self.error(Diagnostic::OptionalChainTaggedTemplate(quasi.node.range())); + } + Ok(self.ast.tagged_template_expression(node, lhs, quasi, type_parameters)) + } + + pub fn parse_template_element(&mut self, tagged: bool) -> TemplateElement { + let node = self.start_node(); + let cur_kind = self.cur_kind(); + let end_offset = match cur_kind { + Kind::TemplateHead | Kind::TemplateMiddle => 2, + Kind::NoSubstitutionTemplate | Kind::TemplateTail => 1, + _ => unreachable!(), + }; + + // cooked = None when template literal has invalid escape sequence + let cooked = self.cur_atom().map(Clone::clone); + + let raw = &self.cur_src()[1..self.cur_src().len() - end_offset]; + let raw = Atom::from(if cooked.is_some() && raw.contains('\r') { + self.ast.new_str(raw.replace("\r\n", "\n").replace('\r', "\n").as_str()) + } else { + raw + }); + + self.bump_any(); + + let mut node = self.end_node(node); + node.start += 1; + node.end -= end_offset; + + if !tagged && cooked.is_none() { + self.error(Diagnostic::TemplateLiteral(node.range())); + } + + let tail = matches!(cur_kind, Kind::TemplateTail | Kind::NoSubstitutionTemplate); + TemplateElement { node, tail, value: TemplateElementValue { raw, cooked } } + } + + /// Section 13.3 Meta Property + fn parse_meta_property(&mut self, node: Node, meta: IdentifierName) -> Result> { + self.bump_any(); // bump `.` + let property = match self.cur_kind() { + Kind::Meta => self.parse_keyword_identifier(Kind::Meta), + Kind::Target => self.parse_keyword_identifier(Kind::Target), + _ => self.parse_identifier_name()?, + }; + let node = self.end_node(node); + Ok(self.ast.meta_property(node, meta, property)) + } + + /// Section 13.3 Left-Hand-Side Expression + pub fn parse_lhs_expression(&mut self) -> Result> { + let node = self.start_node(); + let mut in_optional_chain = false; + let lhs = self.parse_member_expression_base(&mut in_optional_chain)?; + let lhs = self.parse_call_expression(node, lhs, &mut in_optional_chain)?; + if in_optional_chain { + let node = self.end_node(node); + Ok(self.map_to_chain_expression(node, lhs)) + } else { + Ok(lhs) + } + } + + fn map_to_chain_expression(&mut self, node: Node, expr: Expression<'a>) -> Expression<'a> { + match expr { + Expression::MemberExpression(result) => { + self.ast.chain_expression(node, ChainElement::MemberExpression(result)) + } + Expression::CallExpression(result) => { + self.ast.chain_expression(node, ChainElement::CallExpression(result)) + } + expr => expr, + } + } + + /// Section 13.3 Member Expression + fn parse_member_expression_base( + &mut self, + in_optional_chain: &mut bool, + ) -> Result> { + let node = self.start_node(); + let lhs = self.parse_primary_expression()?; + self.parse_member_expression_rhs(node, lhs, in_optional_chain) + } + + /// Section 13.3 Super Call + fn parse_super(&mut self) -> Expression<'a> { + let node = self.start_node(); + self.bump_any(); // bump `super` + let node = self.end_node(node); + + // The `super` keyword can appear at below: + // SuperProperty: + // super [ Expression ] + // super . IdentifierName + // SuperCall: + // super ( Arguments ) + if !matches!(self.cur_kind(), Kind::Dot | Kind::LBrack | Kind::LParen) { + self.error(Diagnostic::UnexpectedSuper(node.range())); + } + + self.ast.super_(node) + } + + /// parse rhs of a member expression, starting from lhs + fn parse_member_expression_rhs( + &mut self, + lhs_node: Node, + lhs: Expression<'a>, + in_optional_chain: &mut bool, + ) -> Result> { + let mut lhs = lhs; + while !self.at(Kind::Eof) { + lhs = match self.cur_kind() { + // Decorator context does not parse computed member expressions, e.g. + // `class C { @dec() ["method"]() {} }` + Kind::LBrack if !self.ctx.has_decorator() => { + self.parse_computed_member_expression(lhs_node, lhs, false)? + } + Kind::Dot => self.parse_static_member_expression(lhs_node, lhs, false)?, + Kind::QuestionDot => { + *in_optional_chain = true; + match self.peek_kind() { + Kind::LBrack => { + self.bump_any(); // bump `?.` + self.parse_computed_member_expression(lhs_node, lhs, true)? + } + Kind::PrivateIdentifier => { + self.parse_static_member_expression(lhs_node, lhs, true)? + } + kind if kind.is_identifier_name() => { + self.parse_static_member_expression(lhs_node, lhs, true)? + } + _ => break, + } + } + Kind::Bang if !self.cur_token().is_on_new_line && self.ts_enabled() => { + self.bump_any(); + self.ast.ts_non_null_expression(self.end_node(lhs_node), lhs) + } + Kind::LAngle | Kind::ShiftLeft if self.ts_enabled() => { + if let Some(arguments) = self.parse_ts_type_arguments_in_expression()? { + lhs = Expression::TSInstantiationExpression(self.ast.alloc( + TSInstantiationExpression { + node: self.end_node(lhs_node), + expression: lhs, + type_parameters: arguments, + }, + )); + continue; + } + break; + } + _ => break, + }; + } + Ok(lhs) + } + + /// Section 13.3 `MemberExpression` + /// static member `a.b` + fn parse_static_member_expression( + &mut self, + lhs_node: Node, + lhs: Expression<'a>, + optional: bool, + ) -> Result> { + self.bump_any(); // advance `.` or `?.` + if self.cur_kind() == Kind::PrivateIdentifier { + let private_ident = self.parse_private_identifier(); + Ok(self.ast.private_field_expression( + self.end_node(lhs_node), + lhs, + private_ident, + optional, + )) + } else { + let ident = self.parse_identifier_name()?; + Ok(self.ast.static_member_expression(self.end_node(lhs_node), lhs, ident, optional)) + } + } + + /// Section 13.3 `MemberExpression` + /// `MemberExpression`[Yield, Await] : + /// `MemberExpression`[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ] + fn parse_computed_member_expression( + &mut self, + lhs_node: Node, + lhs: Expression<'a>, + optional: bool, + ) -> Result> { + self.bump_any(); // advance `[` + let has_in = self.ctx.has_in(); + self.ctx = self.ctx.and_in(true); + let property = self.parse_expression()?; + self.ctx = self.ctx.and_in(has_in); + self.expect(Kind::RBrack)?; + Ok(self.ast.computed_member_expression(self.end_node(lhs_node), lhs, property, optional)) + } + + /// `NewExpression` : new `NewExpression` + /// `https://tc39.es/ecma262/#sec-new-operator` + fn parse_new_expression(&mut self) -> Result> { + let node = self.start_node(); + let identifier = self.parse_keyword_identifier(Kind::New); + if self.at(Kind::Dot) { + return self.parse_meta_property(node, identifier); + } + let rhs_node = self.start_node(); + + let mut optional = false; + let mut callee = self.parse_member_expression_base(&mut optional)?; + + let mut type_parameter = None; + if let Expression::TSInstantiationExpression(instantiation_expr) = callee { + let instantiation_expr = instantiation_expr.unbox(); + type_parameter.replace(instantiation_expr.type_parameters); + callee = instantiation_expr.expression; + } + + // parse `new ident` without arguments + let arguments = if self.at(Kind::LParen) { + CallArguments::parse(self)?.elements + } else { + self.ast.new_vec() + }; + + if matches!(callee, Expression::ImportExpression(_)) { + self.error(Diagnostic::NewDynamicImport(self.end_node(rhs_node).range())); + } + + let node = self.end_node(node); + + if optional { + self.error(Diagnostic::NewOptionalChain(node.range())); + } + + Ok(self.ast.new_expression(node, callee, arguments, type_parameter)) + } + + /// Section 13.3 Call Expression + fn parse_call_expression( + &mut self, + lhs_node: Node, + lhs: Expression<'a>, + in_optional_chain: &mut bool, + ) -> Result> { + let mut lhs = lhs; + let mut type_arguments = None; + loop { + lhs = self.parse_member_expression_rhs(lhs_node, lhs, in_optional_chain)?; + let optional_call = self.eat(Kind::QuestionDot); + *in_optional_chain = if optional_call { true } else { *in_optional_chain }; + if let Expression::TSInstantiationExpression(expr) = lhs { + let expr = expr.unbox(); + type_arguments.replace(expr.type_parameters); + lhs = expr.expression; + } + match self.cur_kind() { + Kind::LParen => { + lhs = self.parse_call_arguments( + lhs_node, + lhs, + optional_call, + type_arguments.take(), + )?; + } + Kind::NoSubstitutionTemplate | Kind::TemplateHead => { + lhs = self.parse_tagged_template( + lhs_node, + lhs, + *in_optional_chain, + type_arguments.take(), + )?; + } + Kind::LAngle | Kind::ShiftLeft if self.ts_enabled() => { + let result = self.try_parse(|p| { + let arguments = p.parse_ts_type_arguments()?; + if p.at(Kind::RAngle) { + // a>c is not (a)>c, but a<(b>>c) + return p.unexpected(); + } + + // ac is (ac + if !p.at(Kind::LParen) + && !p.at(Kind::NoSubstitutionTemplate) + && !p.at(Kind::TemplateHead) + && p.cur_kind().is_at_expression() + && !p.cur_token().is_on_new_line + { + return p.unexpected(); + } + + type_arguments = arguments; + Ok(()) + }); + if result.is_err() { + break; + } + } + _ => break, + } + } + + Ok(lhs) + } + + fn parse_call_arguments( + &mut self, + lhs_node: Node, + lhs: Expression<'a>, + optional: bool, + type_parameters: Option>>, + ) -> Result> { + let has_in = self.ctx.has_in(); + self.ctx = self.ctx.and_in(true); + let call_arguments = CallArguments::parse(self)?; + self.ctx = self.ctx.and_in(has_in); + Ok(self.ast.call_expression( + self.end_node(lhs_node), + lhs, + call_arguments.elements, + optional, + type_parameters, + )) + } + + /// Section 13.4 Update Expression + fn parse_update_expression(&mut self) -> Result> { + let node = self.start_node(); + let lhs = self.parse_lhs_expression()?; + // ++ -- postfix update expressions + if self.cur_kind().is_update_operator() && !self.cur_token().is_on_new_line { + let operator = map_update_operator(self.cur_kind()); + self.bump_any(); + let lhs = SimpleAssignmentTarget::cover(lhs, self)?; + return Ok(self.ast.update_expression(self.end_node(node), operator, false, lhs)); + } + Ok(lhs) + } + + /// Section 13.5 Unary Expression + pub fn parse_unary_expression_base(&mut self, lhs_node: Node) -> Result> { + // [+Await] AwaitExpression + if self.is_await_expression() { + return self.parse_await_expression(lhs_node); + } + + if (self.at(Kind::LAngle) || self.at(Kind::ShiftLeft)) + && !self.source_type.is_jsx() + && self.ts_enabled() + { + return self.parse_ts_type_assertion(); + } + + // ++ -- prefix update expressions + if self.cur_kind().is_update_operator() { + let operator = map_update_operator(self.cur_kind()); + self.bump_any(); + let argument = self.parse_unary_expression_base(lhs_node)?; + let argument = SimpleAssignmentTarget::cover(argument, self)?; + return Ok(self.ast.update_expression( + self.end_node(lhs_node), + operator, + true, + argument, + )); + } + + // delete void typeof + - ~ ! prefix unary expressions + if self.cur_kind().is_unary_operator() { + return self.parse_unary_expression(); + } + + self.parse_update_expression() + } + + fn parse_unary_expression(&mut self) -> Result> { + let node = self.start_node(); + let operator = map_unary_operator(self.cur_kind()); + self.bump_any(); + let argument = self.parse_unary_expression_base(node)?; + Ok(self.ast.unary_expression(self.end_node(node), operator, true, argument)) + } + + fn parse_binary_or_logical_expression_base( + &mut self, + lhs_binding_power: BindingPower, + ) -> Result> { + let lhs_node = self.start_node(); + + let lhs = if self.ctx.has_in() && self.at(Kind::PrivateIdentifier) { + let left = self.parse_private_identifier(); + self.expect(Kind::In)?; + let right = self.parse_unary_expression_base(lhs_node)?; + Expression::PrivateInExpression(self.ast.alloc(PrivateInExpression { + node: self.end_node(lhs_node), + left, + operator: BinaryOperator::In, + right, + })) + } else { + self.parse_unary_expression_base(lhs_node)? + }; + + self.parse_binary_or_logical_expression_recursive(lhs_node, lhs, lhs_binding_power) + } + + /// Section 13.6 - 13.13 Binary Expression + fn parse_binary_or_logical_expression_recursive( + &mut self, + lhs_node: Node, + lhs: Expression<'a>, + min_binding_power: BindingPower, + ) -> Result> { + // Pratt Parsing Algorithm + // https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html + let mut lhs = lhs; + loop { + // re-lex for `>=` `>>` `>>>` + // This is need for jsx `
=
` case + let kind = self.re_lex_right_angle(); + + // Omit the In keyword for the grammer in 13.10 Relational Operators + // RelationalExpression[In, Yield, Await] : + // [+In] RelationalExpression[+In, ?Yield, ?Await] in ShiftExpression[?Yield, ?Await] + if kind == Kind::In && !self.ctx.has_in() + || (kind == Kind::As && self.cur_token().is_on_new_line) + { + break; + } + + let Some(left_binding_power) = BindingPower::value(kind) else { break }; + + let stop = if BindingPower::is_right_associative(left_binding_power) { + left_binding_power < min_binding_power + } else { + left_binding_power <= min_binding_power + }; + + if stop { + break; + } + + self.bump_any(); // bump operator + + if self.ts_enabled() && kind == Kind::As { + let type_annotation = self.parse_ts_type()?; + lhs = Expression::TSAsExpression(self.ast.alloc(TSAsExpression { + node: self.end_node(lhs_node), + expression: lhs, + type_annotation, + })); + continue; + } + + let rhs = self.parse_binary_or_logical_expression_base(left_binding_power)?; + + lhs = if kind.is_logical_operator() { + self.ast.logical_expression( + self.end_node(lhs_node), + lhs, + map_logical_operator(kind), + rhs, + ) + } else if kind.is_binary_operator() { + self.ast.binary_expression( + self.end_node(lhs_node), + lhs, + map_binary_operator(kind), + rhs, + ) + } else { + break; + }; + } + + Ok(lhs) + } + + /// Section 13.14 Conditional Expression + /// `ConditionalExpression`[In, Yield, Await] : + /// `ShortCircuitExpression`[?In, ?Yield, ?Await] + /// `ShortCircuitExpression`[?In, ?Yield, ?Await] ? `AssignmentExpression`[+In, ?Yield, ?Await] : `AssignmentExpression`[?In, ?Yield, ?Await] + fn parse_conditional_expression(&mut self) -> Result> { + let node = self.start_node(); + let lhs = self.parse_binary_or_logical_expression_base(BindingPower::lowest())?; + if !self.eat(Kind::Question) { + return Ok(lhs); + } + + let has_in = self.ctx.has_in(); + self.ctx = self.ctx.and_in(true); + let consequent = self.parse_assignment_expression_base()?; + self.ctx = self.ctx.and_in(has_in); + + self.expect(Kind::Colon)?; + let alternate = self.parse_assignment_expression_base()?; + Ok(self.ast.conditional_expression(self.end_node(node), lhs, consequent, alternate)) + } + + pub fn parse_assignment_expression_base(&mut self) -> Result> { + match self.is_parenthesized_arrow_function() { + IsParenthesizedArrowFunction::True => { + return self.parse_parenthesized_arrow_function(); + } + IsParenthesizedArrowFunction::Maybe => { + let pos = self.cur_token().start; + if !self.state.not_parenthesized_arrow.contains(&pos) { + if let Ok((type_parameters, params, return_type, r#async, node)) = + self.try_parse(Parser::parse_parenthesized_arrow_function_head) + { + return self.parse_arrow_function_body( + node, + type_parameters, + params, + return_type, + r#async, + ); + } + self.state.not_parenthesized_arrow.insert(pos); + } + } + IsParenthesizedArrowFunction::False => {} + } + + let node = self.start_node(); + if self.cur_kind().is_binding_identifier() + && self.peek_at(Kind::Arrow) + && !self.peek_token().is_on_new_line + { + self.parse_single_param_function_expression(node, false, false) + } else if self.at_async_no_new_line() + && self.peek_kind().is_binding_identifier() + && !self.peek_token().is_on_new_line + && self.nth_at(2, Kind::Arrow) + && !self.nth(2).is_on_new_line + { + self.bump_any(); // bump async + self.parse_single_param_function_expression(node, true, false) + } else { + self.parse_assignment_expression() + } + } + + /// `AssignmentExpression`[In, Yield, Await] : + pub fn parse_assignment_expression(&mut self) -> Result> { + // [+Yield] YieldExpression + if self.is_yield_expression() { + return self.parse_yield_expression(); + } + + let node = self.start_node(); + + let lhs = self.parse_conditional_expression()?; + self.parse_assignment_expression_recursive(node, lhs) + } + + fn parse_assignment_expression_recursive( + &mut self, + node: Node, + lhs: Expression<'a>, + ) -> Result> { + if !self.cur_kind().is_assignment_operator() { + return Ok(lhs); + } + + let operator = map_assignment_operator(self.cur_kind()); + + // 13.15.5 Destructuring Assignment + // LeftHandSideExpression = AssignmentExpression + // is converted to + // AssignmentPattern[Yield, Await] : + // ObjectAssignmentPattern + // ArrayAssignmentPattern + let left = AssignmentTarget::cover(lhs, self)?; + + self.bump_any(); + + let right = self.parse_assignment_expression_base()?; + Ok(self.ast.assignment_expression(self.end_node(node), operator, left, right)) + } + + /// Section 13.16 Sequence Expression + fn parse_sequence_expression( + &mut self, + node: Node, + first_expression: Expression<'a>, + ) -> Result> { + let mut expressions = self.ast.new_vec_single(first_expression); + while self.eat(Kind::Comma) { + let expression = self.parse_assignment_expression_base()?; + expressions.push(expression); + } + Ok(self.ast.sequence_expression(self.end_node(node), expressions)) + } + + /// `AwaitExpression`[Yield] : + /// await `UnaryExpression`[?Yield, +Await] + fn parse_await_expression(&mut self, lhs_node: Node) -> Result> { + let node = self.start_node(); + self.bump_any(); + let has_await = self.ctx.has_await(); + self.ctx = self.ctx.and_await(true); + let argument = self.parse_unary_expression_base(lhs_node)?; + self.ctx = self.ctx.and_await(has_await); + Ok(self.ast.await_expression(self.end_node(node), argument)) + } + + fn is_await_expression(&mut self) -> bool { + if self.at(Kind::Await) { + if self.ctx.has_await() { + return true; + } + let peek_token = self.peek_token(); + return peek_token.kind.is_after_await_or_yield() && !peek_token.is_on_new_line; + } + false + } + + fn is_yield_expression(&mut self) -> bool { + if self.at(Kind::Yield) { + if self.ctx.has_yield() { + return true; + } + let peek_token = self.peek_token(); + return peek_token.kind.is_after_await_or_yield() && !peek_token.is_on_new_line; + } + false + } +} diff --git a/crates/oxc_parser/src/js/function.rs b/crates/oxc_parser/src/js/function.rs new file mode 100644 index 0000000000000..90ebfedf1a1a1 --- /dev/null +++ b/crates/oxc_parser/src/js/function.rs @@ -0,0 +1,491 @@ +use oxc_allocator::Box; +use oxc_ast::{ + ast::*, + context::{Context, StatementContext}, + GetNode, Node, +}; +use oxc_diagnostics::{Diagnostic, Result}; + +use super::list::FormalParameterList; +use crate::lexer::Kind; +use crate::list::SeparatedList; +use crate::Parser; + +type ArrowFunctionHead<'a> = ( + Option>>, + FormalParameters<'a>, + Option>, + bool, + Node, +); + +#[derive(Debug, Copy, Clone)] +pub enum IsParenthesizedArrowFunction { + True, + False, + Maybe, +} + +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum FunctionKind { + Declaration { single_statement: bool }, + Expression, + DefaultExport, + TSDeclaration, +} + +impl FunctionKind { + pub const fn is_id_required(self) -> bool { + matches!(self, Self::Declaration { single_statement: true }) + } + + pub fn is_expression(self) -> bool { + self == Self::Expression + } +} + +impl<'a> Parser<'a> { + pub fn at_function_with_async(&mut self) -> bool { + self.at(Kind::Function) + || self.at(Kind::Async) + && self.peek_at(Kind::Function) + && !self.peek_token().is_on_new_line + } + + pub fn at_async_no_new_line(&mut self) -> bool { + self.at(Kind::Async) && !self.cur_token().escaped && !self.peek_token().is_on_new_line + } + + pub fn parse_function_body(&mut self) -> Result>> { + let node = self.start_node(); + self.expect(Kind::LCurly)?; + + // We may be in a [Decorator] context when parsing a function expression or + // arrow function. The body of the function is not in [Decorator] context. + let save_decorator_context = self.ctx.has_decorator(); + if save_decorator_context { + self.ctx = self.ctx.and_decorator(false); + } + + let (directives, statements) = self.with_context(Context::Return, |p| { + p.parse_directives_and_statements(/* is_top_level */ false) + })?; + + if save_decorator_context { + self.ctx = self.ctx.and_decorator(true); + } + + self.expect(Kind::RCurly)?; + Ok(self.ast.function_body(self.end_node(node), directives, statements)) + } + + pub fn parse_formal_parameters( + &mut self, + params_kind: FormalParameterKind, + ) -> Result> { + let node = self.start_node(); + let elements = FormalParameterList::parse(self)?.elements; + Ok(self.ast.formal_parameters(self.end_node(node), params_kind, elements)) + } + + pub fn parse_function( + &mut self, + node: Node, + id: Option, + r#async: bool, + generator: bool, + func_kind: FunctionKind, + ) -> Result>> { + let has_await = self.ctx.has_await(); + let has_yield = self.ctx.has_yield(); + self.ctx = self.ctx.and_await(r#async).and_yield(generator); + + let type_parameters = self.parse_ts_type_parameters()?; + + let params = self.parse_formal_parameters(FormalParameterKind::FormalParameter)?; + + let return_type = self.parse_ts_return_type_annotation()?; + + let body = self.at(Kind::LCurly).then(|| self.parse_function_body()).transpose()?; + + self.ctx = self.ctx.and_await(has_await).and_yield(has_yield); + + if !self.ts_enabled() && body.is_none() { + return self.unexpected(); + } + + let function_type = if body.is_none() { + FunctionType::TSDeclareFunction + } else { + match func_kind { + FunctionKind::Declaration { .. } | FunctionKind::DefaultExport => { + FunctionType::FunctionDeclaration + } + FunctionKind::Expression { .. } => FunctionType::FunctionExpression, + FunctionKind::TSDeclaration { .. } => FunctionType::TSDeclareFunction, + } + }; + + if FunctionType::TSDeclareFunction == function_type { + self.asi()?; + } + + Ok(self.ast.function( + function_type, + self.end_node(node), + id, + false, // expression + generator, + r#async, + params, + body, + type_parameters, + return_type, + false, + )) + } + + /// [Function Declaration](https://tc39.es/ecma262/#prod-FunctionDeclaration) + pub fn parse_function_declaration( + &mut self, + stmt_ctx: StatementContext, + ) -> Result> { + let func_kind = + FunctionKind::Declaration { single_statement: stmt_ctx.is_single_statement() }; + let decl = self.parse_function_impl(func_kind)?; + if stmt_ctx.is_single_statement() { + if decl.r#async { + self.error(Diagnostic::AsyncFunctionDeclaration( + decl.node.start..decl.params.node.end, + )); + } else if decl.generator { + self.error(Diagnostic::GeneratorFunctionDeclaration( + decl.node.start..decl.params.node.end, + )); + } + } + + Ok(self.ast.function_declaration(decl)) + } + + pub fn parse_function_impl( + &mut self, + func_kind: FunctionKind, + ) -> Result>> { + let node = self.start_node(); + let r#async = self.eat(Kind::Async); + self.expect(Kind::Function)?; + let generator = self.eat(Kind::Star); + let id = self.parse_function_id(func_kind, r#async, generator); + self.parse_function(node, id, r#async, generator, func_kind) + } + + /// [Function Expression](https://tc39.es/ecma262/#prod-FunctionExpression) + pub fn parse_function_expression( + &mut self, + node: Node, + r#async: bool, + ) -> Result> { + let func_kind = FunctionKind::Expression; + self.expect(Kind::Function)?; + + let save_decorator_context = self.ctx.has_decorator(); + self.ctx = self.ctx.and_decorator(false); + + let generator = self.eat(Kind::Star); + let id = self.parse_function_id(func_kind, r#async, generator); + let function = self.parse_function(node, id, r#async, generator, func_kind)?; + + self.ctx = self.ctx.and_decorator(save_decorator_context); + + Ok(self.ast.function_expression(function)) + } + + pub fn parse_single_param_function_expression( + &mut self, + node: Node, + r#async: bool, + generator: bool, + ) -> Result> { + let has_await = self.ctx.has_await(); + let has_yield = self.ctx.has_yield(); + + self.ctx = self.ctx.union_await_if(r#async).union_yield_if(generator); + let params_node = self.start_node(); + let param = self.parse_binding_identifier()?; + let ident = self.ast.binding_identifier(param); + let pattern = self.ast.binding_pattern(ident, None, false); + let params_node = self.end_node(params_node); + let formal_parameter = self.ast.formal_parameter(params_node, pattern, None, false, None); + let params = self.ast.formal_parameters( + params_node, + FormalParameterKind::ArrowFormalParameters, + self.ast.new_vec_single(formal_parameter), + ); + + self.expect(Kind::Arrow)?; + + self.ctx = self.ctx.and_await(r#async).and_yield(generator); + let expression = !self.at(Kind::LCurly); + let body = if expression { + let expr = self.parse_assignment_expression_base()?; + let node = expr.node(); + let expr_stmt = self.ast.expression_statement(node, expr); + self.ast.function_body(node, self.ast.new_vec(), self.ast.new_vec_single(expr_stmt)) + } else { + self.parse_function_body()? + }; + self.ctx = self.ctx.and_await(has_await).and_yield(has_yield); + + Ok(self.ast.arrow_expression( + self.end_node(node), + expression, + false, + r#async, + params, + body, + None, + None, + )) + } + + /// Section 15.4 Method Definitions + /// `ClassElementName` ( `UniqueFormalParameters` ) { `FunctionBody` } + /// `GeneratorMethod` + /// * `ClassElementName` + /// `AsyncMethod` + /// async `ClassElementName` + /// `AsyncGeneratorMethod` + /// async * `ClassElementName` + pub fn parse_method( + &mut self, + r#async: bool, + generator: bool, + ) -> Result>> { + let node = self.start_node(); + self.parse_function(node, None, r#async, generator, FunctionKind::Expression) + } + + /// Section 15.5 Yield Expression + /// yield + /// yield [no `LineTerminator` here] `AssignmentExpression` + /// yield [no `LineTerminator` here] * `AssignmentExpression` + pub fn parse_yield_expression(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); // advance `yield` + + let mut delegate = false; + let mut argument = None; + + if !self.cur_token().is_on_new_line { + delegate = self.eat(Kind::Star); + let not_assignment_expr = matches!( + self.cur_kind(), + Kind::Semicolon + | Kind::Eof + | Kind::RCurly + | Kind::RParen + | Kind::RBrack + | Kind::Colon + | Kind::Comma + ); + if !not_assignment_expr || delegate { + let has_yield = self.ctx.has_yield(); + self.ctx = self.ctx.union_yield_if(true); + argument = Some(self.parse_assignment_expression_base()?); + self.ctx = self.ctx.and_yield(has_yield); + } + } + + Ok(self.ast.yield_expression(self.end_node(node), delegate, argument)) + } + + // id: None - for AnonymousDefaultExportedFunctionDeclaration + pub fn parse_function_id( + &mut self, + kind: FunctionKind, + r#async: bool, + generator: bool, + ) -> Option { + let ctx = self.ctx; + if kind.is_expression() { + self.ctx = self.ctx.and_await(r#async).and_yield(generator); + } + let id = self.cur_kind().is_binding_identifier().then(|| { + let (node, name) = self.parse_identifier_kind(Kind::Ident); + BindingIdentifier { node, name } + }); + self.ctx = ctx; + + if kind.is_id_required() && id.is_none() { + self.error(Diagnostic::ExpectFunctionName(self.cur_token().range())); + } + + id + } + + pub fn is_parenthesized_arrow_function_expression( + &mut self, + r#async: bool, + ) -> IsParenthesizedArrowFunction { + let offset = usize::from(r#async); + + match self.nth_kind(offset) { + Kind::LParen => match self.nth_kind(offset + 1) { + // '()' is an arrow expression if followed by an '=>', a type annotation or body. + // Otherwise, a parenthesized expression with a missing inner expression + Kind::RParen + if matches!( + self.nth_kind(offset + 2), + Kind::Arrow | Kind::Colon | Kind::LCurly + ) => + { + IsParenthesizedArrowFunction::True + } + // Rest parameter '(...a' is certainly not a parenthesized expression + Kind::Dot3 => IsParenthesizedArrowFunction::True, + // '([ ...', '({ ... } can either be a parenthesized object or array expression or a destructing parameter + Kind::LBrack | Kind::LCurly => IsParenthesizedArrowFunction::Maybe, + _ if self.nth_kind(offset + 1).is_binding_identifier() + || self.nth_at(offset + 1, Kind::This) => + { + match self.nth_kind(offset + 2) { + // '(a: ' must be a type annotation + Kind::Colon => IsParenthesizedArrowFunction::True, + // * '(a = ': an initializer or a parenthesized assignment expression + // * '(a, ': separator to next parameter or a parenthesized sequence expression + // * '(a)': a single parameter OR a parenthesized expression + Kind::Eq | Kind::Comma | Kind::RParen => { + IsParenthesizedArrowFunction::Maybe + } + // '(a?:' | '(a?,' | '(a?=' | '(a?)' + Kind::Question + if matches!( + self.nth_kind(offset + 3), + Kind::Colon | Kind::Comma | Kind::Eq | Kind::RParen + ) => + { + IsParenthesizedArrowFunction::True + } + _ => IsParenthesizedArrowFunction::False, + } + } + _ => IsParenthesizedArrowFunction::False, + }, + Kind::LAngle => { + if !self.nth_kind(offset + 1).is_identifier() { + return IsParenthesizedArrowFunction::False; + } + + if self.source_type.is_jsx() { + return match self.nth_kind(offset + 2) { + Kind::Extends => { + let third_kind = self.nth_kind(offset + 3); + if matches!(third_kind, Kind::Eq | Kind::RAngle) { + IsParenthesizedArrowFunction::False + } else if third_kind.is_identifier() { + IsParenthesizedArrowFunction::Maybe + } else { + IsParenthesizedArrowFunction::True + } + } + Kind::Eq | Kind::Comma => IsParenthesizedArrowFunction::True, + _ => IsParenthesizedArrowFunction::False, + }; + } + + IsParenthesizedArrowFunction::Maybe + } + _ => IsParenthesizedArrowFunction::False, + } + } + + pub fn is_parenthesized_arrow_function(&mut self) -> IsParenthesizedArrowFunction { + match self.cur_kind() { + Kind::LAngle | Kind::LParen => self.is_parenthesized_arrow_function_expression(false), + Kind::Async => { + let peeked = self.peek_token(); + if !peeked.is_on_new_line && matches!(peeked.kind, Kind::LAngle | Kind::LParen) { + self.is_parenthesized_arrow_function_expression(true) + } else { + IsParenthesizedArrowFunction::False + } + } + _ => IsParenthesizedArrowFunction::False, + } + } + + pub fn parse_parenthesized_arrow_function_head(&mut self) -> Result> { + let node = self.start_node(); + let r#async = self.eat(Kind::Async); + + let has_await = self.ctx.has_await(); + self.ctx = self.ctx.union_await_if(r#async); + + let type_parameters = self.parse_ts_type_parameters()?; + + let params = self.parse_formal_parameters(FormalParameterKind::ArrowFormalParameters)?; + + let return_type = self.parse_ts_return_type_annotation()?; + + self.ctx = self.ctx.and_await(has_await); + + if self.cur_token().is_on_new_line { + self.error(Diagnostic::LineterminatorBeforeArrow(self.cur_token().range())); + } + + self.expect(Kind::Arrow)?; + + Ok((type_parameters, params, return_type, r#async, node)) + } + + /// [`ConciseBody`](https://tc39.es/ecma262/#prod-ConciseBody) + /// [lookahead ≠ {] `ExpressionBody`[?In, ~Await] + /// { `FunctionBody`[~Yield, ~Await] } + /// `ExpressionBody`[In, Await] : + /// `AssignmentExpression`[?In, ~Yield, ?Await] + pub fn parse_arrow_function_body( + &mut self, + node: Node, + type_parameters: Option>>, + params: FormalParameters<'a>, + return_type: Option>, + r#async: bool, + ) -> Result> { + let has_await = self.ctx.has_await(); + let has_yield = self.ctx.has_yield(); + self.ctx = self.ctx.and_await(r#async).and_yield(false); + + let expression = !self.at(Kind::LCurly); + let body = if expression { + let expr = self.parse_assignment_expression_base()?; + let node = expr.node(); + let expr_stmt = self.ast.expression_statement(node, expr); + self.ast.function_body(node, self.ast.new_vec(), self.ast.new_vec_single(expr_stmt)) + } else { + self.parse_function_body()? + }; + + self.ctx = self.ctx.and_await(has_await).and_yield(has_yield); + + Ok(self.ast.arrow_expression( + self.end_node(node), + expression, + false, + r#async, + params, + body, + type_parameters, + return_type, + )) + } + + /// Section Arrow Function `https://tc39.es/ecma262/#sec-arrow-function-definitions` + /// `ArrowFunction`[In, Yield, Await] : + /// `ArrowParameters`[?Yield, ?Await] [no `LineTerminator` here] => `ConciseBody`[?In] + pub fn parse_parenthesized_arrow_function(&mut self) -> Result> { + let (type_parameters, params, return_type, r#async, node) = + self.parse_parenthesized_arrow_function_head()?; + self.parse_arrow_function_body(node, type_parameters, params, return_type, r#async) + } +} diff --git a/crates/oxc_parser/src/js/grammar.rs b/crates/oxc_parser/src/js/grammar.rs new file mode 100644 index 0000000000000..92b38384f5f45 --- /dev/null +++ b/crates/oxc_parser/src/js/grammar.rs @@ -0,0 +1,180 @@ +//! Cover Grammar for Destructuring Assignment + +use oxc_ast::{ast::*, GetNode}; +use oxc_diagnostics::{Diagnostic, Result}; + +use crate::Parser; + +pub trait CoverGrammar<'a, T>: Sized { + fn cover(value: T, p: &mut Parser<'a>) -> Result; +} + +impl<'a> CoverGrammar<'a, Expression<'a>> for AssignmentTarget<'a> { + fn cover(expr: Expression<'a>, p: &mut Parser<'a>) -> Result { + match expr { + Expression::ArrayExpression(array_expr) => { + ArrayAssignmentTarget::cover(array_expr.unbox(), p) + .map(|pat| p.ast.alloc(pat)) + .map(AssignmentTargetPattern::ArrayAssignmentTarget) + .map(AssignmentTarget::AssignmentTargetPattern) + } + Expression::ObjectExpression(object_expr) => { + ObjectAssignmentTarget::cover(object_expr.unbox(), p) + .map(|pat| p.ast.alloc(pat)) + .map(AssignmentTargetPattern::ObjectAssignmentTarget) + .map(AssignmentTarget::AssignmentTargetPattern) + } + _ => { + SimpleAssignmentTarget::cover(expr, p).map(AssignmentTarget::SimpleAssignmentTarget) + } + } + } +} + +impl<'a> CoverGrammar<'a, Expression<'a>> for SimpleAssignmentTarget<'a> { + #[allow(clippy::only_used_in_recursion)] + fn cover(expr: Expression<'a>, p: &mut Parser<'a>) -> Result { + match expr { + Expression::Identifier(ident) => { + Ok(SimpleAssignmentTarget::AssignmentTargetIdentifier(ident)) + } + Expression::MemberExpression(expr) => { + Ok(SimpleAssignmentTarget::MemberAssignmentTarget(expr)) + } + Expression::ParenthesizedExpression(expr) => { + let node = expr.node; + match expr.unbox().expression { + Expression::ObjectExpression(_) | Expression::ArrayExpression(_) => { + Err(Diagnostic::InvalidAssignment(node.range())) + } + expr => SimpleAssignmentTarget::cover(expr, p), + } + } + Expression::TSAsExpression(expr) => Ok(SimpleAssignmentTarget::TSAsExpression(expr)), + Expression::TSNonNullExpression(expr) => { + Ok(SimpleAssignmentTarget::TSNonNullExpression(expr)) + } + Expression::TSTypeAssertion(expr) => Ok(SimpleAssignmentTarget::TSTypeAssertion(expr)), + expr => Err(Diagnostic::InvalidAssignment(expr.node().range())), + } + } +} + +impl<'a> CoverGrammar<'a, ArrayExpression<'a>> for ArrayAssignmentTarget<'a> { + fn cover(expr: ArrayExpression<'a>, p: &mut Parser<'a>) -> Result { + let mut elements = p.ast.new_vec(); + let mut rest = None; + + let len = expr.elements.len(); + for (i, elem) in expr.elements.into_iter().enumerate() { + if let Some(argument) = elem { + match argument { + Argument::Expression(expr) => { + let target = AssignmentTargetMaybeDefault::cover(expr, p)?; + elements.push(Some(target)); + } + Argument::SpreadElement(elem) => { + if i == len - 1 { + rest = Some(AssignmentTarget::cover(elem.unbox().argument, p)?); + if let Some(node) = expr.trailing_comma { + p.error(Diagnostic::RestElementTraillingComma(node.range())); + } + } else { + return Err(Diagnostic::SpreadLastElement(elem.node.range())); + } + } + } + } else { + elements.push(None); + } + } + + Ok(ArrayAssignmentTarget { + node: expr.node, + elements, + rest, + trailing_comma: expr.trailing_comma, + }) + } +} + +impl<'a> CoverGrammar<'a, Expression<'a>> for AssignmentTargetMaybeDefault<'a> { + fn cover(expr: Expression<'a>, p: &mut Parser<'a>) -> Result { + match expr { + Expression::AssignmentExpression(assignment_expr) => { + let target = AssignmentTargetWithDefault::cover(assignment_expr.unbox(), p)?; + Ok(AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(p.ast.alloc(target))) + } + expr => { + let target = AssignmentTarget::cover(expr, p)?; + Ok(AssignmentTargetMaybeDefault::AssignmentTarget(p.ast.alloc(target))) + } + } + } +} + +impl<'a> CoverGrammar<'a, AssignmentExpression<'a>> for AssignmentTargetWithDefault<'a> { + fn cover(expr: AssignmentExpression<'a>, _p: &mut Parser<'a>) -> Result { + Ok(Self { node: expr.node, binding: expr.left, init: expr.right }) + } +} + +impl<'a> CoverGrammar<'a, ObjectExpression<'a>> for ObjectAssignmentTarget<'a> { + fn cover(expr: ObjectExpression<'a>, p: &mut Parser<'a>) -> Result { + let mut properties = p.ast.new_vec(); + let mut rest = None; + + let len = expr.properties.len(); + for (i, elem) in expr.properties.into_iter().enumerate() { + match elem { + ObjectProperty::Property(property) => { + let target = AssignmentTargetProperty::cover(property.unbox(), p)?; + properties.push(target); + } + ObjectProperty::SpreadProperty(spread) => { + if i == len - 1 { + rest = Some(AssignmentTarget::cover(spread.unbox().argument, p)?); + } else { + return Err(Diagnostic::SpreadLastElement(spread.node.range())); + } + } + } + } + + Ok(Self { node: expr.node, properties, rest }) + } +} + +impl<'a> CoverGrammar<'a, Property<'a>> for AssignmentTargetProperty<'a> { + fn cover(property: Property<'a>, p: &mut Parser<'a>) -> Result { + if property.shorthand { + let binding = match property.key { + PropertyKey::Identifier(ident) => { + IdentifierReference { node: ident.node, name: ident.name } + } + _ => return p.unexpected(), + }; + let init = match property.value { + PropertyValue::Expression(Expression::AssignmentExpression(assignment_expr)) => { + Some(assignment_expr.unbox().right) + } + _ => None, + }; + let target = AssignmentTargetPropertyIdentifier { node: property.node, binding, init }; + Ok(AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(p.ast.alloc(target))) + } else { + let binding = match property.value { + PropertyValue::Expression(expr) => AssignmentTargetMaybeDefault::cover(expr, p)?, + PropertyValue::Pattern(_) => { + return Err(Diagnostic::InvalidAssignment(property.value.node().range())); + } + }; + let target = AssignmentTargetPropertyProperty { + node: property.node, + name: property.key, + binding, + }; + Ok(AssignmentTargetProperty::AssignmentTargetPropertyProperty(p.ast.alloc(target))) + } + } +} diff --git a/crates/oxc_parser/src/js/list.rs b/crates/oxc_parser/src/js/list.rs new file mode 100644 index 0000000000000..91285af0b9327 --- /dev/null +++ b/crates/oxc_parser/src/js/list.rs @@ -0,0 +1,479 @@ +use oxc_allocator::Vec; +use oxc_ast::{ast::*, syntax_directed_operations::PrivateBoundIdentifiers, Atom, GetNode, Node}; +use oxc_diagnostics::{Diagnostic, Result}; +use rustc_hash::FxHashMap; + +use crate::lexer::Kind; +use crate::list::{NormalList, SeparatedList}; +use crate::Parser; + +/// ObjectExpression.properties +pub struct ObjectExpressionProperties<'a> { + pub elements: Vec<'a, ObjectProperty<'a>>, + pub trailing_comma: Option, +} + +impl<'a> SeparatedList<'a> for ObjectExpressionProperties<'a> { + fn new(p: &Parser<'a>) -> Self { + Self { elements: p.ast.new_vec(), trailing_comma: None } + } + + fn open(&self) -> Kind { + Kind::LCurly + } + + fn close(&self) -> Kind { + Kind::RCurly + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let element = match p.cur_kind() { + Kind::Dot3 => p.parse_spread_element().map(ObjectProperty::SpreadProperty), + _ => p.parse_property_definition().map(ObjectProperty::Property), + }?; + + if p.at(Kind::Comma) && p.peek_at(self.close()) { + self.trailing_comma = Some(p.end_node(p.start_node())); + } + + self.elements.push(element); + Ok(()) + } +} + +/// ObjectPattern.properties +pub struct ObjectPatternProperties<'a> { + pub elements: Vec<'a, ObjectPatternProperty<'a>>, +} + +impl<'a> SeparatedList<'a> for ObjectPatternProperties<'a> { + fn new(p: &Parser<'a>) -> Self { + Self { elements: p.ast.new_vec() } + } + + fn open(&self) -> Kind { + Kind::LCurly + } + + fn close(&self) -> Kind { + Kind::RCurly + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let element = match p.cur_kind() { + Kind::Dot3 => { + let rest_element = p.parse_rest_element()?; + + if !matches!(rest_element.argument.kind, BindingPatternKind::BindingIdentifier(_)) { + p.error(Diagnostic::InvalidRestArgument(rest_element.node.range())); + } + + ObjectPatternProperty::RestElement(rest_element) + } + _ => p + .parse_object_pattern_property() + .map(|prop| p.ast.alloc(prop)) + .map(ObjectPatternProperty::Property)?, + }; + self.elements.push(element); + Ok(()) + } +} + +/// ArrayExpression.elements, with optional element +pub struct ArrayExpressionList<'a> { + pub elements: Vec<'a, Option>>, + pub trailing_comma: Option, +} + +impl<'a> SeparatedList<'a> for ArrayExpressionList<'a> { + fn new(p: &Parser<'a>) -> Self { + Self { elements: p.ast.new_vec(), trailing_comma: None } + } + + fn open(&self) -> Kind { + Kind::LBrack + } + + fn close(&self) -> Kind { + Kind::RBrack + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let element = match p.cur_kind() { + Kind::Comma => Ok(None), + Kind::Dot3 => p.parse_spread_element().map(Argument::SpreadElement).map(Some), + _ => p.parse_assignment_expression_base().map(Argument::Expression).map(Some), + }; + + if p.at(Kind::Comma) && p.peek_at(self.close()) { + self.trailing_comma = Some(p.end_node(p.start_node())); + } + + self.elements.push(element?); + Ok(()) + } +} + +/// ArrayPattern.elements, with optional element +pub struct ArrayPatternList<'a> { + pub elements: Vec<'a, Option>>, +} + +impl<'a> SeparatedList<'a> for ArrayPatternList<'a> { + fn new(p: &Parser<'a>) -> Self { + Self { elements: p.ast.new_vec() } + } + + fn open(&self) -> Kind { + Kind::LBrack + } + + fn close(&self) -> Kind { + Kind::RBrack + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let element = match p.cur_kind() { + Kind::Comma => None, + Kind::Dot3 => { + p.parse_rest_element().map(|rest| p.ast.rest_element_pattern(rest)).map(Some)? + } + _ => p.parse_binding_element().map(Some)?, + }; + self.elements.push(element); + Ok(()) + } +} + +/// Section 13.3 Arguments for `CallExpression`, `NewExpression` +pub struct CallArguments<'a> { + pub elements: Vec<'a, Argument<'a>>, + pub rest_element_with_trilling_comma: Option, +} + +impl<'a> SeparatedList<'a> for CallArguments<'a> { + fn new(p: &Parser<'a>) -> Self { + Self { elements: p.ast.new_vec(), rest_element_with_trilling_comma: None } + } + + fn open(&self) -> Kind { + Kind::LParen + } + + fn close(&self) -> Kind { + Kind::RParen + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let element = if p.at(Kind::Dot3) { + let result = p.parse_spread_element().map(Argument::SpreadElement); + if p.at(Kind::Comma) { + if let Ok(Argument::SpreadElement(argument)) = &result { + self.rest_element_with_trilling_comma = Some(argument.node); + } + } + result + } else { + p.parse_assignment_expression_base().map(Argument::Expression) + }; + self.elements.push(element?); + Ok(()) + } +} + +pub struct SequenceExpressionList<'a> { + pub node: Node, + pub elements: Vec<'a, Expression<'a>>, +} + +impl<'a> SeparatedList<'a> for SequenceExpressionList<'a> { + fn new(p: &Parser<'a>) -> Self { + Self { elements: p.ast.new_vec(), node: Node::default() } + } + + fn open(&self) -> Kind { + Kind::LParen + } + + fn close(&self) -> Kind { + Kind::RParen + } + + fn start_sequence(&mut self, p: &mut Parser) { + self.node = p.start_node(); + } + + fn finish_sequence(&mut self, p: &mut Parser) { + self.node = p.end_node(self.node); + } + + // read everything as expression and map to it to either + // ParenthesizedExpression or ArrowFormalParameters later + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let element = p.parse_assignment_expression_base()?; + self.elements.push(element); + Ok(()) + } +} + +/// Function Parameters +pub struct FormalParameterList<'a> { + pub elements: Vec<'a, FormalParameter<'a>>, +} + +impl<'a> SeparatedList<'a> for FormalParameterList<'a> { + fn new(p: &Parser<'a>) -> Self { + Self { elements: p.ast.new_vec() } + } + + fn open(&self) -> Kind { + Kind::LParen + } + + fn close(&self) -> Kind { + Kind::RParen + } + + // Section 15.1 Parameter Lists + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let node = p.start_node(); + p.eat_decorators()?; + + let modifiers = p.parse_class_element_modifiers(true); + let accessibility = modifiers.accessibility(); + let readonly = modifiers.readonly(); + + let pattern = match p.cur_kind() { + Kind::Dot3 => p.parse_rest_element().map(|rest| p.ast.rest_element_pattern(rest))?, + Kind::This if p.ts_enabled() => { + p.parse_ts_this_parameter()?; + // don't add this to ast fow now, the ast node shouldn't be in BindingIdentifier + return Ok(()); + } + _ => p.parse_binding_element()?, + }; + + let decorators = p.state.consume_decorators(); + let formal_parameter = + p.ast.formal_parameter(p.end_node(node), pattern, accessibility, readonly, decorators); + self.elements.push(formal_parameter); + + Ok(()) + } +} + +/// Assert Entries +/// `https://tc39.es/proposal-import-assertions` +pub struct AssertEntries<'a> { + pub elements: Vec<'a, ImportAttribute>, + keys: FxHashMap, +} + +impl<'a> SeparatedList<'a> for AssertEntries<'a> { + fn new(p: &Parser<'a>) -> Self { + Self { elements: p.ast.new_vec(), keys: FxHashMap::default() } + } + + fn open(&self) -> Kind { + Kind::LCurly + } + + fn close(&self) -> Kind { + Kind::RCurly + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let node = p.start_node(); + let key = match p.cur_kind() { + Kind::Str => ImportAttributeKey::StringLiteral(p.parse_literal_string()?), + _ => ImportAttributeKey::Identifier(p.parse_identifier_name()?), + }; + + if let Some(old_node) = self.keys.get(&key.as_atom()) { + p.error(Diagnostic::Redeclaration(key.as_atom(), old_node.range(), key.node().range())); + } else { + self.keys.insert(key.as_atom(), key.node()); + } + + p.expect(Kind::Colon)?; + let value = p.parse_literal_string()?; + let element = ImportAttribute { node: p.end_node(node), key, value }; + self.elements.push(element); + Ok(()) + } +} + +pub struct ExportNamedSpecifiers<'a> { + pub elements: Vec<'a, ExportSpecifier>, +} + +impl<'a> SeparatedList<'a> for ExportNamedSpecifiers<'a> { + fn new(p: &Parser<'a>) -> Self { + Self { elements: p.ast.new_vec() } + } + + fn open(&self) -> Kind { + Kind::LCurly + } + + fn close(&self) -> Kind { + Kind::RCurly + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let specifier_node = p.start_node(); + let peek_kind = p.peek_kind(); + + // export { type} // name: `type` + // export { type type } // name: `type` type-export: `true` + // export { type as } // name: `as` type-export: `true` + // export { type as as } // name: `type` type-export: `false` (aliased to `as`) + // export { type as as as } // name: `as` type-export: `true`, aliased to `as` + let mut export_kind = ImportOrExportKind::Value; + if p.ts_enabled() && p.at(Kind::Type) { + if p.peek_at(Kind::As) { + if p.nth_at(2, Kind::As) { + if p.nth_at(3, Kind::Str) || p.nth_kind(3).is_identifier_name() { + export_kind = ImportOrExportKind::Type; + } + } else if !(p.nth_at(2, Kind::Str) || p.nth_kind(2).is_identifier_name()) { + export_kind = ImportOrExportKind::Type; + } + } else if (matches!(peek_kind, Kind::Str) || peek_kind.is_identifier_name()) { + export_kind = ImportOrExportKind::Type; + } + } + + if export_kind == ImportOrExportKind::Type { + p.bump_any(); + } + + let local = p.parse_module_export_name()?; + let exported = if p.eat(Kind::As) { p.parse_module_export_name()? } else { local.clone() }; + let element = ExportSpecifier { node: p.end_node(specifier_node), local, exported }; + self.elements.push(element); + Ok(()) + } +} + +pub struct PrivateBoundIdentifierMeta { + node: Node, + r#static: bool, + kind: Option, +} + +pub struct ClassElements<'a> { + pub elements: Vec<'a, ClassElement<'a>>, + + /// https://tc39.es/ecma262/#sec-static-semantics-privateboundidentifiers + pub private_bound_identifiers: FxHashMap, +} + +impl<'a> ClassElements<'a> { + pub fn new(p: &mut Parser<'a>) -> Self { + Self { elements: p.ast.new_vec(), private_bound_identifiers: FxHashMap::default() } + } + + fn detect_private_name_conflict( + &self, + p: &mut Parser, + private_ident: &PrivateIdentifier, + r#static: bool, + kind: Option, + ) { + if let Some(existed) = self.private_bound_identifiers.get(&private_ident.name) { + if !(r#static == existed.r#static + && match existed.kind { + Some(MethodDefinitionKind::Get) => { + kind.as_ref().map_or(false, |kind| *kind == MethodDefinitionKind::Set) + } + Some(MethodDefinitionKind::Set) => { + kind.as_ref().map_or(false, |kind| *kind == MethodDefinitionKind::Get) + } + _ => false, + }) + { + p.error(Diagnostic::Redeclaration( + private_ident.name.clone(), + existed.node.range(), + private_ident.node.range(), + )); + } + } + } + + fn on_declare_private_property( + &mut self, + p: &mut Parser, + private_ident: &PrivateIdentifier, + r#static: bool, + kind: Option, + ) { + self.detect_private_name_conflict(p, private_ident, r#static, kind); + + self.private_bound_identifiers.insert( + private_ident.name.clone(), + PrivateBoundIdentifierMeta { r#static, kind, node: private_ident.node }, + ); + } +} + +impl<'a> NormalList<'a> for ClassElements<'a> { + fn open(&self) -> Kind { + Kind::LCurly + } + + fn close(&self) -> Kind { + Kind::RCurly + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + // skip empty class element `;` + while p.at(Kind::Semicolon) { + p.bump_any(); + } + if p.at(self.close()) { + return Ok(()); + } + let element = p.parse_class_element()?; + + if let Some(private_ident) = element.private_bound_identifiers() { + self.on_declare_private_property( + p, + &private_ident, + element.r#static(), + element.method_definition_kind(), + ); + } + + self.elements.push(element); + Ok(()) + } +} + +pub struct SwitchCases<'a> { + pub elements: Vec<'a, SwitchCase<'a>>, +} + +impl<'a> SwitchCases<'a> { + pub fn new(p: &mut Parser<'a>) -> Self { + Self { elements: p.ast.new_vec() } + } +} + +impl<'a> NormalList<'a> for SwitchCases<'a> { + fn open(&self) -> Kind { + Kind::LCurly + } + + fn close(&self) -> Kind { + Kind::RCurly + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let element = p.parse_switch_case()?; + self.elements.push(element); + Ok(()) + } +} diff --git a/crates/oxc_parser/src/js/mod.rs b/crates/oxc_parser/src/js/mod.rs new file mode 100644 index 0000000000000..dfe67a0ec6c0f --- /dev/null +++ b/crates/oxc_parser/src/js/mod.rs @@ -0,0 +1,16 @@ +//! JavaScript Parsing Functions + +#![allow(clippy::missing_errors_doc)] + +mod grammar; +pub mod list; + +mod binding; +mod class; +pub mod declaration; +mod expression; +pub mod function; +mod module; +mod object; +mod operator; +mod statement; diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs new file mode 100644 index 0000000000000..40a9097b145ae --- /dev/null +++ b/crates/oxc_parser/src/js/module.rs @@ -0,0 +1,412 @@ +use oxc_allocator::{Box, Vec}; +use oxc_ast::{ast::*, context::Context, Node}; +use oxc_diagnostics::{Diagnostic, Result}; + +use super::function::FunctionKind; +use super::list::{AssertEntries, ExportNamedSpecifiers}; +use crate::lexer::Kind; +use crate::list::SeparatedList; +use crate::Parser; + +impl<'a> Parser<'a> { + /// [Import Call](https://tc39.es/ecma262/#sec-import-calls) + /// `ImportCall` : import ( `AssignmentExpression` ) + pub fn parse_import_expression(&mut self, node: Node) -> Result> { + self.bump_any(); // advance '(' + + let has_in = self.ctx.has_in(); + self.ctx = self.ctx.and_in(true); + + let expression = self.parse_assignment_expression_base()?; + let mut arguments = self.ast.new_vec(); + if self.eat(Kind::Comma) && !self.at(Kind::RParen) { + arguments.push(self.parse_assignment_expression_base()?); + } + + self.ctx = self.ctx.and_in(has_in); + self.ctx = self.ctx.and_in(has_in); + self.bump(Kind::Comma); + self.expect(Kind::RParen)?; + Ok(self.ast.import_expression(self.end_node(node), expression, arguments)) + } + + /// Section 16.2.2 Import Declaration + pub fn parse_import_declaration(&mut self) -> Result> { + let node = self.start_node(); + + self.bump_any(); // advance `import` + + if self.ts_enabled() + && ((self.cur_kind().is_binding_identifier() && self.peek_at(Kind::Eq)) + || (self.at(Kind::Type) + && self.peek_kind().is_binding_identifier() + && self.nth_at(2, Kind::Eq))) + { + let decl = self.parse_ts_import_equals_declaration(node, false)?; + return Ok(Statement::Declaration(decl)); + } + + // `import type ...` + let import_kind = self.parse_import_or_export_kind(); + + let specifiers = if self.at(Kind::Str) { + // import "source" + self.ast.new_vec() + } else { + self.parse_import_declaration_specifiers()? + }; + + let source = self.parse_literal_string()?; + let assertions = self.parse_import_attributes()?; + self.asi()?; + + let node = self.end_node(node); + let kind = ModuleDeclarationKind::ImportDeclaration(self.ast.import_declaration( + specifiers, + source, + assertions, + import_kind, + )); + + Ok(self.ast.module_declaration(node, kind)) + } + + // Full Syntax: + // `https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#syntax` + fn parse_import_declaration_specifiers( + &mut self, + ) -> Result> { + let mut specifiers = self.ast.new_vec(); + // import defaultExport from "module-name"; + if self.cur_kind().is_binding_identifier() { + specifiers.push(self.parse_import_default_specifier()?); + if self.eat(Kind::Comma) { + match self.cur_kind() { + // import defaultExport, * as name from "module-name"; + Kind::Star => specifiers.push(self.parse_import_namespace_specifier()?), + // import defaultExport, { export1 [ , [...] ] } from "module-name"; + Kind::LCurly => { + let mut import_specifiers = self.parse_import_specifiers()?; + specifiers.append(&mut import_specifiers); + } + _ => return self.unexpected(), + } + } + // import * as name from "module-name"; + } else if self.at(Kind::Star) { + specifiers.push(self.parse_import_namespace_specifier()?); + // import { export1 , export2 as alias2 , [...] } from "module-name"; + } else if self.at(Kind::LCurly) { + let mut import_specifiers = self.parse_import_specifiers()?; + specifiers.append(&mut import_specifiers); + }; + + self.expect(Kind::From)?; + Ok(specifiers) + } + + // import default from "module-name" + fn parse_import_default_specifier(&mut self) -> Result { + let node = self.start_node(); + let local = self.parse_binding_identifier()?; + Ok(ImportDeclarationSpecifier::ImportDefaultSpecifier(ImportDefaultSpecifier { + node: self.end_node(node), + local, + })) + } + + // import * as name from "module-name" + fn parse_import_namespace_specifier(&mut self) -> Result { + let node = self.start_node(); + self.bump_any(); // advance `*` + self.expect(Kind::As)?; + let local = self.parse_binding_identifier()?; + Ok(ImportDeclarationSpecifier::ImportNamespaceSpecifier(ImportNamespaceSpecifier { + node: self.end_node(node), + local, + })) + } + + // import { export1 , export2 as alias2 , [...] } from "module-name"; + fn parse_import_specifiers(&mut self) -> Result> { + self.bump_any(); // advance `{` + let mut first = true; + let mut specifiers = self.ast.new_vec(); + // TODO: move to list.rs + while !self.at(Kind::RCurly) && !self.at(Kind::Eof) { + if first { + first = false; + } else { + self.expect(Kind::Comma)?; + if self.at(Kind::RCurly) { + break; + } + } + let import_specifier = self.parse_import_specifier()?; + let specifier = ImportDeclarationSpecifier::ImportSpecifier(import_specifier); + specifiers.push(specifier); + } + self.expect(Kind::RCurly)?; + Ok(specifiers) + } + + // import assertion + // https://tc39.es/proposal-import-assertions + fn parse_import_attributes(&mut self) -> Result>> { + if !self.at(Kind::Assert) || self.cur_token().is_on_new_line { + return Ok(None); + } + self.bump_any(); + + let ctx = self.ctx; + self.ctx = Context::default(); + let entries = AssertEntries::parse(self)?.elements; + self.ctx = ctx; + + Ok(Some(entries)) + } + + pub fn parse_ts_export_assignment_declaration( + &mut self, + ) -> Result>> { + let node = self.start_node(); + self.expect(Kind::Eq)?; + + let expression = self.parse_assignment_expression_base()?; + self.asi()?; + + Ok(self.ast.alloc(TSExportAssignment { node: self.end_node(node), expression })) + } + + pub fn parse_ts_export_namespace(&mut self) -> Result> { + let node = self.start_node(); + self.expect(Kind::As)?; + self.expect(Kind::Namespace)?; + + let id = self.parse_identifier_name()?; + self.asi()?; + + Ok(self.ast.alloc(TSNamespaceExportDeclaration { node: self.end_node(node), id })) + } + + /// Exports + /// `https://tc39.es/ecma262/#sec-exports` + pub fn parse_export_declaration(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); // advance `export` + + let kind = match self.cur_kind() { + Kind::Eq if self.ts_enabled() => self + .parse_ts_export_assignment_declaration() + .map(ModuleDeclarationKind::TSExportAssignment), + Kind::As if self.peek_at(Kind::Namespace) && self.ts_enabled() => self + .parse_ts_export_namespace() + .map(ModuleDeclarationKind::TSNamespaceExportDeclaration), + Kind::Default => self + .parse_export_default_declaration() + .map(ModuleDeclarationKind::ExportDefaultDeclaration), + Kind::Star => { + self.parse_export_all_declaration().map(ModuleDeclarationKind::ExportAllDeclaration) + } + Kind::LCurly => self + .parse_export_named_specifiers() + .map(ModuleDeclarationKind::ExportNamedDeclaration), + Kind::Type if self.peek_at(Kind::LCurly) && self.ts_enabled() => self + .parse_export_named_specifiers() + .map(ModuleDeclarationKind::ExportNamedDeclaration), + _ => self + .parse_export_named_declaration() + .map(ModuleDeclarationKind::ExportNamedDeclaration), + }?; + Ok(self.ast.module_declaration(self.end_node(node), kind)) + } + + // export NamedExports ; + // NamedExports : + // { } + // { ExportsList } + // { ExportsList , } + // ExportsList : + // ExportSpecifier + // ExportsList , ExportSpecifier + // ExportSpecifier : + // ModuleExportName + // ModuleExportName as ModuleExportName + fn parse_export_named_specifiers(&mut self) -> Result>> { + let export_kind = self.parse_import_or_export_kind(); + + let ctx = self.ctx; + self.ctx = Context::default(); + let specifiers = ExportNamedSpecifiers::parse(self)?.elements; + self.ctx = ctx; + + let source = if self.eat(Kind::From) && self.cur_kind().is_literal() { + let source = self.parse_literal_string()?; + Some(source) + } else { + None + }; + + // ExportDeclaration : export NamedExports ; + // * It is a Syntax Error if ReferencedBindings of NamedExports contains any StringLiterals. + if source.is_none() { + for specifier in &specifiers { + if let ModuleExportName::StringLiteral(literal) = &specifier.local { + self.error(Diagnostic::ExportNamedString( + literal.value.clone(), + specifier.local.name().clone(), + literal.node.range(), + )); + } + } + } + + self.asi()?; + Ok(self.ast.export_named_declaration(None, specifiers, source, export_kind)) + } + + // export Declaration + fn parse_export_named_declaration(&mut self) -> Result>> { + let declaration = self.parse_declaration_clause()?; + Ok(self.ast.export_named_declaration(Some(declaration), self.ast.new_vec(), None, None)) + } + + // export default HoistableDeclaration[~Yield, +Await, +Default] + // export default ClassDeclaration[~Yield, +Await, +Default] + // export default AssignmentExpression[+In, ~Yield, +Await] ; + fn parse_export_default_declaration( + &mut self, + ) -> Result>> { + let exported = self.parse_keyword_identifier(Kind::Default); + let declaration = match self.cur_kind() { + Kind::Class => self + .parse_class_declaration(/* declare */ false) + .map(ExportDefaultDeclarationKind::ClassDeclaration)?, + _ if self.at(Kind::Abstract) && self.peek_at(Kind::Class) && self.ts_enabled() => { + self.parse_class_declaration(/* declare */ false) + .map(ExportDefaultDeclarationKind::ClassDeclaration)? + } + _ if self.at(Kind::Interface) + && !self.peek_token().is_on_new_line + && self.ts_enabled() => + { + self.parse_ts_interface_declaration(false, self.start_node()).map( + |decl| match decl { + Declaration::TSInterfaceDeclaration(decl) => { + ExportDefaultDeclarationKind::TSInterfaceDeclaration(decl) + } + _ => unreachable!(), + }, + )? + } + _ if self.at(Kind::Enum) && self.ts_enabled() => { + self.parse_ts_enum_declaration(false, self.start_node()).map(|decl| match decl { + Declaration::TSEnumDeclaration(decl) => { + ExportDefaultDeclarationKind::TSEnumDeclaration(decl) + } + _ => unreachable!(), + })? + } + _ if self.at_function_with_async() => self + .parse_function_impl(FunctionKind::DefaultExport) + .map(ExportDefaultDeclarationKind::FunctionDeclaration)?, + _ => { + let decl = self + .parse_assignment_expression_base() + .map(ExportDefaultDeclarationKind::Expression)?; + self.asi()?; + decl + } + }; + let exported = ModuleExportName::Identifier(exported); + Ok(self.ast.export_default_declaration(declaration, exported)) + } + + // export ExportFromClause FromClause ; + // ExportFromClause : + // * + // * as ModuleExportName + // NamedExports + fn parse_export_all_declaration(&mut self) -> Result>> { + self.bump_any(); // bump `star` + let exported = self.eat(Kind::As).then(|| self.parse_module_export_name()).transpose()?; + self.expect(Kind::From)?; + let source = self.parse_literal_string()?; + let assertions = self.parse_import_attributes()?; + self.asi()?; + Ok(self.ast.export_all_declaration(exported, source, assertions)) + } + + // ImportSpecifier : + // ImportedBinding + // ModuleExportName as ImportedBinding + fn parse_import_specifier(&mut self) -> Result { + let specifier_node = self.start_node(); + let peek_kind = self.peek_kind(); + let mut import_kind = ImportOrExportKind::Value; + if self.ts_enabled() && self.at(Kind::Type) { + if self.peek_at(Kind::As) { + if self.nth_at(2, Kind::As) { + if self.nth_kind(3).is_identifier_name() { + import_kind = ImportOrExportKind::Type; + } + } else if !self.nth_kind(2).is_identifier_name() { + import_kind = ImportOrExportKind::Type; + } + } else if peek_kind.is_identifier_name() { + import_kind = ImportOrExportKind::Type; + } + } + + if import_kind == ImportOrExportKind::Type { + self.bump_any(); + } + let (imported, local) = if self.peek_at(Kind::As) { + let imported = self.parse_module_export_name()?; + self.bump(Kind::As); + let local = self.parse_binding_identifier()?; + (imported, local) + } else { + let local = self.parse_binding_identifier()?; + let imported = IdentifierName { node: local.node, name: local.name.clone() }; + (ModuleExportName::Identifier(imported), local) + }; + Ok(ImportSpecifier { node: self.end_node(specifier_node), imported, local }) + } + + // ModuleExportName : + // IdentifierName + // StringLiteral + pub fn parse_module_export_name(&mut self) -> Result { + match self.cur_kind() { + Kind::Str => { + let literal = self.parse_literal_string()?; + // ModuleExportName : StringLiteral + // It is a Syntax Error if IsStringWellFormedUnicode(the SV of StringLiteral) is false. + if !literal.is_string_well_formed_unicode() { + self.error(Diagnostic::ExportLoneSurrogate(literal.node.range())); + }; + Ok(ModuleExportName::StringLiteral(literal)) + } + _ => Ok(ModuleExportName::Identifier(self.parse_identifier_name()?)), + } + } + + fn parse_import_or_export_kind(&mut self) -> Option { + if !self.ts_enabled() { + return None; + } + + // import type { bar } from 'foo'; + // import type * as React from 'react'; + // import type ident from 'foo'; + // export type { bar } from 'foo'; + if matches!(self.peek_kind(), Kind::LCurly | Kind::Star | Kind::Ident) + && self.eat(Kind::Type) + { + return Some(ImportOrExportKind::Type); + } + + Some(ImportOrExportKind::Value) + } +} diff --git a/crates/oxc_parser/src/js/object.rs b/crates/oxc_parser/src/js/object.rs new file mode 100644 index 0000000000000..ae67c20e0011c --- /dev/null +++ b/crates/oxc_parser/src/js/object.rs @@ -0,0 +1,260 @@ +use oxc_allocator::Box; +use oxc_ast::{ast::*, Node}; +use oxc_diagnostics::{Diagnostic, Result}; + +use super::list::ObjectExpressionProperties; +use crate::lexer::Kind; +use crate::list::SeparatedList; +use crate::Parser; + +impl<'a> Parser<'a> { + /// [Object Expression](https://tc39.es/ecma262/#sec-object-initializer) + /// `ObjectLiteral`[Yield, Await] : + /// { } + /// { `PropertyDefinitionList`[?Yield, ?Await] } + /// { `PropertyDefinitionList`[?Yield, ?Await] , } + pub fn parse_object_expression(&mut self) -> Result> { + let node = self.start_node(); + + let has_in = self.ctx.has_in(); + self.ctx = self.ctx.and_in(true); + let object_expression_properties = ObjectExpressionProperties::parse(self)?; + self.ctx = self.ctx.and_in(has_in); + + Ok(self.ast.object_expression( + self.end_node(node), + object_expression_properties.elements, + object_expression_properties.trailing_comma, + )) + } + + /// `PropertyDefinition`[Yield, Await] + pub fn parse_property_definition(&mut self) -> Result>> { + let peek_kind = self.peek_kind(); + let class_element_name = peek_kind.is_class_element_name_start(); + match self.cur_kind() { + // get ClassElementName + Kind::Get if class_element_name => self.parse_method_getter(), + // set ClassElementName + Kind::Set if class_element_name => self.parse_method_setter(), + // AsyncMethod + // AsyncGeneratorMethod + Kind::Async + if (class_element_name || peek_kind == Kind::Star) + && !self.peek_token().is_on_new_line => + { + self.parse_property_definition_method() + } + // GeneratorMethod + Kind::Star if class_element_name => self.parse_property_definition_method(), + // IdentifierReference + kind if kind.is_identifier_reference(false, false) + // test Kind::Dot to ignore ({ foo.bar: baz }) + // see https://stackoverflow.com/questions/30285947/syntaxerror-unexpected-token + && !matches!( + peek_kind, + Kind::LParen | Kind::Colon | Kind::LAngle | Kind::ShiftLeft | Kind::Dot + ) => + { + self.parse_property_definition_shorthand() + } + _ => { + let node = self.start_node(); + let (key, computed) = self.parse_property_name()?; + + if self.at(Kind::Colon) { + return self.parse_property_definition_assignment(node, key, computed); + } + + if matches!(self.cur_kind(), Kind::LParen | Kind::LAngle | Kind::ShiftLeft) { + let method = self.parse_method(false, false)?; + return Ok(self.ast.property( + self.end_node(node), + PropertyKind::Init, + key, + PropertyValue::Expression(self.ast.function_expression(method)), + /* method */ true, + /* shorthand */ false, + /* computed */ computed, + )); + } + + self.unexpected() + } + } + } + + /// `PropertyDefinition`[Yield, Await] : + /// ... `AssignmentExpression`[+In, ?Yield, ?Await] + pub fn parse_spread_element(&mut self) -> Result>> { + let node = self.start_node(); + self.bump_any(); // advance `...` + let argument = self.parse_assignment_expression_base()?; + Ok(self.ast.spread_element(self.end_node(node), argument)) + } + + /// `PropertyDefinition`[Yield, Await] : + /// `IdentifierReference`[?Yield, ?Await] + /// `CoverInitializedName`[?Yield, ?Await] + fn parse_property_definition_shorthand(&mut self) -> Result>> { + let node = self.start_node(); + let identifier = self.parse_identifier_reference()?; + // CoverInitializedName ({ foo = bar }) + let value = if self.eat(Kind::Eq) { + let right = self.parse_assignment_expression_base()?; + let left = AssignmentTarget::SimpleAssignmentTarget( + SimpleAssignmentTarget::AssignmentTargetIdentifier( + self.ast.alloc(identifier.clone()), + ), + ); + self.ast.assignment_expression( + self.end_node(node), + AssignmentOperator::Assign, + left, + right, + ) + } else { + // IdentifierReference ({ foo }) + Expression::Identifier(self.ast.alloc(identifier.clone())) + }; + Ok(self.ast.property( + self.end_node(node), + PropertyKind::Init, + PropertyKey::Identifier(IdentifierName { + node: identifier.node, + name: identifier.name, + }), + PropertyValue::Expression(value), + /* method */ false, + /* shorthand */ true, + /* computed */ false, + )) + } + + /// `PropertyDefinition`[Yield, Await] : + /// `PropertyName`[?Yield, ?Await] : `AssignmentExpression`[+In, ?Yield, ?Await] + fn parse_property_definition_assignment( + &mut self, + node: Node, + key: PropertyKey<'a>, + computed: bool, + ) -> Result>> { + self.bump_any(); // bump `:` + let value = self.parse_assignment_expression_base()?; + Ok(self.ast.property( + self.end_node(node), + PropertyKind::Init, + key, + PropertyValue::Expression(value), + /* method */ false, + /* shorthand */ false, + /* computed */ computed, + )) + } + + /// `PropertyName`[Yield, Await] : + /// `LiteralPropertyName` + /// `ComputedPropertyName`[?Yield, ?Await] + pub fn parse_property_name(&mut self) -> Result<(PropertyKey<'a>, bool)> { + let mut computed = false; + let key = match self.cur_kind() { + Kind::Str => self.parse_literal_expression().map(PropertyKey::Expression)?, + kind if kind.is_number() => { + self.parse_literal_expression().map(PropertyKey::Expression)? + } + // { [foo]() {} } + Kind::LBrack => { + computed = true; + self.parse_computed_property_name().map(PropertyKey::Expression)? + } + _ => PropertyKey::Identifier(self.parse_identifier_name()?), + }; + Ok((key, computed)) + } + + /// `ComputedPropertyName`[Yield, Await] : [ `AssignmentExpression`[+In, ?Yield, ?Await] ] + pub fn parse_computed_property_name(&mut self) -> Result> { + self.bump_any(); // advance `[` + + let has_in = self.ctx.has_in(); + self.ctx = self.ctx.and_in(true); + let expression = self.parse_assignment_expression_base()?; + self.ctx = self.ctx.and_in(has_in); + + self.expect(Kind::RBrack)?; + Ok(expression) + } + + /// `PropertyDefinition`[Yield, Await] : + /// `MethodDefinition`[?Yield, ?Await] + fn parse_property_definition_method(&mut self) -> Result>> { + let node = self.start_node(); + let r#async = self.eat(Kind::Async); + let generator = self.eat(Kind::Star); + let (key, computed) = self.parse_property_name()?; + let method = self.parse_method(r#async, generator)?; + let value = PropertyValue::Expression(self.ast.function_expression(method)); + Ok(self.ast.property( + self.end_node(node), + PropertyKind::Init, + key, + value, + /* method */ true, + /* shorthand */ false, + /* computed */ computed, + )) + } + + /// `MethodDefinition`[Yield, Await] : + /// get `ClassElementName`[?Yield, ?Await] ( ) { `FunctionBody`[~Yield, ~Await] } + fn parse_method_getter(&mut self) -> Result>> { + let node = self.start_node(); + self.expect(Kind::Get)?; + let (key, computed) = self.parse_property_name()?; + let method = self.parse_method(false, false)?; + + if !method.params.is_empty() { + self.error(Diagnostic::GetterParameters(method.params.node.range())); + } + + let value = PropertyValue::Expression(self.ast.function_expression(method)); + Ok(self.ast.property( + self.end_node(node), + PropertyKind::Get, + key, + value, + /* method */ false, + /* shorthand */ false, + /* computed */ computed, + )) + } + + /// `MethodDefinition`[Yield, Await] : + /// set `ClassElementName`[?Yield, ?Await] ( `PropertySetParameterList` ) { `FunctionBody`[~Yield, ~Await] } + fn parse_method_setter(&mut self) -> Result>> { + let node = self.start_node(); + self.expect(Kind::Set)?; + let (key, computed) = self.parse_property_name()?; + let method = self.parse_method(false, false)?; + + if method.params.items.len() != 1 { + self.error(Diagnostic::SetterParameters(method.params.node.range())); + } + + if method.params.items.len() == 1 { + if let BindingPatternKind::RestElement(elem) = &method.params.items[0].pattern.kind { + self.error(Diagnostic::SetterParametersRestPattern(elem.node.range())); + } + } + + Ok(self.ast.property( + self.end_node(node), + PropertyKind::Set, + key, + PropertyValue::Expression(self.ast.function_expression(method)), + /* method */ false, + /* shorthand */ false, + /* computed */ computed, + )) + } +} diff --git a/crates/oxc_parser/src/js/operator.rs b/crates/oxc_parser/src/js/operator.rs new file mode 100644 index 0000000000000..e0b530604f9c6 --- /dev/null +++ b/crates/oxc_parser/src/js/operator.rs @@ -0,0 +1,145 @@ +use oxc_ast::ast::*; + +use crate::lexer::Kind; + +/// Operator Precedence +/// `https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table` +#[derive(Debug, Eq, Ord, PartialOrd, PartialEq, Copy, Clone)] +#[repr(u8)] +pub enum BindingPower { + Comma = 0, + // Yield = 1, + // Assignment = 2, + // Conditional = 3, + Coalesce = 4, + LogicalOr = 5, + LogicalAnd = 6, + BitwiseOr = 7, + BitwiseXor = 8, + BitwiseAnd = 9, + Equality = 10, + Relational = 11, + Shift = 12, + Additive = 13, + Multiplicative = 14, + Exponential = 15, + // Unary = 16, + // Update = 17, + // LeftHandSide = 18, + // Member = 19, + // Primary = 20, +} + +impl BindingPower { + pub const fn lowest() -> Self { + Self::Comma + } + + pub fn is_right_associative(binding_power: Self) -> bool { + binding_power == Self::Exponential + } + + pub const fn value(kind: Kind) -> Option { + match kind { + Kind::Question2 => Some(Self::Coalesce), + Kind::Pipe2 => Some(Self::LogicalOr), + Kind::Amp2 => Some(Self::LogicalAnd), + Kind::Pipe => Some(Self::BitwiseOr), + Kind::Caret => Some(Self::BitwiseXor), + Kind::Amp => Some(Self::BitwiseAnd), + Kind::Eq2 | Kind::Eq3 | Kind::Neq | Kind::Neq2 => Some(Self::Equality), + Kind::LAngle + | Kind::RAngle + | Kind::LtEq + | Kind::GtEq + | Kind::Instanceof + | Kind::In + | Kind::As => Some(Self::Relational), + Kind::ShiftLeft | Kind::ShiftRight | Kind::ShiftRight3 => Some(Self::Shift), + Kind::Plus | Kind::Minus => Some(Self::Additive), + Kind::Star | Kind::Slash | Kind::Percent => Some(Self::Multiplicative), + Kind::Star2 => Some(Self::Exponential), + _ => None, + } + } +} + +pub fn map_binary_operator(kind: Kind) -> BinaryOperator { + match kind { + Kind::Eq2 => BinaryOperator::Equality, + Kind::Neq => BinaryOperator::Inequality, + Kind::Eq3 => BinaryOperator::StrictEquality, + Kind::Neq2 => BinaryOperator::StrictInequality, + Kind::LAngle => BinaryOperator::LessThan, + Kind::LtEq => BinaryOperator::LessEqualThan, + Kind::RAngle => BinaryOperator::GreaterThan, + Kind::GtEq => BinaryOperator::GreaterEqualThan, + Kind::ShiftLeft => BinaryOperator::ShiftLeft, + Kind::ShiftRight => BinaryOperator::ShiftRight, + Kind::ShiftRight3 => BinaryOperator::ShiftRightZeroFill, + Kind::Plus => BinaryOperator::Addition, + Kind::Minus => BinaryOperator::Subtraction, + Kind::Star => BinaryOperator::Multiplication, + Kind::Slash => BinaryOperator::Division, + Kind::Percent => BinaryOperator::Remainder, + Kind::Pipe => BinaryOperator::BitwiseOR, + Kind::Caret => BinaryOperator::BitwiseXOR, + Kind::Amp => BinaryOperator::BitwiseAnd, + Kind::In => BinaryOperator::In, + Kind::Instanceof => BinaryOperator::Instanceof, + Kind::Star2 => BinaryOperator::Exponential, + _ => unreachable!("Binary Operator: {kind:?}"), + } +} + +pub fn map_unary_operator(kind: Kind) -> UnaryOperator { + match kind { + Kind::Minus => UnaryOperator::UnaryNegation, + Kind::Plus => UnaryOperator::UnaryPlus, + Kind::Bang => UnaryOperator::LogicalNot, + Kind::Tilde => UnaryOperator::BitwiseNot, + Kind::Typeof => UnaryOperator::Typeof, + Kind::Void => UnaryOperator::Void, + Kind::Delete => UnaryOperator::Delete, + _ => unreachable!("Unary Operator: {kind:?}"), + } +} + +pub fn map_logical_operator(kind: Kind) -> LogicalOperator { + match kind { + Kind::Pipe2 => LogicalOperator::Or, + Kind::Amp2 => LogicalOperator::And, + Kind::Question2 => LogicalOperator::Coalesce, + _ => unreachable!("Logical Operator: {kind:?}"), + } +} + +pub fn map_update_operator(kind: Kind) -> UpdateOperator { + match kind { + Kind::Plus2 => UpdateOperator::Increment, + Kind::Minus2 => UpdateOperator::Decrement, + _ => unreachable!("Update Operator: {kind:?}"), + } +} + +pub fn map_assignment_operator(kind: Kind) -> AssignmentOperator { + match kind { + Kind::Eq => AssignmentOperator::Assign, + Kind::PlusEq => AssignmentOperator::Addition, + Kind::MinusEq => AssignmentOperator::Subtraction, + Kind::StarEq => AssignmentOperator::Multiplication, + Kind::SlashEq => AssignmentOperator::Division, + Kind::PercentEq => AssignmentOperator::Remainder, + Kind::ShiftLeftEq => AssignmentOperator::ShiftLeft, + Kind::ShiftRightEq => AssignmentOperator::ShiftRight, + Kind::ShiftRight3Eq => AssignmentOperator::ShiftRightZeroFill, + Kind::PipeEq => AssignmentOperator::BitwiseOR, + Kind::CaretEq => AssignmentOperator::BitwiseXOR, + Kind::AmpEq => AssignmentOperator::BitwiseAnd, + Kind::Amp2Eq => AssignmentOperator::LogicalAnd, + Kind::Pipe2Eq => AssignmentOperator::LogicalOr, + Kind::Question2Eq => AssignmentOperator::LogicalNullish, + Kind::Star2Eq => AssignmentOperator::Exponential, + _ => unreachable!("Update Operator: {kind:?}"), + } +} diff --git a/crates/oxc_parser/src/js/statement.rs b/crates/oxc_parser/src/js/statement.rs new file mode 100644 index 0000000000000..e4178a2b543d7 --- /dev/null +++ b/crates/oxc_parser/src/js/statement.rs @@ -0,0 +1,507 @@ +use oxc_allocator::{Box, Vec}; +use oxc_ast::{ + ast::*, + context::{Context, StatementContext}, + Node, +}; +use oxc_diagnostics::{Diagnostic, Result}; + +use super::declaration::{VariableDeclarationContext, VariableDeclarationParent}; +use super::grammar::CoverGrammar; +use super::list::SwitchCases; +use crate::lexer::Kind; +use crate::list::NormalList; +use crate::Parser; + +impl<'a> Parser<'a> { + /// `https://tc39.es/ecma262/#prod-StatementList` + /// `StatementList`[Yield, Await, Return] : + /// `StatementListItem`[?Yield, ?Await, ?Return] + /// `StatementList`[?Yield, ?Await, ?Return] `StatementListItem`[?Yield, ?Await, ?Return] + pub fn parse_directives_and_statements( + &mut self, + is_top_level: bool, + ) -> Result<(Vec<'a, Directive<'a>>, Vec<'a, Statement<'a>>)> { + let mut directives = self.ast.new_vec(); + let mut statements = self.ast.new_vec(); + + let mut expecting_diretives = true; + while !self.at(Kind::Eof) { + match self.cur_kind() { + Kind::RCurly if !is_top_level => break, + Kind::Import if !matches!(self.peek_kind(), Kind::Dot | Kind::LParen) => { + let stmt = self.parse_import_declaration()?; + statements.push(stmt); + } + Kind::Export => { + let stmt = self.parse_export_declaration()?; + statements.push(stmt); + } + Kind::At => { + self.eat_decorators()?; + continue; + } + _ => { + let stmt = self.parse_statement_list_item(StatementContext::StatementList)?; + + // Section 11.2.1 Directive Prologue + // The only way to get a correct directive is to parse the statement first and check if it is a string literal. + // All other method are flawed, see test cases in [babel](https://github.com/babel/babel/blob/main/packages/babel-parser/test/fixtures/core/categorized/not-directive/input.js) + if expecting_diretives { + if let Statement::ExpressionStatement(expr) = &stmt { + if let Expression::StringLiteral(string) = &expr.expression { + let src = &self.source[string.node.start + 1..string.node.end - 1]; + let directive = + self.ast.directive(expr.node, (*string).clone(), src); + directives.push(directive); + continue; + } + } + expecting_diretives = false; + } + + statements.push(stmt); + } + }; + } + + Ok((directives, statements)) + } + + /// `StatementListItem`[Yield, Await, Return] : + /// Statement[?Yield, ?Await, ?Return] + /// Declaration[?Yield, ?Await] + pub fn parse_statement_list_item( + &mut self, + stmt_ctx: StatementContext, + ) -> Result> { + let node = self.start_node(); + if self.ts_enabled() { + if self.at(Kind::Type) + && self.peek_kind().is_identifier_name() + && !self.peek_token().is_on_new_line + { + return self + .parse_ts_type_alias_declaration(false, node) + .map(Statement::Declaration); + } + if self.at(Kind::Abstract) && !self.peek_token().is_on_new_line { + return self.parse_class_statement(stmt_ctx); + } + if self.is_at_ts_declaration_clause() { + let peek_kind = self.peek_kind(); + // ignore "declare `${expr}" and "declare `template`" + if peek_kind != Kind::TemplateHead && peek_kind != Kind::NoSubstitutionTemplate { + return self.parse_ts_declare_statement(); + } + } + + if self.is_nth_at_ts_namespace_declaration(0) { + if self.at(Kind::Namespace) || self.at(Kind::Module) { + return self.parse_ts_namespace_or_module_statement(/* declare */ false); + } + if self.at(Kind::Global) { + return self.parse_ts_global_statement(); + } + } + match self.cur_kind() { + Kind::Const | Kind::Enum if self.is_at_enum_declaration() => { + return self.parse_ts_enum_declaration(false, node).map(Statement::Declaration); + } + Kind::Interface if self.is_at_interface_declaration() => { + return self + .parse_ts_interface_declaration(false, node) + .map(Statement::Declaration); + } + _ => (), + } + } + + match self.cur_kind() { + Kind::LCurly => self.parse_block_statement(), + Kind::Semicolon => Ok(self.parse_empty_statement()), + Kind::If => self.parse_if_statement(), + Kind::Do => self.parse_do_while_statement(), + Kind::While => self.parse_while_statement(), + Kind::For => self.parse_for_statement(), + Kind::Break | Kind::Continue => self.parse_break_or_continue_statement(), + Kind::With => self.parse_with_statement(), + Kind::Switch => self.parse_switch_statement(), + Kind::Throw => self.parse_throw_statement(), + Kind::Try => self.parse_try_statement(), + Kind::Debugger => self.parse_debugger_statement(), + Kind::Class => self.parse_class_statement(stmt_ctx), + Kind::Import if !matches!(self.peek_kind(), Kind::Dot | Kind::LParen) => { + self.parse_import_declaration() + } + Kind::Export => self.parse_export_declaration(), + // [+Return] ReturnStatement[?Yield, ?Await] + // Error is checked in linter + Kind::Return => self.parse_return_statement(), + Kind::Var | Kind::Const => self.parse_variable_statement(stmt_ctx), + Kind::Let if !self.cur_token().escaped => self.parse_let(stmt_ctx), + Kind::At => { + self.eat_decorators()?; + self.parse_statement_list_item(stmt_ctx) + } + _ if self.at_function_with_async() => self.parse_function_declaration(stmt_ctx), + _ => self.parse_expression_or_labeled_statment(), + } + } + + fn parse_expression_or_labeled_statment(&mut self) -> Result> { + let node = self.start_node(); + let expr = self.parse_expression()?; + if let Expression::Identifier(ident) = &expr { + // Section 14.13 Labelled Statement + // Avoids lookahead for a labeled statement, which is on a hot path + if self.eat(Kind::Colon) { + let label = LabelIdentifier { node: ident.node, name: ident.name.clone() }; + let body = self.parse_statement_list_item(StatementContext::Label)?; + return Ok(self.ast.labeled_statement(self.end_node(node), label, body)); + } + } + self.parse_expression_statement(node, expr) + } + + /// Section 14.2 Block Statement + pub fn parse_block(&mut self) -> Result>> { + let node = self.start_node(); + self.expect(Kind::LCurly)?; + let mut body = self.ast.new_vec(); + while !self.at(Kind::RCurly) && !self.at(Kind::Eof) { + let stmt = self.parse_statement_list_item(StatementContext::StatementList)?; + body.push(stmt); + } + self.expect(Kind::RCurly)?; + Ok(self.ast.block(self.end_node(node), body)) + } + + pub fn parse_block_statement(&mut self) -> Result> { + let block = self.parse_block()?; + Ok(self.ast.block_statement(block)) + } + + /// Section 14.3.2 Variable Statement + pub fn parse_variable_statement( + &mut self, + stmt_ctx: StatementContext, + ) -> Result> { + let decl = self.parse_variable_declaration(VariableDeclarationContext::new( + VariableDeclarationParent::Statement, + ))?; + + if stmt_ctx.is_single_statement() && decl.kind.is_lexical() { + self.error(Diagnostic::LexicalDeclarationSingleStatement(decl.node.range())); + } + + Ok(Statement::Declaration(Declaration::VariableDeclaration(decl))) + } + + /// Section 14.4 Empty Statement + fn parse_empty_statement(&mut self) -> Statement<'a> { + let node = self.start_node(); + self.bump_any(); // bump `;` + self.ast.empty_statement(self.end_node(node)) + } + + /// Section 14.5 Expression Statement + pub fn parse_expression_statement( + &mut self, + node: Node, + expression: Expression<'a>, + ) -> Result> { + self.asi()?; + Ok(self.ast.expression_statement(self.end_node(node), expression)) + } + + /// Section 14.6 If Statement + fn parse_if_statement(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); // bump `if` + let test = self.parse_paren_expression()?; + let consequent = self.parse_statement_list_item(StatementContext::If)?; + let alternate = self + .eat(Kind::Else) + .then(|| self.parse_statement_list_item(StatementContext::If)) + .transpose()?; + Ok(self.ast.if_statement(self.end_node(node), test, consequent, alternate)) + } + + /// Section 14.7.2 Do-While Statement + fn parse_do_while_statement(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); // advance `do` + let body = self.parse_statement_list_item(StatementContext::Do)?; + self.expect(Kind::While)?; + let test = self.parse_paren_expression()?; + self.bump(Kind::Semicolon); + Ok(self.ast.do_while_statement(self.end_node(node), body, test)) + } + + /// Section 14.7.3 While Statement + fn parse_while_statement(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); // bump `while` + let test = self.parse_paren_expression()?; + let body = self.parse_statement_list_item(StatementContext::While)?; + Ok(self.ast.while_statement(self.end_node(node), test, body)) + } + + /// Section 14.7.4 For Statement + fn parse_for_statement(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); // bump `for` + + // [+Await] + let r#await = self.ctx.has_await() && self.eat(Kind::Await); + + self.expect(Kind::LParen)?; + + // for (;.. + if self.at(Kind::Semicolon) { + return self.parse_for_loop(node, None, r#await); + } + + // for (let | for (const | for (var + // disallow for (let in ..) + if self.at(Kind::Const) + || self.at(Kind::Var) + || (self.at(Kind::Let) && self.peek_kind().is_after_let()) + { + let init_declaration = self.without_context(Context::In, |p| { + p.parse_variable_declaration(VariableDeclarationContext::new( + VariableDeclarationParent::For, + )) + })?; + + let kind = self.cur_kind(); + + // for (.. a in) for (.. a of) + if matches!(kind, Kind::In | Kind::Of) { + let init = ForStatementLeft::VariableDeclaration(init_declaration); + return self.parse_for_in_or_of_loop(node, r#await, init); + } + + let init = Some(ForStatementInit::VariableDeclaration(init_declaration)); + return self.parse_for_loop(node, init, r#await); + } + + let is_let_of = self.at(Kind::Let) && self.peek_at(Kind::Of); + let is_async_of = + self.at(Kind::Async) && !self.cur_token().escaped && self.peek_at(Kind::Of); + let expression_node = self.start_node(); + + let init_expression = self.without_context(Context::In, Parser::parse_expression)?; + + // for (a.b in ...), for ([a] in ..), for ({a} in ..) + if self.at(Kind::In) || self.at(Kind::Of) { + let target = AssignmentTarget::cover(init_expression, self) + .map_err(|_| Diagnostic::UnexpectedToken(self.end_node(expression_node).range()))?; + let for_stmt_left = ForStatementLeft::AssignmentTarget(target); + + if !r#await && is_async_of { + self.error(Diagnostic::ForLoopAsyncOf(self.end_node(expression_node).range())); + } + + if is_let_of { + self.error(Diagnostic::UnexpectedKeyword( + "let", + self.end_node(expression_node).range(), + )); + } + + return self.parse_for_in_or_of_loop(node, r#await, for_stmt_left); + } + + self.parse_for_loop(node, Some(ForStatementInit::Expression(init_expression)), r#await) + } + + fn parse_for_loop( + &mut self, + node: Node, + init: Option>, + r#await: bool, + ) -> Result> { + self.expect(Kind::Semicolon)?; + let test = (!self.at(Kind::Semicolon)).then(|| self.parse_expression()).transpose()?; + self.expect(Kind::Semicolon)?; + let update = (!self.at(Kind::RParen)).then(|| self.parse_expression()).transpose()?; + self.expect(Kind::RParen)?; + + if r#await { + self.error(Diagnostic::ForAwait(self.end_node(node).range())); + } + + let body = self.parse_statement_list_item(StatementContext::For)?; + + Ok(self.ast.for_statement(self.end_node(node), init, test, update, body)) + } + + fn parse_for_in_or_of_loop( + &mut self, + node: Node, + r#await: bool, + left: ForStatementLeft<'a>, + ) -> Result> { + let is_for_in = self.at(Kind::In); + self.bump_any(); // bump `in` or `of` + let right = if is_for_in { + self.parse_expression() + } else { + self.parse_assignment_expression_base() + }?; + self.expect(Kind::RParen)?; + + if r#await && is_for_in { + self.error(Diagnostic::ForAwait(self.end_node(node).range())); + } + + let body = self.parse_statement_list_item(StatementContext::For)?; + let node = self.end_node(node); + + if is_for_in { + Ok(self.ast.for_in_statement(node, left, right, body)) + } else { + Ok(self.ast.for_of_statement(node, r#await, left, right, body)) + } + } + + /// Section 14.8 Continue Statement + /// Section 14.9 Break Statement + fn parse_break_or_continue_statement(&mut self) -> Result> { + let node = self.start_node(); + let kind = self.cur_kind(); + self.bump_any(); // bump `break` or `continue` + let label = + if self.can_insert_semicolon() { None } else { Some(self.parse_label_identifier()?) }; + self.asi()?; + let end_node = self.end_node(node); + match kind { + Kind::Break => Ok(self.ast.break_statement(end_node, label)), + Kind::Continue => Ok(self.ast.continue_statement(end_node, label)), + _ => unreachable!(), + } + } + + /// Section 14.10 Return Statement + /// `ReturnStatement`[Yield, Await] : + /// return ; + /// return [no `LineTerminator` here] Expression[+In, ?Yield, ?Await] ; + fn parse_return_statement(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); // advance `return` + let argument = if self.eat(Kind::Semicolon) || self.can_insert_semicolon() { + None + } else { + let expr = self.with_context(Context::In, Parser::parse_expression)?; + self.asi()?; + Some(expr) + }; + Ok(self.ast.return_statement(self.end_node(node), argument)) + } + + /// Section 14.11 With Statement + fn parse_with_statement(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); // bump `with` + let object = self.parse_paren_expression()?; + let body = self.parse_statement_list_item(StatementContext::With)?; + let node = self.end_node(node); + Ok(self.ast.with_statement(node, object, body)) + } + + /// Section 14.12 Switch Statement + fn parse_switch_statement(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); // advance `switch` + let discriminant = self.parse_paren_expression()?; + let cases = { + let mut switch_cases = SwitchCases::new(self); + switch_cases.parse(self)?; + switch_cases.elements + }; + Ok(self.ast.switch_statement(self.end_node(node), discriminant, cases)) + } + + pub fn parse_switch_case(&mut self) -> Result> { + let node = self.start_node(); + let test = match self.cur_kind() { + Kind::Default => { + self.bump_any(); + None + } + Kind::Case => { + self.bump_any(); + let expression = self.parse_expression()?; + Some(expression) + } + _ => return self.unexpected(), + }; + self.expect(Kind::Colon)?; + let mut consequent = self.ast.new_vec(); + while !matches!(self.cur_kind(), Kind::Case | Kind::Default | Kind::RCurly | Kind::Eof) { + let stmt = self.parse_statement_list_item(StatementContext::StatementList)?; + consequent.push(stmt); + } + Ok(self.ast.switch_case(self.end_node(node), test, consequent)) + } + + /// Section 14.14 Throw Statement + fn parse_throw_statement(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); // advance `throw` + if self.cur_token().is_on_new_line { + self.error(Diagnostic::IllegalNewline( + "throw", + self.end_node(node).range(), + self.cur_token().range(), + )); + } + let argument = self.parse_expression()?; + self.asi()?; + Ok(self.ast.throw_statement(self.end_node(node), argument)) + } + + /// Section 14.15 Try Statement + fn parse_try_statement(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); // bump `try` + + let block = self.parse_block()?; + + let handler = self.at(Kind::Catch).then(|| self.parse_catch_clause()).transpose()?; + + let finalizer = self.eat(Kind::Finally).then(|| self.parse_block()).transpose()?; + + #[allow(clippy::range_plus_one)] + let range = self.prev_token_end..self.prev_token_end + 1; + if handler.is_none() && finalizer.is_none() { + self.error(Diagnostic::ExpectCatchFinally(range)); + } + + Ok(self.ast.try_statement(self.end_node(node), block, handler, finalizer)) + } + + fn parse_catch_clause(&mut self) -> Result>> { + let node = self.start_node(); + self.bump_any(); // advance `catch` + let param = if self.eat(Kind::LParen) { + let pattern = self.parse_binding_pattern()?.0; + self.expect(Kind::RParen)?; + Some(pattern) + } else { + None + }; + let body = self.parse_block()?; + Ok(self.ast.catch_clause(self.end_node(node), param, body)) + } + + /// Section 14.16 Debugger Statement + fn parse_debugger_statement(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); + self.asi()?; + Ok(self.ast.debugger_statement(self.end_node(node))) + } +} diff --git a/crates/oxc_parser/src/jsx/mod.rs b/crates/oxc_parser/src/jsx/mod.rs new file mode 100644 index 0000000000000..cac8522a3cfec --- /dev/null +++ b/crates/oxc_parser/src/jsx/mod.rs @@ -0,0 +1,363 @@ +//! [JSX](https://facebook.github.io/jsx) + +#![allow(clippy::missing_errors_doc)] + +use oxc_allocator::{Box, Vec}; +use oxc_ast::{ast::*, Node}; +use oxc_diagnostics::Result; + +use crate::lexer::Kind; +use crate::Context; +use crate::Parser; + +impl<'a> Parser<'a> { + pub fn parse_jsx_expression(&mut self) -> Result> { + if self.peek_at(Kind::RAngle) { + self.parse_jsx_fragment().map(Expression::JSXFragment) + } else { + self.parse_jsx_element(false).map(Expression::JSXElement) + } + } + + /// `JSXFragment` : + /// < > `JSXChildren_opt` < / > + fn parse_jsx_fragment(&mut self) -> Result>> { + let node = self.start_node(); + let opening_fragment = self.parse_jsx_opening_fragment(node)?; + let children = self.parse_jsx_children()?; + let closing_fragment = self.parse_jsx_closing_fragment()?; + Ok(self.ast.jsx_fragment(self.end_node(node), opening_fragment, closing_fragment, children)) + } + + /// <> + fn parse_jsx_opening_fragment(&mut self, node: Node) -> Result { + self.expect(Kind::LAngle)?; + self.expect_jsx_child(Kind::RAngle)?; + Ok(self.ast.jsx_opening_fragment(self.end_node(node))) + } + + /// + fn parse_jsx_closing_fragment(&mut self) -> Result { + let node = self.start_node(); + self.expect(Kind::LAngle)?; + self.expect(Kind::Slash)?; + self.expect(Kind::RAngle)?; + Ok(self.ast.jsx_closing_fragment(self.end_node(node))) + } + + /// `JSXElement` : + /// `JSXSelfClosingElement` + /// `JSXOpeningElement` `JSXChildren_opt` `JSXClosingElement` + /// `in_jsx_child`: + /// used for telling `JSXClosingElement` to parse the next jsx child or not + /// true when inside jsx element, false when at top level expression + fn parse_jsx_element(&mut self, in_jsx_child: bool) -> Result>> { + let node = self.start_node(); + let opening_element = self.parse_jsx_opening_element(node, in_jsx_child)?; + let children = if opening_element.self_closing { + self.ast.new_vec() + } else { + self.parse_jsx_children()? + }; + let closing_element = (!opening_element.self_closing) + .then(|| self.parse_jsx_closing_element(in_jsx_child)) + .transpose()?; + Ok(self.ast.jsx_element(self.end_node(node), opening_element, closing_element, children)) + } + + /// `JSXOpeningElement` : + /// < `JSXElementName` `JSXAttributes_opt` > + fn parse_jsx_opening_element( + &mut self, + node: Node, + in_jsx_child: bool, + ) -> Result>> { + self.expect(Kind::LAngle)?; + let name = self.parse_jsx_element_name()?; + // for tsx + let type_parameters = if self.ts_enabled() { + let ctx = self.ctx; + self.ctx = Context::default(); + let args = self.parse_ts_type_arguments()?; + self.ctx = ctx; + args + } else { + None + }; + let attributes = self.parse_jsx_attributes()?; + let self_closing = self.eat(Kind::Slash); + if !self_closing || in_jsx_child { + self.expect_jsx_child(Kind::RAngle)?; + } else { + self.expect(Kind::RAngle)?; + } + Ok(self.ast.jsx_opening_element( + self.end_node(node), + self_closing, + name, + attributes, + type_parameters, + )) + } + + fn parse_jsx_closing_element( + &mut self, + in_jsx_child: bool, + ) -> Result>> { + let node = self.start_node(); + self.expect(Kind::LAngle)?; + self.expect(Kind::Slash)?; + let name = self.parse_jsx_element_name()?; + if in_jsx_child { + self.expect_jsx_child(Kind::RAngle)?; + } else { + self.expect(Kind::RAngle)?; + } + Ok(self.ast.jsx_closing_element(self.end_node(node), name)) + } + + /// `JSXElementName` : + /// `JSXIdentifier` + /// `JSXNamespacedName` + /// `JSXMemberExpression` + fn parse_jsx_element_name(&mut self) -> Result> { + let node = self.start_node(); + let identifier = self.parse_jsx_identifier()?; + + // + if self.eat(Kind::Colon) { + let property = self.parse_jsx_identifier()?; + return Ok(JSXElementName::NamespacedName(self.ast.jsx_namespaced_name( + self.end_node(node), + identifier, + property, + ))); + } + + // + if self.at(Kind::Dot) { + return self + .parse_jsx_member_expression(node, identifier) + .map(JSXElementName::MemberExpression); + } + + Ok(JSXElementName::Identifier(identifier)) + } + + /// `JSXMemberExpression` : + /// `JSXIdentifier` . `JSXIdentifier` + /// `JSXMemberExpression` . `JSXIdentifier` + fn parse_jsx_member_expression( + &mut self, + node: Node, + object: JSXIdentifier, + ) -> Result>> { + let mut node = node; + let mut object = JSXMemberExpressionObject::Identifier(object); + let mut property = None; + + while self.eat(Kind::Dot) && !self.at(Kind::Eof) { + // + if let Some(prop) = property { + let obj = self.ast.jsx_member_expression(node, object, prop); + object = JSXMemberExpressionObject::MemberExpression(obj); + } + + // + property = Some(self.parse_jsx_identifier()?); + node = self.end_node(node); + } + + if let Some(property) = property { + return Ok(self.ast.jsx_member_expression(self.end_node(node), object, property)); + } + + self.unexpected() + } + + /// `JSXChildren` : + /// `JSXChild` `JSXChildren_opt` + fn parse_jsx_children(&mut self) -> Result>> { + let mut children = self.ast.new_vec(); + while !self.at(Kind::Eof) { + if let Some(child) = self.parse_jsx_child()? { + children.push(child); + } else { + break; + } + } + Ok(children) + } + + /// `JSXChild` : + /// `JSXText` + /// `JSXElement` + /// `JSXFragment` + /// { `JSXChildExpression_opt` } + fn parse_jsx_child(&mut self) -> Result>> { + match self.cur_kind() { + // Ok(None), + // <> open fragment + Kind::LAngle if self.peek_at(Kind::RAngle) => { + self.parse_jsx_fragment().map(JSXChild::Fragment).map(Some) + } + // { + self.parse_jsx_element(true).map(JSXChild::Element).map(Some) + } + // {...expr} + Kind::LCurly if self.peek_at(Kind::Dot3) => { + self.parse_jsx_spread_child().map(JSXChild::Spread).map(Some) + } + // {expr} + Kind::LCurly => self + .parse_jsx_expression_container(true) + .map(JSXChild::ExpressionContainer) + .map(Some), + // text + Kind::JSXText => Ok(Some(JSXChild::Text(self.parse_jsx_text()))), + _ => self.unexpected(), + } + } + + /// { `JSXChildExpression_opt` } + fn parse_jsx_expression_container( + &mut self, + in_jsx_child: bool, + ) -> Result> { + let node = self.start_node(); + self.bump_any(); // bump `{` + let expr = match self.cur_kind() { + // {} empty + Kind::RCurly => { + let node = self.start_node(); + JSXExpression::EmptyExpression(self.ast.jsx_empty_expression(self.end_node(node))) + } + // {expr} + _ => self.parse_jsx_assignment_expression().map(JSXExpression::Expression)?, + }; + if in_jsx_child { + self.expect_jsx_child(Kind::RCurly)?; + } else { + self.expect(Kind::RCurly)?; + } + Ok(self.ast.jsx_expression_container(self.end_node(node), expr)) + } + + fn parse_jsx_assignment_expression(&mut self) -> Result> { + // TODO: check for SequenceExpression and warn + // "JSX expressions may not use the comma operator. Did you meant to write an array?" + let ctx = self.ctx; + self.ctx = Context::default(); + let expr = self.parse_expression(); + self.ctx = ctx; + expr + } + + /// `JSXChildExpression` : + /// { ... `AssignmentExpression` } + fn parse_jsx_spread_child(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); // bump `{` + self.expect(Kind::Dot3)?; + let expr = self.parse_jsx_assignment_expression()?; + self.expect_jsx_child(Kind::RCurly)?; + Ok(self.ast.jsx_spread_child(self.end_node(node), expr)) + } + + /// `JSXAttributes` : + /// `JSXSpreadAttribute` `JSXAttributes_opt` + /// `JSXAttribute` `JSXAttributes_opt` + fn parse_jsx_attributes(&mut self) -> Result>> { + let mut attributes = self.ast.new_vec(); + while !matches!(self.cur_kind(), Kind::Eof | Kind::LAngle | Kind::RAngle | Kind::Slash) { + let attribute = match self.cur_kind() { + Kind::LCurly => { + self.parse_jsx_spread_attribute().map(JSXAttributeItem::SpreadAttribute) + } + _ => self.parse_jsx_attribute().map(JSXAttributeItem::Attribute), + }?; + attributes.push(attribute); + } + Ok(attributes) + } + + /// `JSXAttribute` : + /// `JSXAttributeName` `JSXAttributeInitializer_opt` + fn parse_jsx_attribute(&mut self) -> Result>> { + let node = self.start_node(); + let name = self.parse_jsx_attribute_name()?; + let value = if self.at(Kind::Eq) { + self.expect_jsx_attribute_value(Kind::Eq)?; + Some(self.parse_jsx_attribute_value()?) + } else { + None + }; + Ok(self.ast.jsx_attribute(self.end_node(node), name, value)) + } + + /// `JSXSpreadAttribute` : + /// { ... `AssignmentExpression` } + fn parse_jsx_spread_attribute(&mut self) -> Result>> { + let node = self.start_node(); + self.bump_any(); // bump `{` + self.expect(Kind::Dot3)?; + let argument = self.parse_jsx_assignment_expression()?; + self.expect(Kind::RCurly)?; + Ok(self.ast.jsx_spread_attribute(self.end_node(node), argument)) + } + + /// `JSXAttributeName` : + /// `JSXIdentifier` + /// `JSXNamespacedName` + fn parse_jsx_attribute_name(&mut self) -> Result> { + let node = self.start_node(); + let identifier = self.parse_jsx_identifier()?; + + if self.eat(Kind::Colon) { + let property = self.parse_jsx_identifier()?; + return Ok(JSXAttributeName::NamespacedName(self.ast.jsx_namespaced_name( + self.end_node(node), + identifier, + property, + ))); + } + + Ok(JSXAttributeName::Identifier(identifier)) + } + + fn parse_jsx_attribute_value(&mut self) -> Result> { + match self.cur_kind() { + Kind::Str => self.parse_literal_string().map(JSXAttributeValue::StringLiteral), + Kind::LCurly => { + let expr = self.parse_jsx_expression_container(false)?; + Ok(JSXAttributeValue::ExpressionContainer(expr)) + } + _ => self.unexpected(), + } + } + + /// `JSXIdentifier` : + /// `IdentifierStart` + /// `JSXIdentifier` `IdentifierPart` + /// `JSXIdentifier` [no `WhiteSpace` or Comment here] - + fn parse_jsx_identifier(&mut self) -> Result { + let node = self.start_node(); + if !self.at(Kind::Ident) && !self.cur_kind().is_all_keyword() { + return self.unexpected(); + } + // we are at a valid normal Ident or Keyword, let's keep on lexing for `-` + self.re_lex_jsx_identifier(); + let name = self.cur_atom().unwrap().clone(); + self.bump_any(); + Ok(self.ast.jsx_identifier(self.end_node(node), name)) + } + + fn parse_jsx_text(&mut self) -> JSXText { + let node = self.start_node(); + let value = self.cur_atom().unwrap().clone(); + self.bump_any(); + self.ast.jsx_text(self.end_node(node), value) + } +} diff --git a/crates/oxc_parser/src/lexer/mod.rs b/crates/oxc_parser/src/lexer/mod.rs index 140f8df348c90..f1fc9e500117f 100644 --- a/crates/oxc_parser/src/lexer/mod.rs +++ b/crates/oxc_parser/src/lexer/mod.rs @@ -23,7 +23,7 @@ use oxc_allocator::{Allocator, String}; use oxc_ast::{Atom, SourceType, Span}; use oxc_diagnostics::{Diagnostic, Diagnostics}; use string_builder::AutoCow; -use token::{RegExp, Token, TokenValue}; +pub use token::{RegExp, Token, TokenValue}; #[derive(Debug, Clone)] pub struct LexerCheckpoint<'a> { diff --git a/crates/oxc_parser/src/lib.rs b/crates/oxc_parser/src/lib.rs index abdab9d0825c0..e52e5091d81b3 100644 --- a/crates/oxc_parser/src/lib.rs +++ b/crates/oxc_parser/src/lib.rs @@ -1,3 +1,177 @@ +//! Recursive Descent Parser for ECMAScript and TypeScript + +#![allow(clippy::wildcard_imports)] // allow for use `oxc_ast::ast::*` + +mod cursor; +mod list; +mod state; + +mod js; +mod jsx; +mod ts; + mod lexer; -pub use lexer::Lexer; +use oxc_allocator::Allocator; +use oxc_ast::{ast::Program, context::Context, AstBuilder, Node, SourceType}; +use oxc_diagnostics::{Diagnostic, Diagnostics, Result}; + +use crate::{ + lexer::{Kind, Lexer, Token}, + state::ParserState, +}; + +#[derive(Debug)] +pub struct ParserReturn<'a> { + pub program: Program<'a>, + pub errors: Vec, +} + +pub struct Parser<'a> { + lexer: Lexer<'a>, + + /// SourceType: JavaScript or TypeScript, Script or Module, jsx support? + source_type: SourceType, + + /// Source Code + source: &'a str, + + /// All syntax errors from parser and lexer + /// Note: favor adding to `Diagnostics` instead of raising Err + errors: Diagnostics, + + /// The current parsing token + token: Token, + + /// The end range of the previous token + prev_token_end: usize, + + /// Parser state + state: ParserState<'a>, + + /// Parsing context saved into every AST node + ctx: Context, + + /// Ast builder for creating AST nodes + ast: AstBuilder<'a>, +} + +impl<'a> Parser<'a> { + #[must_use] + pub fn new(allocator: &'a Allocator, source: &'a str, source_type: SourceType) -> Self { + let errors = Diagnostics::default(); + Self { + lexer: Lexer::new(allocator, source, errors.clone(), source_type), + source_type, + source, + errors, + token: Token::default(), + prev_token_end: 0, + state: ParserState::default(), + ctx: source_type.default_context(), + ast: AstBuilder::new(allocator), + } + } + + #[must_use] + pub fn allow_return_outside_function(mut self, allow: bool) -> Self { + self.ctx = self.ctx.and_return(allow); + self + } + + /// Parser main entry point + /// Returns an empty `Program` on unrecoverable error, + /// Recoverable errors are stored inside `errors`. + #[must_use] + pub fn parse(mut self) -> ParserReturn<'a> { + let program = match self.parse_program() { + Ok(program) => program, + Err(error) => { + self.error(self.flow_error().unwrap_or(error)); + let program = self.ast.program( + Node::default(), + self.ast.new_vec(), + self.ast.new_vec(), + self.source_type, + ); + program + } + }; + ParserReturn { program, errors: self.errors.borrow().clone() } + } + + fn parse_program(&mut self) -> Result> { + // initialize cur_token and prev_token by moving onto the first token + self.bump_any(); + + let (directives, statements) = + self.parse_directives_and_statements(/* is_top_level */ true)?; + + let node = Node::new(0, self.source.len(), self.ctx); + Ok(self.ast.program(node, directives, statements, self.source_type)) + } + + /// Check for Flow declaration if the file cannot be parsed. + /// The declaration must be [on the first line before any code](https://flow.org/en/docs/usage/#toc-prepare-your-code-for-flow) + fn flow_error(&self) -> Option { + if self.source_type.is_javascript() + && (self.source.starts_with("// @flow") || self.source.starts_with("/* @flow */")) + { + return Some(Diagnostic::Flow(0..8)); + } + None + } + + /// Return error info at current token + /// Panics + /// * The lexer did not push a diagnostic when `Kind::Undetermined` is returned + fn unexpected(&self) -> Result { + // The lexer should have reported a more meaningful diagnostic + // when it is a undetermined kind. + if self.cur_kind() == Kind::Undetermined { + return Err(self.errors.borrow_mut().pop().unwrap()); + } + Err(Diagnostic::UnexpectedToken(self.current_range())) + } + + /// Push a Syntax Error + fn error(&mut self, error: Diagnostic) { + self.errors.borrow_mut().push(error); + } + + #[must_use] + const fn ts_enabled(&self) -> bool { + self.source_type.is_typescript() + } +} + +#[cfg(test)] +mod test { + + use super::*; + + #[test] + fn smoke_test() { + let allocator = Allocator::default(); + let source_type = SourceType::default(); + let source = ""; + let ret = Parser::new(&allocator, source, source_type).parse(); + assert!(ret.program.is_empty()); + assert!(ret.errors.is_empty()); + } + + #[test] + fn flow_error() { + let allocator = Allocator::default(); + let source_type = SourceType::default(); + let source = "// @flow\nasdf adsf"; + let ret = Parser::new(&allocator, source, source_type).parse(); + assert!(ret.program.is_empty()); + assert_eq!(ret.errors.first().unwrap().to_string(), "Flow is not supported"); + + let source = "/* @flow */\n asdf asdf"; + let ret = Parser::new(&allocator, source, source_type).parse(); + assert!(ret.program.is_empty()); + assert_eq!(ret.errors.first().unwrap().to_string(), "Flow is not supported"); + } +} diff --git a/crates/oxc_parser/src/list.rs b/crates/oxc_parser/src/list.rs new file mode 100644 index 0000000000000..ee4d575f411eb --- /dev/null +++ b/crates/oxc_parser/src/list.rs @@ -0,0 +1,74 @@ +use oxc_diagnostics::Result; + +use crate::{lexer::Kind, Parser}; + +pub trait NormalList<'a> { + /// Open element, e.g.. `{` `[` `(` + fn open(&self) -> Kind; + + /// Close element, e.g.. `}` `]` `)` + fn close(&self) -> Kind; + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()>; + + /// Main entry point, parse the list + fn parse(&mut self, p: &mut Parser<'a>) -> Result<()> { + p.expect(self.open())?; + while !p.at(self.close()) && !p.at(Kind::Eof) { + self.parse_element(p)?; + } + p.expect(self.close())?; + Ok(()) + } +} + +pub trait SeparatedList<'a>: Sized { + fn new(p: &Parser<'a>) -> Self; + + fn parse(p: &mut Parser<'a>) -> Result { + let mut list = Self::new(p); + list.parse_list(p)?; + Ok(list) + } + + /// Open element, e.g.. `{` `[` `(` + fn open(&self) -> Kind; + + /// Close element, e.g.. `}` `]` `)` + fn close(&self) -> Kind; + + /// Seperator element, e.g. `,` + fn seperator(&self) -> Kind { + Kind::Comma + } + + fn start_sequence(&mut self, _p: &mut Parser<'a>) {} + fn finish_sequence(&mut self, _p: &mut Parser<'a>) {} + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()>; + + /// Main entry point, parse the list + fn parse_list(&mut self, p: &mut Parser<'a>) -> Result<()> { + p.expect(self.open())?; + self.start_sequence(p); + + let mut first = true; + + while !p.at(self.close()) && !p.at(Kind::Eof) { + if first { + first = false; + } else { + p.expect(self.seperator())?; + if p.at(self.close()) { + break; + } + } + + self.parse_element(p)?; + } + + self.finish_sequence(p); + p.expect(self.close())?; + Ok(()) + } +} diff --git a/crates/oxc_parser/src/state.rs b/crates/oxc_parser/src/state.rs new file mode 100644 index 0000000000000..b6271ed61fffe --- /dev/null +++ b/crates/oxc_parser/src/state.rs @@ -0,0 +1,17 @@ +use std::collections::HashSet; + +use oxc_allocator::Vec; +use oxc_ast::ast::Decorator; + +#[derive(Default)] +pub struct ParserState<'a> { + pub not_parenthesized_arrow: HashSet, + + pub decorators: Option>>, +} + +impl<'a> ParserState<'a> { + pub fn consume_decorators(&mut self) -> Option>> { + self.decorators.take() + } +} diff --git a/crates/oxc_parser/src/ts/list.rs b/crates/oxc_parser/src/ts/list.rs new file mode 100644 index 0000000000000..8a7f194e88091 --- /dev/null +++ b/crates/oxc_parser/src/ts/list.rs @@ -0,0 +1,161 @@ +use oxc_allocator::{Box, Vec}; +use oxc_ast::ast::*; +use oxc_diagnostics::Result; + +use crate::lexer::Kind; +use crate::list::{NormalList, SeparatedList}; +use crate::Parser; + +pub struct TSEnumMemberList<'a> { + pub members: Vec<'a, TSEnumMember<'a>>, +} + +impl<'a> SeparatedList<'a> for TSEnumMemberList<'a> { + fn new(p: &Parser<'a>) -> Self { + Self { members: p.ast.new_vec() } + } + + fn open(&self) -> Kind { + Kind::LCurly + } + + fn close(&self) -> Kind { + Kind::RCurly + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let element = p.parse_ts_enum_member()?; + self.members.push(element); + Ok(()) + } +} + +pub struct TSTupleElementList<'a> { + pub elements: Vec<'a, TSTupleElement<'a>>, +} + +impl<'a> SeparatedList<'a> for TSTupleElementList<'a> { + fn new(p: &Parser<'a>) -> Self { + Self { elements: p.ast.new_vec() } + } + + fn open(&self) -> Kind { + Kind::LBrack + } + + fn close(&self) -> Kind { + Kind::RBrack + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let node = p.start_node(); + if p.is_at_named_tuple_element() { + let _is_rest = p.eat(Kind::Dot3); + let label = p.parse_identifier_name()?; + let optional = p.eat(Kind::Question); + p.expect(Kind::Colon)?; + + let element_type = p.parse_ts_type()?; + self.elements.push(TSTupleElement::TSNamedTupleMember(p.ast.alloc( + TSNamedTupleMember { node: p.end_node(node), element_type, label, optional }, + ))); + + return Ok(()); + } + + if p.eat(Kind::Dot3) { + let type_annotation = p.parse_ts_type()?; + self.elements.push(TSTupleElement::TSRestType( + p.ast.alloc(TSRestType { node: p.end_node(node), type_annotation }), + )); + return Ok(()); + } + + let type_annotation = p.parse_ts_type()?; + if p.eat(Kind::Question) { + self.elements.push(TSTupleElement::TSOptionalType( + p.ast.alloc(TSOptionalType { node: p.end_node(node), type_annotation }), + )); + } else { + self.elements.push(TSTupleElement::TSType(type_annotation)); + } + + Ok(()) + } +} + +pub struct TSTypeParameterList<'a> { + pub params: Vec<'a, Box<'a, TSTypeParameter<'a>>>, +} + +impl<'a> SeparatedList<'a> for TSTypeParameterList<'a> { + fn new(p: &Parser<'a>) -> Self { + Self { params: p.ast.new_vec() } + } + + fn open(&self) -> Kind { + Kind::LAngle + } + + fn close(&self) -> Kind { + Kind::RAngle + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let param = p.parse_ts_type_parameter()?; + p.re_lex_ts_r_angle(); + self.params.push(param); + Ok(()) + } +} + +pub struct TSInterfaceOrObjectBodyList<'a> { + pub body: Vec<'a, TSSignature<'a>>, +} + +impl<'a> TSInterfaceOrObjectBodyList<'a> { + pub fn new(p: &Parser<'a>) -> Self { + Self { body: p.ast.new_vec() } + } +} + +impl<'a> NormalList<'a> for TSInterfaceOrObjectBodyList<'a> { + fn open(&self) -> Kind { + Kind::LCurly + } + + fn close(&self) -> Kind { + Kind::RCurly + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let property = p.parse_ts_type_signature()?; + self.body.push(property); + Ok(()) + } +} + +pub struct TSTypeArgumentList<'a> { + pub params: Vec<'a, TSType<'a>>, +} + +impl<'a> SeparatedList<'a> for TSTypeArgumentList<'a> { + fn new(p: &Parser<'a>) -> Self { + Self { params: p.ast.new_vec() } + } + + fn open(&self) -> Kind { + Kind::LAngle + } + + fn close(&self) -> Kind { + Kind::RAngle + } + + fn parse_element(&mut self, p: &mut Parser<'a>) -> Result<()> { + let ty = p.parse_ts_type()?; + p.re_lex_ts_r_angle(); + self.params.push(ty); + Ok(()) + } +} diff --git a/crates/oxc_parser/src/ts/mod.rs b/crates/oxc_parser/src/ts/mod.rs new file mode 100644 index 0000000000000..f0019de14fa35 --- /dev/null +++ b/crates/oxc_parser/src/ts/mod.rs @@ -0,0 +1,5 @@ +#![allow(clippy::missing_errors_doc)] + +mod list; +mod statement; +mod types; diff --git a/crates/oxc_parser/src/ts/statement.rs b/crates/oxc_parser/src/ts/statement.rs new file mode 100644 index 0000000000000..cb5097799d10b --- /dev/null +++ b/crates/oxc_parser/src/ts/statement.rs @@ -0,0 +1,512 @@ +use oxc_allocator::Box; +use oxc_ast::{ + ast::*, + context::{Context, StatementContext}, + Node, +}; +use oxc_diagnostics::Result; + +use super::list::{TSEnumMemberList, TSInterfaceOrObjectBodyList}; +use crate::js::declaration::{VariableDeclarationContext, VariableDeclarationParent}; +use crate::js::function::FunctionKind; +use crate::lexer::Kind; +use crate::list::{NormalList, SeparatedList}; +use crate::Parser; + +impl<'a> Parser<'a> { + /** ------------------- Enum ------------------ */ + + pub fn is_at_enum_declaration(&mut self) -> bool { + self.at(Kind::Enum) || (self.at(Kind::Const) && self.peek_at(Kind::Enum)) + } + + /// `https://www.typescriptlang.org/docs/handbook/enums.html` + pub fn parse_ts_enum_declaration( + &mut self, + declare: bool, + node: Node, + ) -> Result> { + let r#const = self.eat(Kind::Const); + self.expect(Kind::Enum)?; + + let id = self.parse_binding_identifier()?; + let members = TSEnumMemberList::parse(self)?.members; + Ok(self.ast.ts_enum_declaration(node, id, members, declare, r#const)) + } + + pub fn parse_ts_enum_member(&mut self) -> Result> { + let node = self.start_node(); + let id = self.parse_ts_enum_member_name()?; + + let initializer = + if self.eat(Kind::Eq) { Some(self.parse_assignment_expression_base()?) } else { None }; + + Ok(TSEnumMember { node: self.end_node(node), id, initializer }) + } + + fn parse_ts_enum_member_name(&mut self) -> Result> { + match self.cur_kind() { + Kind::LBrack => { + Ok(TSEnumMemberName::ComputedPropertyName(self.parse_computed_property_name()?)) + } + Kind::Str => Ok(TSEnumMemberName::StringLiteral(self.parse_literal_string()?)), + kind if kind.is_number() => { + Ok(TSEnumMemberName::NumberLiteral(self.parse_literal_number()?)) + } + _ => Ok(TSEnumMemberName::Identifier(self.parse_identifier_name()?)), + } + } + + /** ------------------- Annotation ----------------- */ + + pub fn parse_ts_type_annotation(&mut self) -> Result>> { + if self.at(Kind::Colon) { + let node = self.start_node(); + self.bump_any(); // bump ':' + let type_annotation = self.parse_ts_type()?; + Ok(Some(self.ast.ts_type_annotation(self.end_node(node), type_annotation))) + } else { + Ok(None) + } + } + + pub fn parse_ts_variable_annotation(&mut self) -> Result<(Option>, bool)> { + if !self.at(Kind::Bang) { + return Ok((self.parse_ts_type_annotation()?, false)); + } + + if self.cur_token().is_on_new_line { + return Ok((None, false)); + } + + let node = self.start_node(); + self.bump(Kind::Bang); + + if self.eat(Kind::Colon) { + let type_annotation = self.parse_ts_type()?; + Ok((Some(self.ast.ts_type_annotation(self.end_node(node), type_annotation)), true)) + } else { + self.unexpected() + } + } + + pub fn parse_ts_type_alias_declaration( + &mut self, + declare: bool, + node: Node, + ) -> Result> { + self.expect(Kind::Type)?; + + let id = self.parse_binding_identifier()?; + let params = self.parse_ts_type_parameters()?; + self.expect(Kind::Eq)?; + + let annotation = self.parse_ts_type()?; + + self.asi()?; + Ok(self.ast.ts_type_alias_declaration(node, id, annotation, params, declare)) + } + + /** --------------------- Interface ------------------------ */ + + pub fn parse_ts_interface_declaration( + &mut self, + declare: bool, + node: Node, + ) -> Result> { + self.expect(Kind::Interface)?; // bump interface + let id = self.parse_binding_identifier()?; + let type_parameters = self.parse_ts_type_parameters()?; + let (extends, _) = self.parse_heritage_clause()?; + let body = self.parse_ts_interface_body()?; + let extends = extends.map(|e| self.ast.ts_interface_heritages(e)); + Ok(self.ast.ts_interface_declaration( + self.end_node(node), + id, + body, + type_parameters, + extends, + declare, + )) + } + + fn parse_ts_interface_body(&mut self) -> Result>> { + let node = self.start_node(); + let mut body_list = TSInterfaceOrObjectBodyList::new(self); + body_list.parse(self)?; + Ok(self.ast.ts_interface_body(self.end_node(node), body_list.body)) + } + + pub fn is_at_interface_declaration(&mut self) -> bool { + if !self.at(Kind::Interface) || self.peek_token().is_on_new_line { + false + } else { + self.peek_token().kind.is_binding_identifier() || self.peek_at(Kind::LCurly) + } + } + + pub fn parse_ts_type_signature(&mut self) -> Result> { + if self.is_at_ts_index_signature_member() { + return self.parse_ts_index_signature_member(); + } + + match self.cur_kind() { + Kind::LParen | Kind::LAngle => self.parse_ts_call_signature_member(), + Kind::New if self.peek_at(Kind::LParen) || self.peek_at(Kind::LAngle) => { + self.parse_ts_constructor_signature_member() + } + Kind::Get if self.is_next_at_type_member_name() => { + self.parse_ts_getter_signature_member() + } + Kind::Set if self.is_next_at_type_member_name() => { + self.parse_ts_setter_signature_member() + } + _ => self.parse_ts_property_or_method_signature_member(), + } + } + + /// Must be at `[ident:` or ` [ident:` + pub fn is_at_ts_index_signature_member(&mut self) -> bool { + let mut offset = 0; + while self.is_nth_at_modifier(offset, false) { + offset += 1; + } + + if !self.nth_at(offset, Kind::LBrack) { + return false; + } + + if !self.nth_kind(offset + 1).is_identifier() { + return false; + } + + self.nth_at(offset + 2, Kind::Colon) + } + + pub fn is_nth_at_modifier(&mut self, n: usize, is_constructor_parameter: bool) -> bool { + let nth = self.nth(n); + if !(matches!( + nth.kind, + Kind::Public + | Kind::Protected + | Kind::Private + | Kind::Static + | Kind::Abstract + | Kind::Readonly + | Kind::Declare + | Kind::Override + )) { + return false; + } + + let next = self.nth(n + 1); + + if next.is_on_new_line { + false + } else { + let followed_by_any_member = + matches!(next.kind, Kind::PrivateIdentifier | Kind::LBrack) + || next.kind.is_literal_property_name(); + let followed_by_class_member = !is_constructor_parameter && next.kind == Kind::Star; + // allow `...` for error recovery + let followed_by_parameter = is_constructor_parameter + && matches!(next.kind, Kind::LCurly | Kind::LBrack | Kind::Dot3); + + followed_by_any_member || followed_by_class_member || followed_by_parameter + } + } + + /** ----------------------- Namespace & Module ----------------------- */ + + fn parse_ts_module_block(&mut self) -> Result>> { + let node = self.start_node(); + + let mut statements = self.ast.new_vec(); + + if self.at(Kind::LCurly) { + self.expect(Kind::LCurly)?; + + while !self.eat(Kind::RCurly) && !self.at(Kind::Eof) { + let stmt = self.parse_ts_module_item()?; + statements.push(stmt); + } + } + + Ok(self.ast.ts_module_block(self.end_node(node), statements)) + } + + fn parse_ts_module_item(&mut self) -> Result> { + match self.cur_kind() { + Kind::Import if !matches!(self.peek_kind(), Kind::Dot | Kind::LParen) => { + self.parse_import_declaration() + } + Kind::Export => self.parse_export_declaration(), + Kind::At => { + self.eat_decorators()?; + self.parse_ts_module_item() + } + _ => self.parse_statement_list_item(StatementContext::StatementList), + } + } + + pub fn parse_ts_namespace_or_module_declaration_body( + &mut self, + node: Node, + declare: bool, + ) -> Result>> { + let id = match self.cur_kind() { + Kind::Str => self.parse_literal_string().map(TSModuleDeclarationName::StringLiteral), + _ => self.parse_identifier_name().map(TSModuleDeclarationName::Identifier), + }?; + + let body = if self.eat(Kind::Dot) { + let node = self.start_node(); + let decl = self.parse_ts_namespace_or_module_declaration_body(node, false)?; + TSModuleDeclarationBody::TSModuleDeclaration(decl) + } else { + let block = self.parse_ts_module_block()?; + self.asi()?; + TSModuleDeclarationBody::TSModuleBlock(block) + }; + + Ok(self.ast.ts_module_declaration(self.end_node(node), id, body, declare)) + } + + pub fn parse_ts_namespace_or_module_declaration( + &mut self, + declare: bool, + ) -> Result>> { + let node = self.start_node(); + self.expect(Kind::Namespace).or_else(|_| self.expect(Kind::Module))?; + self.parse_ts_namespace_or_module_declaration_body(node, declare) + } + + pub fn parse_ts_global_declaration(&mut self) -> Result>> { + let node = self.start_node(); + self.parse_ts_namespace_or_module_declaration_body(node, false) + } + + pub fn parse_ts_namespace_or_module_statement( + &mut self, + declare: bool, + ) -> Result> { + self.parse_ts_namespace_or_module_declaration(declare) + .map(|decl| Statement::Declaration(Declaration::TSModuleDeclaration(decl))) + } + + pub fn parse_ts_global_statement(&mut self) -> Result> { + self.parse_ts_global_declaration() + .map(|decl| Statement::Declaration(Declaration::TSModuleDeclaration(decl))) + } + + pub fn is_nth_at_ts_namespace_declaration(&mut self, n: usize) -> bool { + if self.nth(n + 1).is_on_new_line { + return false; + } + + if self.nth_at(n, Kind::Module) || self.nth_at(n, Kind::Namespace) { + return self.nth_kind(n + 1).is_identifier() || self.nth_at(n + 1, Kind::Str); + } + + if self.nth_at(n, Kind::Global) { + return self.nth_at(n + 1, Kind::LCurly); + } + + false + } + + /** ----------------------- declaration --------------------- */ + + pub fn is_at_ts_declaration_clause(&mut self) -> bool { + if !self.at(Kind::Declare) || self.peek_token().is_on_new_line { + return false; + } + + if matches!( + self.peek_kind(), + Kind::Function | Kind::Const | Kind::Enum | Kind::Class | Kind::Import + ) { + return true; + } + + if self.peek_kind().is_variable_declaration() { + return true; + } + + if self.nth(2).is_on_new_line { + return false; + } + + if self.peek_at(Kind::Type) || self.peek_at(Kind::Interface) { + return true; + } + + if self.peek_at(Kind::Async) && self.nth_at(2, Kind::Function) { + return true; + } + + if self.is_nth_at_ts_namespace_declaration(1) { + return true; + } + + if self.peek_at(Kind::Abstract) && self.nth_at(2, Kind::Class) { + return true; + } + + false + } + + pub fn parse_ts_declare_statement(&mut self) -> Result> { + let declaration = self.parse_declaration_clause()?; + Ok(Statement::Declaration(declaration)) + } + + pub fn parse_declaration_clause(&mut self) -> Result> { + let has_ambient = self.ctx.has_ambient(); + let declare = self.eat(Kind::Declare); + if declare { + self.ctx = self.ctx.and_ambient(true); + } + + let start_node = self.start_node(); + + let result = match self.cur_kind() { + Kind::Namespace | Kind::Module => self + .parse_ts_namespace_or_module_declaration(declare) + .map(Declaration::TSModuleDeclaration), + Kind::Global => { + let decl = if self.peek_at(Kind::LCurly) { + // valid syntax for + // declare global { } + self.parse_ts_namespace_or_module_declaration_body(start_node, declare) + } else { + self.parse_ts_global_declaration() + }?; + Ok(Declaration::TSModuleDeclaration(decl)) + } + Kind::Type => self.parse_ts_type_alias_declaration(declare, start_node), + Kind::Const | Kind::Enum if self.is_at_enum_declaration() => { + self.parse_ts_enum_declaration(declare, start_node) + } + Kind::Interface if self.is_at_interface_declaration() => { + self.parse_ts_interface_declaration(declare, start_node) + } + Kind::Class | Kind::Abstract => { + self.parse_class_declaration(declare).map(Declaration::ClassDeclaration) + } + Kind::Import => { + self.bump_any(); + self.parse_ts_import_equals_declaration(start_node, true) + } + kind if kind.is_variable_declaration() => self + .parse_variable_declaration(VariableDeclarationContext::new( + VariableDeclarationParent::Clause, + )) + .map(Declaration::VariableDeclaration), + _ if self.at_function_with_async() => { + if declare { + self.parse_ts_declare_function().map(Declaration::FunctionDeclaration) + } else { + self.parse_function_impl(FunctionKind::Declaration { single_statement: true }) + .map(Declaration::FunctionDeclaration) + } + } + _ => self.unexpected(), + }; + + self.ctx = self.ctx.and_ambient(has_ambient); + result + } + + pub fn parse_ts_declare_function(&mut self) -> Result>> { + let node = self.start_node(); + let r#async = self.eat(Kind::Async); + self.expect(Kind::Function)?; + let func_kind = FunctionKind::TSDeclaration; + let id = self.parse_function_id(func_kind, r#async, false); + self.parse_function(node, id, r#async, false, func_kind) + } + + pub fn parse_ts_type_assertion(&mut self) -> Result> { + let node = self.start_node(); + self.re_lex_ts_l_angle(); + self.expect(Kind::LAngle)?; + let type_annotation = self.parse_ts_type()?; + self.expect(Kind::RAngle)?; + let lhs_node = self.start_node(); + let expression = self.parse_unary_expression_base(lhs_node)?; + Ok(self.ast.ts_type_assertion(self.end_node(node), type_annotation, expression)) + } + + pub fn parse_ts_import_equals_declaration( + &mut self, + node: Node, + is_export: bool, + ) -> Result> { + let import_kind = if !self.peek_at(Kind::Eq) && self.eat(Kind::Type) { + ImportOrExportKind::Value + } else { + ImportOrExportKind::Type + }; + + let id = self.parse_binding_identifier()?; + + self.expect(Kind::Eq)?; + + let reference_node = self.start_node(); + let module_reference = if self.eat(Kind::Require) { + self.expect(Kind::LParen)?; + let expression = self.parse_literal_string()?; + self.expect(Kind::RParen)?; + TSModuleReference::ExternalModuleReference(TSExternalModuleReference { + node: self.end_node(reference_node), + expression, + }) + } else { + TSModuleReference::TypeName(self.parse_ts_qualified_name()?) + }; + + self.asi()?; + + Ok(self.ast.ts_import_equals_declaration( + self.end_node(node), + id, + module_reference, + is_export, + import_kind, + )) + } + + pub fn parse_ts_this_parameter(&mut self) -> Result<()> { + let _ident = self.parse_identifier_kind(Kind::Ident); + let _ = self.parse_ts_type_annotation()?; + Ok(()) + } + + pub fn eat_decorators(&mut self) -> Result<()> { + if !self.at(Kind::At) { + return Ok(()); + } + + let in_decorator = self.ctx.has_decorator(); + self.ctx = self.ctx.and_decorator(true); + + let mut decorators = self.ast.new_vec(); + while self.at(Kind::At) { + let decorator = self.parse_decorator()?; + decorators.push(decorator); + } + + self.ctx = self.ctx.and_decorator(in_decorator); + + self.state.decorators = Some(decorators); + Ok(()) + } + + fn parse_decorator(&mut self) -> Result> { + self.bump_any(); // bump @ + let node = self.start_node(); + let expr = self.with_context(Context::Decorator, Self::parse_lhs_expression)?; + Ok(self.ast.decorator(self.end_node(node), expr)) + } +} diff --git a/crates/oxc_parser/src/ts/types.rs b/crates/oxc_parser/src/ts/types.rs new file mode 100644 index 0000000000000..fe73b80b97189 --- /dev/null +++ b/crates/oxc_parser/src/ts/types.rs @@ -0,0 +1,1070 @@ +use bitflags::bitflags; +use oxc_allocator::{Box, Vec}; +use oxc_ast::{ast::*, context::Context}; +use oxc_diagnostics::{Diagnostic, Result}; + +use super::list::{ + TSInterfaceOrObjectBodyList, TSTupleElementList, TSTypeArgumentList, TSTypeParameterList, +}; +use crate::js::list::{ArrayPatternList, ObjectPatternProperties}; +use crate::lexer::Kind; +use crate::list::{NormalList, SeparatedList}; +use crate::Parser; + +bitflags! { + /// Bitflag of class member modifiers. + /// Useful to cheaply track all already seen modifiers of a member (instead of using a HashSet). + #[derive(Default)] + pub struct ModifierFlags: u8 { + const DECLARE = 1 << 0; + const PRIVATE = 1 << 1; + const PROTECTED = 1 << 2; + const PUBLIC = 1 << 3; + const STATIC = 1 << 4; + const READONLY = 1 << 5; + const ABSTRACT = 1 << 6; + const OVERRIDE = 1 << 7; + const ACCESSIBILITY = Self::PRIVATE.bits | Self::PROTECTED.bits | Self::PUBLIC.bits; + } +} + +impl ModifierFlags { + pub const fn accessibility(self) -> Option { + if self.contains(Self::PUBLIC) { + return Some(TSAccessibility::Public); + } + if self.contains(Self::PROTECTED) { + return Some(TSAccessibility::Protected); + } + + if self.contains(Self::PRIVATE) { + return Some(TSAccessibility::Private); + } + None + } + + pub const fn readonly(self) -> bool { + self.contains(Self::READONLY) + } + + pub const fn declare(self) -> bool { + self.contains(Self::DECLARE) + } + + pub const fn r#override(self) -> bool { + self.contains(Self::OVERRIDE) + } + + pub const fn r#abstract(self) -> bool { + self.contains(Self::ABSTRACT) + } + + pub const fn r#static(self) -> bool { + self.contains(Self::STATIC) + } +} + +impl<'a> Parser<'a> { + pub fn parse_ts_type(&mut self) -> Result> { + if self.is_at_constructor_type() { + return self.parse_ts_constructor_type(); + } + + if self.is_at_function_type() { + return self.parse_ts_function_type(); + } + + let left = self.parse_ts_union_type()?; + + self.parse_ts_conditional_type(left) + } + + pub fn parse_ts_type_parameters( + &mut self, + ) -> Result>>> { + if !self.ts_enabled() { + return Ok(None); + } + if !self.at(Kind::LAngle) { + return Ok(None); + } + let node = self.start_node(); + let params = TSTypeParameterList::parse(self)?.params; + Ok(Some(self.ast.ts_type_parameters(self.end_node(node), params))) + } + + pub fn parse_ts_implements_clause( + &mut self, + ) -> Result>>> { + self.expect(Kind::Implements)?; + let first = self.parse_ts_implement_name()?; + let mut implements = self.ast.new_vec(); + implements.push(first); + + while self.eat(Kind::Comma) { + implements.push(self.parse_ts_implement_name()?); + } + + Ok(implements) + } + + pub fn parse_ts_type_parameter(&mut self) -> Result>> { + let node = self.start_node(); + + let r#in = if self.at(Kind::In) && self.peek_kind().is_identifier_name() { + self.bump_any(); + true + } else { + false + }; + let out = if self.at(Kind::Out) && self.peek_kind().is_identifier_name() { + self.bump_any(); + true + } else { + false + }; + + if self.at(Kind::In) && self.peek_kind().is_identifier_name() { + // TODO error + } + + let name = self.parse_binding_identifier()?; + let constraint = self.parse_ts_type_constraint()?; + let default = self.parse_ts_default_type()?; + + Ok(self.ast.ts_type_parameter(self.end_node(node), name, constraint, default, r#in, out)) + } + + fn parse_ts_type_constraint(&mut self) -> Result>> { + if !self.at(Kind::Extends) { + return Ok(None); + } + self.bump_any(); + Ok(Some(self.parse_ts_type()?)) + } + + fn parse_ts_default_type(&mut self) -> Result>> { + if !self.at(Kind::Eq) { + return Ok(None); + } + self.bump_any(); + Ok(Some(self.parse_ts_type()?)) + } + + fn parse_ts_conditional_type(&mut self, left: TSType<'a>) -> Result> { + let node = self.start_node(); + if !self.ctx.has_disallow_conditional_types() + && !self.cur_token().is_on_new_line + && self.eat(Kind::Extends) + { + let extends_type = + self.with_context(Context::DisallowConditionalTypes, Self::parse_ts_type)?; + + self.expect(Kind::Question)?; + + let true_type = + self.without_context(Context::DisallowConditionalTypes, Self::parse_ts_type)?; + + self.expect(Kind::Colon)?; + + let false_type = + self.without_context(Context::DisallowConditionalTypes, Self::parse_ts_type)?; + + return Ok(self.ast.ts_conditional_type( + self.end_node(node), + left, + extends_type, + true_type, + false_type, + )); + } + + Ok(left) + } + + fn is_at_constructor_type(&mut self) -> bool { + self.at(Kind::New) || (self.at(Kind::Abstract) && self.peek_at(Kind::New)) + } + + // test ts ts_union_type + // type A = string | number; + // type B = | A | void | null; + // type C = A & C | C; + fn parse_ts_union_type(&mut self) -> Result> { + let node = self.start_node(); + if self.at(Kind::Pipe) { + let mut types = self.ast.new_vec(); + while self.eat(Kind::Pipe) { + types.push(self.parse_ts_intersection_type()?); + } + Ok(self.ast.ts_union_type(self.end_node(node), types)) + } else { + let first = self.parse_ts_intersection_type()?; + if self.at(Kind::Pipe) { + let mut types = self.ast.new_vec(); + types.push(first); + while self.eat(Kind::Pipe) { + types.push(self.parse_ts_intersection_type()?); + } + Ok(self.ast.ts_union_type(self.end_node(node), types)) + } else { + Ok(first) + } + } + } + + // test ts ts_intersection_type + // type A = string & number; + // type B = & A & void & null; + fn parse_ts_intersection_type(&mut self) -> Result> { + let node = self.start_node(); + if self.at(Kind::Amp) { + let mut types = self.ast.new_vec(); + while self.eat(Kind::Amp) { + types.push(self.parse_ts_primary_type()?); + } + Ok(self.ast.ts_intersection_type(self.end_node(node), types)) + } else { + let first = self.parse_ts_primary_type()?; + if self.at(Kind::Amp) { + let mut types = self.ast.new_vec(); + types.push(first); + while self.eat(Kind::Amp) { + types.push(self.parse_ts_primary_type()?); + } + Ok(self.ast.ts_intersection_type(self.end_node(node), types)) + } else { + Ok(first) + } + } + } + + fn parse_ts_primary_type(&mut self) -> Result> { + let node = self.start_node(); + if self.at(Kind::Infer) { + return self.parse_ts_infer_type(); + } + + let mut operator = None; + + if !self.at(Kind::Str) { + if let Some(atom) = self.cur_atom() { + operator = TSTypeOperator::from_src(atom); + } + } + + // test ts ts_type_operator + // type B = keyof A; + // type C = readonly string[]; + // const d: unique symbol = Symbol(); + if let Some(operator) = operator { + self.bump_any(); // bump operator + let type_annotation = self.parse_ts_primary_type()?; + return Ok(self.ast.ts_type_operator_type( + self.end_node(node), + operator, + type_annotation, + )); + } + + let mut left = + self.without_context(Context::DisallowConditionalTypes, Parser::parse_ts_basic_type)?; + + while !self.cur_token().is_on_new_line && self.eat(Kind::LBrack) { + if self.eat(Kind::RBrack) { + // test ts ts_array_type + // type A = string[]; + // type B = { a: number } []; + left = self.ast.ts_array_type(self.end_node(node), left); + } else { + // test ts ts_indexed_access_type + // type A = string[number]; + // type B = string[number][number][number][]; + let index_type = self.parse_ts_type()?; + self.expect(Kind::RBrack)?; + left = self.ast.ts_indexed_access_type(self.end_node(node), left, index_type); + } + } + + Ok(left) + } + + // test ts ts_predefined_type + // type A = any + // type B = number; + // type C = object; + // type D = boolean; + // type E = bigint; + // type F = string; + // type G = symbol; + // type H = void; + // type I = undefined; + // type J = null; + // type K = never + fn parse_ts_basic_type(&mut self) -> Result> { + match self.cur_kind() { + Kind::LParen => { + self.bump_any(); + let result = self.parse_ts_type(); + self.expect(Kind::RParen)?; + result + } + Kind::LBrack => self.parse_ts_tuple_type(), + Kind::LCurly => { + if self.is_at_mapped_type() { + self.parse_ts_mapped_type() + } else { + self.parse_ts_object_ype() + } + } + Kind::Void => { + let node = self.start_node(); + self.bump_any(); + Ok(self.ast.ts_void_keyword(self.end_node(node))) + } + Kind::This => { + let node = self.start_node(); + self.bump_any(); + Ok(self.ast.ts_this_keyword(self.end_node(node))) + } + Kind::NoSubstitutionTemplate | Kind::TemplateHead => { + self.parse_ts_template_literal_type(false) + } + Kind::Typeof => { + if self.peek_at(Kind::Import) { + self.parse_ts_import_type() + } else { + self.parse_ts_typeof_type() + } + } + Kind::Import => self.parse_ts_import_type(), + Kind::Minus if self.peek_kind().is_number() => self.parse_ts_literal_type(), + Kind::Question => self.parse_js_doc_unknown_or_nullable_type(), + kind if kind.is_literal() => self.parse_ts_literal_type(), + _ => { + if !self.peek_at(Kind::Dot) { + let keyword = self.parse_ts_keyword_type(); + if let Some(keyword) = keyword { + return Ok(keyword); + } + } + self.parse_ts_reference_type() + } + } + } + + fn parse_ts_keyword_type(&mut self) -> Option> { + let node = self.start_node(); + match self.cur_kind() { + Kind::Any => { + self.bump_any(); + Some(self.ast.ts_any_keyword(self.end_node(node))) + } + Kind::Unknown => { + self.bump_any(); + Some(self.ast.ts_unknown_keyword(self.end_node(node))) + } + Kind::Number => { + self.bump_any(); + Some(self.ast.ts_number_keyword(self.end_node(node))) + } + Kind::Boolean => { + self.bump_any(); + Some(self.ast.ts_boolean_keyword(self.end_node(node))) + } + Kind::Object => { + self.bump_any(); + Some(self.ast.ts_object_keyword(self.end_node(node))) + } + Kind::String => { + self.bump_any(); + Some(self.ast.ts_string_keyword(self.end_node(node))) + } + Kind::BigInt => { + self.bump_any(); + Some(self.ast.ts_bigint_keyword(self.end_node(node))) + } + Kind::Symbol => { + self.bump_any(); + Some(self.ast.ts_symbol_keyword(self.end_node(node))) + } + Kind::Null => { + self.bump_any(); + Some(self.ast.ts_null_keyword(self.end_node(node))) + } + Kind::Undefined => { + self.bump_any(); + Some(self.ast.ts_undefined_keyword(self.end_node(node))) + } + Kind::Never => { + self.bump_any(); + Some(self.ast.ts_never_keyword(self.end_node(node))) + } + _ => None, + } + } + + // test ts ts_reference_type + // type C = A; + // type D = B.a; + // type E = D.c.b.a; + fn parse_ts_reference_type(&mut self) -> Result> { + let node = self.start_node(); + let type_name = self.parse_ts_qualified_name()?; + let type_parameters = + if self.cur_token().is_on_new_line { None } else { self.parse_ts_type_arguments()? }; + + Ok(self.ast.ts_type_reference(self.end_node(node), type_name, type_parameters)) + } + + fn parse_ts_implement_name(&mut self) -> Result>> { + let node = self.start_node(); + let expression = self.parse_ts_qualified_name()?; + let type_parameters = + if self.cur_token().is_on_new_line { None } else { self.parse_ts_type_arguments()? }; + + Ok(self.ast.ts_type_implement(self.end_node(node), expression, type_parameters)) + } + + pub fn parse_ts_qualified_name(&mut self) -> Result> { + let node = self.start_node(); + let identifier_name = self.parse_identifier_name()?; + let mut left = TSTypeName::IdentifierName(self.ast.alloc(identifier_name)); + + while self.eat(Kind::Dot) { + let right = self.parse_identifier_name()?; + left = TSTypeName::QualifiedName(self.ast.alloc(TSQualifiedName { + node: self.end_node(node), + left, + right, + })); + } + + Ok(left) + } + + pub fn parse_ts_type_arguments( + &mut self, + ) -> Result>>> { + self.re_lex_ts_l_angle(); + if !self.at(Kind::LAngle) { + return Ok(None); + } + let node = self.start_node(); + let params = TSTypeArgumentList::parse(self)?.params; + Ok(Some(self.ast.ts_type_arguments(self.end_node(node), params))) + } + + pub fn parse_ts_type_arguments_in_expression( + &mut self, + ) -> Result>>> { + if !matches!(self.cur_kind(), Kind::LAngle | Kind::ShiftLeft) { + return Ok(None); + } + let node = self.start_node(); + + Ok(self + .try_parse(|p| { + p.re_lex_ts_l_angle(); + + let params = TSTypeArgumentList::parse(p)?.params; + if p.cur_kind().can_follow_type_arguments_in_expr() { + Ok(params) + } else { + p.unexpected() + } + }) + .ok() + .map(|types| self.ast.ts_type_arguments(self.end_node(node), types))) + } + + fn parse_ts_tuple_type(&mut self) -> Result> { + let node = self.start_node(); + let elements = TSTupleElementList::parse(self)?.elements; + Ok(self.ast.ts_tuple_type(self.end_node(node), elements)) + } + + fn is_at_function_type(&mut self) -> bool { + if self.at(Kind::LAngle) { + return true; + } + + if !self.at(Kind::LParen) { + return false; + } + + let checkpoint = self.checkpoint(); + + self.bump_any(); // bump ( + + if self.at(Kind::RParen) || self.at(Kind::Dot3) { + self.rewind(checkpoint); + return true; + } + + let mut is_function_parameter_start = + self.at(Kind::This) || self.cur_kind().is_binding_identifier(); + + if is_function_parameter_start { + self.bump_any(); + } + + if match self.cur_kind() { + Kind::LBrack => ArrayPatternList::parse(self).is_ok(), + Kind::LCurly => ObjectPatternProperties::parse(self).is_ok(), + _ => false, + } { + is_function_parameter_start = true; + } + + let result = if is_function_parameter_start { + matches!(self.cur_kind(), Kind::Colon | Kind::Eq | Kind::Comma | Kind::Question) + || (self.at(Kind::RParen) && self.peek_at(Kind::Arrow)) + } else { + false + }; + + self.rewind(checkpoint); + + result + } + + fn is_at_mapped_type(&mut self) -> bool { + if !self.at(Kind::LCurly) { + return false; + } + + if self.peek_at(Kind::Plus) || self.peek_at(Kind::Minus) { + return self.nth_at(2, Kind::Readonly); + } + + let mut offset = 1; + + if self.nth_at(offset, Kind::Readonly) { + offset += 1; + } + + self.nth_at(offset, Kind::LBrack) + && self.nth_kind(offset + 1).is_identifier_name() + && self.nth_at(offset + 2, Kind::In) + } + + fn parse_ts_mapped_type(&mut self) -> Result> { + let node = self.start_node(); + self.expect(Kind::LCurly)?; + let mut readonly = TSMappedTypeModifierOperator::None; + if self.eat(Kind::Readonly) { + readonly = TSMappedTypeModifierOperator::True; + } else if self.eat(Kind::Plus) && self.eat(Kind::Readonly) { + readonly = TSMappedTypeModifierOperator::Plus; + } else if self.eat(Kind::Minus) && self.eat(Kind::Readonly) { + readonly = TSMappedTypeModifierOperator::Minus; + } + + self.expect(Kind::LBrack)?; + let type_parameter_node = self.start_node(); + if !self.cur_kind().is_identifier_name() { + return self.unexpected(); + } + let name = self.parse_binding_identifier()?; + self.expect(Kind::In)?; + let constraint = self.parse_ts_type()?; + let type_parameter = self.ast.ts_type_parameter( + self.end_node(type_parameter_node), + name, + Some(constraint), + None, + false, + false, + ); + + let name_type = if self.eat(Kind::As) { Some(self.parse_ts_type()?) } else { None }; + self.expect(Kind::RBrack)?; + + let optional = match self.cur_kind() { + Kind::Question => { + self.bump_any(); + TSMappedTypeModifierOperator::True + } + Kind::Minus => { + self.bump_any(); + self.expect(Kind::Question)?; + TSMappedTypeModifierOperator::Minus + } + Kind::Plus => { + self.bump_any(); + self.expect(Kind::Question)?; + TSMappedTypeModifierOperator::Plus + } + _ => TSMappedTypeModifierOperator::None, + }; + + self.expect(Kind::Colon)?; + let type_annotation = self.parse_ts_type()?; + + self.bump(Kind::Semicolon); + self.expect(Kind::RCurly)?; + + Ok(self.ast.ts_mapped_type( + self.end_node(node), + type_parameter, + name_type, + type_annotation, + optional, + readonly, + )) + } + + pub fn is_at_named_tuple_element(&mut self) -> bool { + let offset = usize::from(self.at(Kind::Dot3)); + let has_colon = self.nth_at(offset + 1, Kind::Colon); + let has_question_colon = + self.nth_at(offset + 1, Kind::Question) && self.nth_at(offset + 2, Kind::Colon); + + self.nth_kind(offset).is_identifier_name() && (has_colon || has_question_colon) + } + + fn parse_ts_object_ype(&mut self) -> Result> { + let node = self.start_node(); + let mut member_list = TSInterfaceOrObjectBodyList::new(self); + member_list.parse(self)?; + + Ok(self.ast.ts_type_literal(self.end_node(node), member_list.body)) + } + + fn parse_ts_literal_type(&mut self) -> Result> { + let node = self.start_node(); + let negative = self.eat(Kind::Minus); + + let expression = self.parse_literal_expression()?; + + let node = self.end_node(node); + let literal = if negative { + match self.ast.unary_expression(node, UnaryOperator::UnaryNegation, true, expression) { + Expression::UnaryExpression(unary_expr) => TSLiteral::UnaryExpression(unary_expr), + _ => unreachable!(), + } + } else { + match expression { + Expression::BooleanLiteral(literal) => TSLiteral::BooleanLiteral(literal), + Expression::NullLiteral(literal) => TSLiteral::NullLiteral(literal), + Expression::NumberLiteral(literal) => TSLiteral::NumberLiteral(literal), + Expression::BigintLiteral(literal) => TSLiteral::BigintLiteral(literal), + Expression::RegExpLiteral(literal) => TSLiteral::RegExpLiteral(literal), + Expression::StringLiteral(literal) => TSLiteral::StringLiteral(literal), + Expression::TemplateLiteral(literal) => TSLiteral::TemplateLiteral(literal), + _ => return self.unexpected(), + } + }; + + Ok(self.ast.ts_literal_type(node, literal)) + } + + fn parse_ts_template_literal_type(&mut self, tagged: bool) -> Result> { + let node = self.start_node(); + let mut types = self.ast.new_vec(); + let mut quasis = self.ast.new_vec(); + match self.cur_kind() { + Kind::NoSubstitutionTemplate => { + quasis.push(self.parse_template_element(tagged)); + } + Kind::TemplateHead => { + quasis.push(self.parse_template_element(tagged)); + types.push(self.parse_ts_type()?); + self.re_lex_template_substitution_tail(); + loop { + match self.cur_kind() { + Kind::Eof => self.expect(Kind::TemplateTail)?, + Kind::TemplateTail => { + quasis.push(self.parse_template_element(tagged)); + break; + } + Kind::TemplateMiddle => { + quasis.push(self.parse_template_element(tagged)); + } + _ => { + types.push(self.parse_ts_type()?); + self.re_lex_template_substitution_tail(); + } + } + } + } + _ => unreachable!("parse_template_literal"), + } + + Ok(self.ast.ts_template_literal_type(self.end_node(node), quasis, types)) + } + + fn parse_ts_typeof_type(&mut self) -> Result> { + let node = self.start_node(); + self.expect(Kind::Typeof)?; + let expr_name = self.parse_ts_qualified_name()?; + let type_parameters = self.parse_ts_type_arguments()?; + Ok(self.ast.ts_type_query_type(self.end_node(node), expr_name, type_parameters)) + } + + fn parse_ts_import_type(&mut self) -> Result> { + let node = self.start_node(); + let is_type_of = self.eat(Kind::Typeof); + self.expect(Kind::Import)?; + self.expect(Kind::LParen)?; + let parameter = self.parse_ts_type()?; + self.expect(Kind::RParen)?; + + let qualifier = + if self.eat(Kind::Dot) { Some(self.parse_ts_qualified_name()?) } else { None }; + + let type_parameters = self.parse_ts_type_arguments()?; + + Ok(self.ast.ts_import_type( + self.end_node(node), + is_type_of, + parameter, + qualifier, + type_parameters, + )) + } + + fn parse_ts_constructor_type(&mut self) -> Result> { + let node = self.start_node(); + let r#abstract = self.eat(Kind::Abstract); + self.expect(Kind::New)?; + let type_parameters = self.parse_ts_type_parameters()?; + let params = self.parse_formal_parameters(FormalParameterKind::Signature)?; + self.expect(Kind::Arrow)?; + let return_type_node = self.start_node(); + let return_type = self.parse_ts_return_type()?; + let return_type = self.ast.ts_type_annotation(self.end_node(return_type_node), return_type); + + Ok(self.ast.ts_constructor_type( + self.end_node(node), + r#abstract, + params, + return_type, + type_parameters, + )) + } + + fn parse_ts_function_type(&mut self) -> Result> { + let node = self.start_node(); + let type_parameters = self.parse_ts_type_parameters()?; + let params = self.parse_formal_parameters(FormalParameterKind::Signature)?; + self.expect(Kind::Arrow)?; + let return_type_node = self.start_node(); + let return_type = self.parse_ts_return_type()?; + let return_type = self.ast.ts_type_annotation(self.end_node(return_type_node), return_type); + Ok(self.ast.ts_function_type(self.end_node(node), params, return_type, type_parameters)) + } + + fn parse_ts_infer_type(&mut self) -> Result> { + let node = self.start_node(); + self.expect(Kind::Infer)?; + + let parameter_node = self.start_node(); + let name = self.parse_binding_identifier()?; + + let constraint = self.try_parse(Parser::parse_constraint_of_infer_type).unwrap_or(None); + + let type_parameter = self.ast.ts_type_parameter( + self.end_node(parameter_node), + name, + constraint, + None, + false, + false, + ); + + Ok(self.ast.ts_infer_type(self.end_node(node), type_parameter)) + } + + fn parse_constraint_of_infer_type(&mut self) -> Result>> { + if self.eat(Kind::Extends) { + let constraint = + self.with_context(Context::DisallowConditionalTypes, Self::parse_ts_type)?; + if self.ctx.has_disallow_conditional_types() || !self.at(Kind::Question) { + return Ok(Some(constraint)); + } + } + self.unexpected() + } + + pub fn parse_ts_return_type_annotation(&mut self) -> Result>> { + if !self.ts_enabled() { + return Ok(None); + } + if !self.at(Kind::Colon) { + return Ok(None); + } + let node = self.start_node(); + self.bump_any(); // bump colon + let return_type = self.parse_ts_return_type()?; + Ok(Some(self.ast.ts_type_annotation(self.end_node(node), return_type))) + } + + fn parse_ts_type_predicate(&mut self) -> Result> { + let node = self.start_node(); + let asserts = self.eat(Kind::Asserts); + + let parameter_name = if self.at(Kind::This) { + let node = self.start_node(); + self.bump_any(); + TSTypePredicateName::This(TSThisKeyword { node: self.end_node(node) }) + } else { + TSTypePredicateName::Identifier(self.parse_identifier_name()?) + }; + + if !asserts { + self.expect(Kind::Is)?; + } else if !self.eat(Kind::Is) { + return Ok(self.ast.ts_type_predicate( + self.end_node(node), + parameter_name, + asserts, + None, + )); + } + + let type_node = self.start_node(); + let type_annotation = self.parse_ts_type()?; + let type_annotation = + Some(self.ast.ts_type_annotation(self.end_node(type_node), type_annotation)); + + Ok(self.ast.ts_type_predicate( + self.end_node(node), + parameter_name, + asserts, + type_annotation, + )) + } + + pub fn parse_ts_return_type(&mut self) -> Result> { + let asserts = self.at(Kind::Asserts) + && (self.peek_kind().is_identifier() || self.peek_at(Kind::This)); + let is_predicate = + (self.cur_kind().is_identifier() || self.at(Kind::This)) && self.peek_at(Kind::Is); + if !self.peek_token().is_on_new_line && (asserts || is_predicate) { + self.parse_ts_type_predicate() + } else { + self.without_context(Context::DisallowConditionalTypes, Self::parse_ts_type) + } + } + + pub fn is_next_at_type_member_name(&mut self) -> bool { + self.peek_kind().is_literal_property_name() || self.peek_at(Kind::LBrack) + } + + pub fn parse_ts_call_signature_member(&mut self) -> Result> { + let node = self.start_node(); + let type_parameters = self.parse_ts_type_parameters()?; + let params = self.parse_formal_parameters(FormalParameterKind::Signature)?; + let return_type = self.parse_ts_return_type_annotation()?; + self.bump(Kind::Comma); + self.bump(Kind::Semicolon); + Ok(self.ast.ts_call_signature_declaration( + self.end_node(node), + params, + return_type, + type_parameters, + )) + } + + pub fn parse_ts_getter_signature_member(&mut self) -> Result> { + let node = self.start_node(); + self.expect(Kind::Get)?; + let (key, computed) = self.parse_property_name()?; + let params = self.parse_formal_parameters(FormalParameterKind::Signature)?; + let return_type = self.parse_ts_return_type_annotation()?; + self.bump(Kind::Comma); + self.bump(Kind::Semicolon); + if !params.is_empty() { + self.error(Diagnostic::GetterParameters(params.node.range())); + } + Ok(self.ast.ts_method_signature( + self.end_node(node), + key, + computed, + /* optional */ false, + TSMethodSignatureKind::Get, + params, + return_type, + None, + )) + } + + pub fn parse_ts_setter_signature_member(&mut self) -> Result> { + let node = self.start_node(); + self.expect(Kind::Set)?; + let (key, computed) = self.parse_property_name()?; + let params = self.parse_formal_parameters(FormalParameterKind::Signature)?; + let return_type = self.parse_ts_return_type_annotation()?; + self.bump(Kind::Comma); + self.bump(Kind::Semicolon); + if params.items.len() != 1 { + self.error(Diagnostic::SetterParameters(params.node.range())); + } + if let Some(return_type) = return_type.as_ref() { + self.error(Diagnostic::ASetAccessorCannotHaveAReturnTypeAnnotation( + return_type.node.range(), + )); + } + Ok(self.ast.ts_method_signature( + self.end_node(node), + key, + computed, + /* optional */ false, + TSMethodSignatureKind::Set, + params, + return_type, + None, + )) + } + + pub fn parse_ts_property_or_method_signature_member(&mut self) -> Result> { + let node = self.start_node(); + let readonly = self.at(Kind::Readonly) && self.is_next_at_type_member_name(); + + if readonly { + self.bump_any(); + } + + let (key, computed) = self.parse_property_name()?; + let optional = self.eat(Kind::Question); + + if self.at(Kind::LParen) || self.at(Kind::LAngle) { + let TSSignature::TSCallSignatureDeclaration(call_signature) = self.parse_ts_call_signature_member()? else { unreachable!() }; + self.bump(Kind::Comma); + self.bump(Kind::Semicolon); + let call_signature = call_signature.unbox(); + Ok(self.ast.ts_method_signature( + self.end_node(node), + key, + computed, + optional, + TSMethodSignatureKind::Method, + call_signature.params, + call_signature.return_type, + call_signature.type_parameters, + )) + } else { + let type_annotation = self.parse_ts_type_annotation()?; + self.bump(Kind::Comma); + self.bump(Kind::Semicolon); + Ok(self.ast.ts_property_signature( + self.end_node(node), + computed, + optional, + readonly, + key, + type_annotation, + )) + } + } + + pub fn parse_ts_constructor_signature_member(&mut self) -> Result> { + let node = self.start_node(); + self.expect(Kind::New)?; + + let type_parameters = self.parse_ts_type_parameters()?; + let params = self.parse_formal_parameters(FormalParameterKind::Signature)?; + let return_type = self.parse_ts_return_type_annotation()?; + self.bump(Kind::Comma); + self.bump(Kind::Semicolon); + + Ok(self.ast.ts_construct_signature_declaration( + self.end_node(node), + params, + return_type, + type_parameters, + )) + } + + pub fn parse_ts_index_signature_member(&mut self) -> Result> { + let node = self.start_node(); + while self.is_nth_at_modifier(0, false) { + if !self.eat(Kind::Readonly) { + self.unexpected()?; + } + } + + self.bump(Kind::LBrack); + let index_name = self.parse_ts_index_signature_name()?; + let mut parameters = self.ast.new_vec(); + parameters.push(index_name); + self.expect(Kind::RBrack)?; + + let type_annotation = self.parse_ts_type_annotation()?; + if let Some(type_annotation) = type_annotation { + self.bump(Kind::Comma); + self.bump(Kind::Semicolon); + Ok(self.ast.ts_index_signature(self.end_node(node), parameters, type_annotation)) + } else { + self.unexpected() + } + } + + fn parse_ts_index_signature_name(&mut self) -> Result>> { + let node = self.start_node(); + let name = self.parse_identifier_name()?.name; + let type_annotation = self.parse_ts_type_annotation()?; + + if type_annotation.is_none() { + self.unexpected()?; + } + + Ok(self.ast.alloc(TSIndexSignatureName { + node: self.end_node(node), + name, + type_annotation: type_annotation.unwrap(), + })) + } + + pub fn parse_class_element_modifiers( + &mut self, + is_constructor_parameter: bool, + ) -> ModifierFlags { + let mut flags = ModifierFlags::empty(); + + if !self.ts_enabled() { + return flags; + } + + loop { + if !self.is_nth_at_modifier(0, is_constructor_parameter) { + break; + } + + let modifier_flag = match self.cur_kind() { + Kind::Private => ModifierFlags::PRIVATE, + Kind::Protected => ModifierFlags::PROTECTED, + Kind::Public => ModifierFlags::PUBLIC, + Kind::Static => ModifierFlags::STATIC, + Kind::Abstract => ModifierFlags::ABSTRACT, + Kind::Readonly => ModifierFlags::READONLY, + Kind::Declare => ModifierFlags::DECLARE, + Kind::Override => ModifierFlags::OVERRIDE, + _ => break, + }; + + flags.set(modifier_flag, true); + + self.bump_any(); + } + + flags + } + + fn parse_js_doc_unknown_or_nullable_type(&mut self) -> Result> { + let node = self.start_node(); + self.bump_any(); // bump `?` + let type_annotation = self.parse_ts_type()?; + let node = self.end_node(node); + if matches!( + self.cur_kind(), + Kind::Comma | Kind::RCurly | Kind::RParen | Kind::RAngle | Kind::Eq | Kind::Pipe + ) { + Ok(self.ast.js_doc_unknown_type(node)) + } else { + Ok(self.ast.js_doc_nullable_type(node, type_annotation, /* postfix */ false)) + } + } +}