Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions crates/oxc_parser/src/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use oxc_diagnostics::OxcDiagnostic;
use oxc_span::Span;

use crate::modifiers::Modifier;

#[cold]
pub fn redeclaration(x0: &str, declare_span: Span, redeclare_span: Span) -> OxcDiagnostic {
OxcDiagnostic::error(format!("Identifier `{x0}` has already been declared")).with_labels([
Expand Down Expand Up @@ -399,7 +401,43 @@ pub fn jsx_element_no_match(span0: Span, span1: Span, name: &str) -> OxcDiagnost
.with_labels([span0, span1])
}

// ================================= MODIFIERS =================================

#[cold]
pub fn modifier_cannot_be_used_here(modifier: &Modifier) -> OxcDiagnostic {
OxcDiagnostic::error(format!("'{}' modifier cannot be used here.", modifier.kind))
.with_label(modifier.span)
}

/// TS(1030)
#[cold]
pub fn modifier_already_seen(modifier: &Modifier) -> OxcDiagnostic {
OxcDiagnostic::error(format!("TS1030: '{}' modifier already seen.", modifier.kind))
.with_label(modifier.span)
.with_help("Remove the duplicate modifier.")
}

/// TS(1273)
#[cold]
pub fn cannot_appear_on_a_type_parameter(modifier: &Modifier) -> OxcDiagnostic {
OxcDiagnostic::error(format!(
"'{}' modifier cannot be used on a type parameter.",
modifier.kind
))
.with_label(modifier.span)
}
/// TS(1090)
pub fn cannot_appear_on_a_parameter(modifier: &Modifier) -> OxcDiagnostic {
OxcDiagnostic::error(format!(
"TS1090: '{}' modifier cannot appear on a parameter.",
modifier.kind
))
.with_label(modifier.span)
}

/// TS(18010)
#[cold]
pub fn modifier_cannot_be_used_here(span: Span, name: &str) -> OxcDiagnostic {
OxcDiagnostic::error(format!("'{name}' modifier cannot be used here.")).with_label(span)
pub fn accessibility_modifier_on_private_property(modifier: &Modifier) -> OxcDiagnostic {
OxcDiagnostic::error("An accessibility modifier cannot be used with a private identifier.")
.with_label(modifier.span)
}
58 changes: 30 additions & 28 deletions crates/oxc_parser/src/js/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
diagnostics,
lexer::Kind,
list::NormalList,
modifiers::{ModifierKind, Modifiers},
modifiers::{ModifierFlags, ModifierKind, Modifiers},
Context, ParserImpl, StatementContext,
};

Expand All @@ -25,7 +25,11 @@ impl<'a> ParserImpl<'a> {
stmt_ctx: StatementContext,
start_span: Span,
) -> Result<Statement<'a>> {
let decl = self.parse_class_declaration(start_span, &Modifiers::empty())?;
let modifiers = self.parse_modifiers(
/* allow_decorators */ true, /* permit_const_as_modifier */ false,
/* stop_on_start_of_class_static_block */ true,
);
let decl = self.parse_class_declaration(start_span, &modifiers)?;

if stmt_ctx.is_single_statement() {
self.error(diagnostics::class_declaration(Span::new(
Expand Down Expand Up @@ -86,14 +90,11 @@ impl<'a> ParserImpl<'a> {
}
let body = self.parse_class_body()?;

for modifier in modifiers.iter() {
if !matches!(modifier.kind, ModifierKind::Declare | ModifierKind::Abstract) {
self.error(diagnostics::modifier_cannot_be_used_here(
modifier.span,
modifier.kind.as_str(),
));
}
}
self.verify_modifiers(
modifiers,
ModifierFlags::DECLARE | ModifierFlags::ABSTRACT,
diagnostics::modifier_cannot_be_used_here,
);

Ok(self.ast.class(
r#type,
Expand All @@ -105,8 +106,8 @@ impl<'a> ParserImpl<'a> {
super_type_parameters,
implements,
decorators,
modifiers.is_contains_abstract(),
modifiers.is_contains_declare(),
modifiers.contains_abstract(),
modifiers.contains_declare(),
))
}

Expand Down Expand Up @@ -178,28 +179,21 @@ impl<'a> ParserImpl<'a> {
pub(crate) fn parse_class_element(&mut self) -> Result<ClassElement<'a>> {
let span = self.start_span();

self.eat_decorators()?;
let modifiers = self.parse_modifiers(true, false, true);

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 = {
let token = self.peek_token();
!token.is_on_new_line && token.kind.is_class_element_name_start()
} && 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();
let accessibility = modifiers.accessibility();
let accessor = modifiers.contains(ModifierKind::Accessor);
let declare = modifiers.contains(ModifierKind::Declare);
let readonly = modifiers.contains(ModifierKind::Readonly);
let r#override = modifiers.contains(ModifierKind::Override);
let r#abstract = modifiers.contains(ModifierKind::Abstract);
let mut r#static = modifiers.contains(ModifierKind::Static);
let mut r#async = modifiers.contains(ModifierKind::Async);

if self.at(Kind::Static) {
// static { block }
Expand Down Expand Up @@ -268,6 +262,14 @@ impl<'a> ParserImpl<'a> {
let definite = self.eat(Kind::Bang);

if let PropertyKey::PrivateIdentifier(private_ident) = &key {
// `private #foo`, etc. is illegal
if self.ts_enabled() {
self.verify_modifiers(
&modifiers,
ModifierFlags::all() - ModifierFlags::ACCESSIBILITY,
diagnostics::accessibility_modifier_on_private_property,
);
}
if private_ident.name == "constructor" {
self.error(diagnostics::private_name_constructor(private_ident.span));
}
Expand Down
17 changes: 7 additions & 10 deletions crates/oxc_parser/src/js/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use super::{VariableDeclarationContext, VariableDeclarationParent};
use crate::{
diagnostics,
lexer::Kind,
modifiers::{ModifierKind, Modifiers},
modifiers::{ModifierFlags, Modifiers},
ParserImpl, StatementContext,
};

Expand Down Expand Up @@ -72,20 +72,17 @@ impl<'a> ParserImpl<'a> {
self.asi()?;
}

for modifier in modifiers.iter() {
if modifier.kind != ModifierKind::Declare {
self.error(diagnostics::modifier_cannot_be_used_here(
modifier.span,
modifier.kind.as_str(),
));
}
}
self.verify_modifiers(
modifiers,
ModifierFlags::DECLARE,
diagnostics::modifier_cannot_be_used_here,
);

Ok(self.ast.variable_declaration(
self.end_span(start_span),
kind,
declarations,
modifiers.is_contains_declare(),
modifiers.contains_declare(),
))
}

Expand Down
17 changes: 7 additions & 10 deletions crates/oxc_parser/src/js/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
diagnostics,
lexer::Kind,
list::SeparatedList,
modifiers::{ModifierKind, Modifiers},
modifiers::{ModifierFlags, ModifierKind, Modifiers},
Context, ParserImpl, StatementContext,
};

Expand Down Expand Up @@ -109,22 +109,19 @@ impl<'a> ParserImpl<'a> {
self.asi()?;
}

for modifier in modifiers.iter() {
if !matches!(modifier.kind, ModifierKind::Declare | ModifierKind::Async) {
self.error(diagnostics::modifier_cannot_be_used_here(
modifier.span,
modifier.kind.as_str(),
));
}
}
self.verify_modifiers(
modifiers,
ModifierFlags::DECLARE | ModifierFlags::ASYNC,
diagnostics::modifier_cannot_be_used_here,
);

Ok(self.ast.function(
function_type,
self.end_span(span),
id,
generator,
r#async,
modifiers.is_contains_declare(),
modifiers.contains_declare(),
this_param,
params,
body,
Expand Down
12 changes: 10 additions & 2 deletions crates/oxc_parser/src/js/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
diagnostics,
lexer::Kind,
list::{NormalList, SeparatedList},
modifiers::ModifierFlags,
ParserImpl,
};

Expand Down Expand Up @@ -267,8 +268,15 @@ impl<'a> SeparatedList<'a> for FormalParameterList<'a> {

let modifiers = p.parse_class_element_modifiers(true);
let accessibility = modifiers.accessibility();
let readonly = modifiers.readonly();
let r#override = modifiers.r#override();
let readonly = modifiers.contains_readonly();
let r#override = modifiers.contains_override();
p.verify_modifiers(
&modifiers,
ModifierFlags::ACCESSIBILITY
.union(ModifierFlags::READONLY)
.union(ModifierFlags::OVERRIDE),
diagnostics::cannot_appear_on_a_parameter,
);

match p.cur_kind() {
Kind::This if p.ts_enabled() => {
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_parser/src/js/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ impl<'a> ParserImpl<'a> {
// For more information, please refer to <https://babeljs.io/docs/babel-plugin-proposal-decorators#decoratorsbeforeexport>
self.eat_decorators()?;
let modifiers = if self.ts_enabled() {
self.eat_modifiers_before_declaration().1
self.eat_modifiers_before_declaration()?
} else {
Modifiers::empty()
};
Expand Down Expand Up @@ -324,7 +324,7 @@ impl<'a> ParserImpl<'a> {
.map(ExportDefaultDeclarationKind::ClassDeclaration)?,
_ if self.at(Kind::Abstract) && self.peek_at(Kind::Class) && self.ts_enabled() => {
// eat the abstract modifier
let (_, modifiers) = self.eat_modifiers_before_declaration();
let modifiers = self.eat_modifiers_before_declaration()?;
self.parse_class_declaration(decl_span, &modifiers)
.map(ExportDefaultDeclarationKind::ClassDeclaration)?
}
Expand Down
21 changes: 20 additions & 1 deletion crates/oxc_parser/src/js/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use oxc_span::Span;
use oxc_syntax::operator::AssignmentOperator;

use super::list::ObjectExpressionProperties;
use crate::{lexer::Kind, list::SeparatedList, Context, ParserImpl};
use crate::{
diagnostics, lexer::Kind, list::SeparatedList, modifiers::Modifier, Context, ParserImpl,
};

impl<'a> ParserImpl<'a> {
/// [Object Expression](https://tc39.es/ecma262/#sec-object-initializer)
Expand Down Expand Up @@ -43,6 +45,23 @@ impl<'a> ParserImpl<'a> {
}
// GeneratorMethod
Kind::Star if class_element_name => self.parse_property_definition_method(),
// Report and handle illegal modifiers
// e.g. const x = { public foo() {} }
modifier_kind
if self.ts_enabled()
&& modifier_kind.is_modifier_kind()
&& !matches!(peek_kind, Kind::Colon) =>
{
if let Ok(modifier) = Modifier::try_from(self.cur_token()) {
self.error(diagnostics::modifier_cannot_be_used_here(&modifier));
} else {
#[cfg(debug_assertions)]
panic!("Kind::is_modifier_kind() is true but the token could not be converted to a Modifier.")
}
// re-parse
self.bump_any();
self.parse_property_definition()
}
// IdentifierReference
kind if kind.is_identifier_reference(false, false)
// test Kind::Dot to ignore ({ foo.bar: baz })
Expand Down
6 changes: 6 additions & 0 deletions crates/oxc_parser/src/lexer/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@ impl Kind {
self.is_identifier_name() || self == Str || self.is_number()
}

pub fn is_identifier_or_keyword(self) -> bool {
self.is_literal_property_name()
|| matches!(self, Self::PrivateIdentifier)
|| self.is_all_keyword()
}

pub fn is_variable_declaration(self) -> bool {
matches!(self, Var | Let | Const)
}
Expand Down
Loading