oxc/crates/oxc_parser/src/modifiers.rs
2024-09-06 04:04:26 +00:00

557 lines
18 KiB
Rust

use bitflags::bitflags;
use oxc_allocator::Vec;
use oxc_ast::ast::TSAccessibility;
use oxc_diagnostics::{OxcDiagnostic, Result};
use oxc_span::{GetSpan, Span, SPAN};
use crate::{
diagnostics,
lexer::{Kind, Token},
ParserImpl,
};
bitflags! {
/// Bitflag of modifiers and contextual modifiers.
/// Useful to cheaply track all already seen modifiers of a member (instead of using a HashSet<ModifierKind>).
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ModifierFlags: u16 {
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 ASYNC = 1 << 8;
const CONST = 1 << 9;
const IN = 1 << 10;
const OUT = 1 << 11;
const EXPORT = 1 << 12;
const DEFAULT = 1 << 13;
const ACCESSOR = 1 << 14;
const ACCESSIBILITY = Self::PRIVATE.bits() | Self::PROTECTED.bits() | Self::PUBLIC.bits();
}
}
/// It is the caller's safety to always check by `Kind::is_modifier_kind`
/// before converting [`Kind`] to [`ModifierFlags`] so that we can assume here that
/// the conversion always succeeds.
impl From<Kind> for ModifierFlags {
fn from(value: Kind) -> Self {
match value {
Kind::Abstract => Self::ABSTRACT,
Kind::Declare => Self::DECLARE,
Kind::Private => Self::PRIVATE,
Kind::Protected => Self::PROTECTED,
Kind::Public => Self::PUBLIC,
Kind::Static => Self::STATIC,
Kind::Readonly => Self::READONLY,
Kind::Override => Self::OVERRIDE,
Kind::Async => Self::ASYNC,
Kind::Const => Self::CONST,
Kind::In => Self::IN,
Kind::Out => Self::OUT,
Kind::Export => Self::EXPORT,
Kind::Default => Self::DEFAULT,
Kind::Accessor => Self::ACCESSOR,
_ => unreachable!(),
}
}
}
impl From<ModifierKind> for ModifierFlags {
fn from(kind: ModifierKind) -> Self {
match kind {
ModifierKind::Abstract => Self::ABSTRACT,
ModifierKind::Declare => Self::DECLARE,
ModifierKind::Private => Self::PRIVATE,
ModifierKind::Protected => Self::PROTECTED,
ModifierKind::Public => Self::PUBLIC,
ModifierKind::Static => Self::STATIC,
ModifierKind::Readonly => Self::READONLY,
ModifierKind::Override => Self::OVERRIDE,
ModifierKind::Async => Self::ASYNC,
ModifierKind::Const => Self::CONST,
ModifierKind::In => Self::IN,
ModifierKind::Out => Self::OUT,
ModifierKind::Export => Self::EXPORT,
ModifierKind::Default => Self::DEFAULT,
ModifierKind::Accessor => Self::ACCESSOR,
}
}
}
impl ModifierFlags {
pub(crate) fn accessibility(self) -> Option<TSAccessibility> {
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
}
}
#[derive(Debug, Hash)]
pub struct Modifier {
pub span: Span,
pub kind: ModifierKind,
}
impl Modifier {
#[inline]
pub fn is_static(&self) -> bool {
matches!(self.kind, ModifierKind::Static)
}
}
impl TryFrom<Token> for Modifier {
type Error = <ModifierKind as TryFrom<Kind>>::Error;
fn try_from(tok: Token) -> std::result::Result<Self, Self::Error> {
ModifierKind::try_from(tok.kind).map(|kind| Self { span: tok.span(), kind })
}
}
/// Symbol modifiers. Primarily used in TypeScript code, but some are also used
/// in JavaScript.
///
/// ```ts
/// class Foo {
/// public readonly x: number
/// // ^^^^^^ ^^^^^^^^
/// // these are modifiers
/// }
/// export const f = new foo()
/// // ^^^ This also counts as a modifier, but is also recorded separately as a
/// // named export declaration
/// ```
#[derive(Debug, Hash)]
pub struct Modifiers<'a> {
/// May contain duplicates.
modifiers: Option<Vec<'a, Modifier>>,
/// Bitflag representation of modifier kinds stored in [`Self::modifiers`].
/// Pre-computed to save CPU cycles on [`Self::contains`] checks (`O(1)`
/// bitflag intersection vs `O(n)` linear search).
flags: ModifierFlags,
}
impl<'a> Default for Modifiers<'a> {
fn default() -> Self {
Self::empty()
}
}
impl<'a> Modifiers<'a> {
/// Create a new set of modifiers
///
/// # Invariants
/// `flags` must correctly reflect the [`ModifierKind`]s within
/// `modifiers`. E.g., if `modifiers` is empty, then so is `flags``.
#[must_use]
pub(crate) fn new(modifiers: Vec<'a, Modifier>, flags: ModifierFlags) -> Self {
if modifiers.is_empty() {
debug_assert!(flags.is_empty());
Self::empty()
} else {
Self { modifiers: Some(modifiers), flags }
}
}
pub fn empty() -> Self {
Self { modifiers: None, flags: ModifierFlags::empty() }
}
pub fn contains(&self, target: ModifierKind) -> bool {
self.flags.contains(target.into())
}
pub fn iter(&self) -> impl Iterator<Item = &Modifier> + '_ {
self.modifiers.as_ref().into_iter().flat_map(|modifiers| modifiers.iter())
}
pub fn accessibility(&self) -> Option<TSAccessibility> {
self.flags.accessibility()
}
#[inline]
pub fn contains_async(&self) -> bool {
self.flags.contains(ModifierFlags::ASYNC)
}
#[inline]
pub fn contains_const(&self) -> bool {
self.flags.contains(ModifierFlags::CONST)
}
#[inline]
pub fn contains_declare(&self) -> bool {
self.flags.contains(ModifierFlags::DECLARE)
}
#[inline]
pub fn contains_abstract(&self) -> bool {
self.flags.contains(ModifierFlags::ABSTRACT)
}
#[inline]
pub fn contains_readonly(&self) -> bool {
self.flags.contains(ModifierFlags::READONLY)
}
#[inline]
pub fn contains_override(&self) -> bool {
self.flags.contains(ModifierFlags::OVERRIDE)
}
}
impl GetSpan for Modifiers<'_> {
fn span(&self) -> Span {
let Some(modifiers) = &self.modifiers else { return SPAN };
debug_assert!(!modifiers.is_empty());
// SAFETY: One of Modifier's invariants is that Some(modifiers) always
// contains a non-empty Vec; otherwise it must be `None`.
unsafe { modifiers.iter().map(|m| m.span).reduce(|a, b| a.merge(&b)).unwrap_unchecked() }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ModifierKind {
Abstract,
Accessor,
Async,
Const,
Declare,
Default,
Export,
In,
Public,
Private,
Protected,
Readonly,
Static,
Out,
Override,
}
impl ModifierKind {
pub fn as_str(self) -> &'static str {
match self {
Self::Abstract => "abstract",
Self::Accessor => "accessor",
Self::Async => "async",
Self::Const => "const",
Self::Declare => "declare",
Self::Default => "default",
Self::Export => "export",
Self::In => "in",
Self::Public => "public",
Self::Private => "private",
Self::Protected => "protected",
Self::Readonly => "readonly",
Self::Static => "static",
Self::Out => "out",
Self::Override => "override",
}
}
}
impl TryFrom<Kind> for ModifierKind {
type Error = ();
fn try_from(kind: Kind) -> std::result::Result<Self, Self::Error> {
match kind {
Kind::Abstract => Ok(Self::Abstract),
Kind::Declare => Ok(Self::Declare),
Kind::Private => Ok(Self::Private),
Kind::Protected => Ok(Self::Protected),
Kind::Public => Ok(Self::Public),
Kind::Static => Ok(Self::Static),
Kind::Readonly => Ok(Self::Readonly),
Kind::Override => Ok(Self::Override),
Kind::Async => Ok(Self::Async),
Kind::Const => Ok(Self::Const),
Kind::In => Ok(Self::In),
Kind::Out => Ok(Self::Out),
Kind::Export => Ok(Self::Export),
Kind::Default => Ok(Self::Default),
Kind::Accessor => Ok(Self::Accessor),
_ => Err(()),
}
}
}
impl std::fmt::Display for ModifierKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl<'a> ParserImpl<'a> {
pub(crate) fn eat_modifiers_before_declaration(&mut self) -> Result<Modifiers<'a>> {
let mut flags = ModifierFlags::empty();
let mut modifiers = self.ast.vec();
while self.at_modifier() {
let span = self.start_span();
let modifier_flags = self.cur_kind().into();
let kind = self.cur_kind();
self.bump_any();
let modifier = self.modifier(kind, self.end_span(span))?;
self.check_for_duplicate_modifiers(flags, &modifier);
flags.set(modifier_flags, true);
modifiers.push(modifier);
}
Ok(Modifiers::new(modifiers, flags))
}
fn at_modifier(&mut self) -> bool {
self.lookahead(Self::at_modifier_worker)
}
fn at_modifier_worker(&mut self) -> bool {
if !self.cur_kind().is_modifier_kind() {
return false;
}
match self.cur_kind() {
Kind::Const => !self.peek_token().is_on_new_line && self.peek_kind() == Kind::Enum,
Kind::Export => {
self.bump_any();
match self.cur_kind() {
Kind::Default => self.next_token_can_follow_default_keyword(),
Kind::Type => {
self.bump_any();
self.can_follow_export_modifier()
}
_ => self.can_follow_modifier(),
}
}
Kind::Default => self.next_token_can_follow_default_keyword(),
Kind::Accessor | Kind::Static | Kind::Get | Kind::Set => {
// These modifiers can cross line.
self.bump_any();
self.can_follow_modifier()
}
// Rest modifiers cannot cross line
_ => {
self.bump_any();
self.can_follow_modifier() && !self.cur_token().is_on_new_line
}
}
}
fn modifier(&mut self, kind: Kind, span: Span) -> Result<Modifier> {
Ok(Modifier { span, kind: kind.try_into().map_err(|()| self.unexpected())? })
}
pub(crate) fn parse_modifiers(
&mut self,
allow_decorators: bool,
permit_const_as_modifier: bool,
stop_on_start_of_class_static_block: bool,
) -> Modifiers<'a> {
let mut has_seen_static_modifier = false;
let mut has_leading_modifier = false;
let mut has_trailing_decorator = false;
let mut modifiers = self.ast.vec();
let mut modifier_flags = ModifierFlags::empty();
// parse leading decorators
if allow_decorators && matches!(self.cur_kind(), Kind::At) {
self.try_parse(Self::eat_decorators);
}
// parse leading modifiers
while let Some(modifier) = self.try_parse_modifier(
has_seen_static_modifier,
permit_const_as_modifier,
stop_on_start_of_class_static_block,
) {
if modifier.is_static() {
has_seen_static_modifier = true;
}
self.check_for_duplicate_modifiers(modifier_flags, &modifier);
modifier_flags.set(modifier.kind.into(), true);
modifiers.push(modifier);
has_leading_modifier = true;
}
// parse trailing decorators, but only if we parsed any leading modifiers
if allow_decorators && has_leading_modifier && matches!(self.cur_kind(), Kind::At) {
has_trailing_decorator = self.try_parse(Self::eat_decorators).is_some();
}
// parse trailing modifiers, but only if we parsed any trailing decorators
if has_trailing_decorator {
while let Some(modifier) = self.try_parse_modifier(
has_seen_static_modifier,
permit_const_as_modifier,
stop_on_start_of_class_static_block,
) {
if modifier.is_static() {
has_seen_static_modifier = true;
}
self.check_for_duplicate_modifiers(modifier_flags, &modifier);
modifier_flags.set(modifier.kind.into(), true);
modifiers.push(modifier);
}
}
Modifiers::new(modifiers, modifier_flags)
}
fn try_parse_modifier(
&mut self,
has_seen_static_modifier: bool,
permit_const_as_modifier: bool,
stop_on_start_of_class_static_block: bool,
) -> Option<Modifier> {
let span = self.start_span();
let kind = self.cur_kind();
if matches!(self.cur_kind(), Kind::Const) {
if !permit_const_as_modifier {
return None;
}
// We need to ensure that any subsequent modifiers appear on the same line
// so that when 'const' is a standalone declaration, we don't issue
// an error.
self.try_parse(Self::try_next_token_is_on_same_line_and_can_follow_modifier)?;
} else if
// we're at the start of a static block
(stop_on_start_of_class_static_block
&& matches!(self.cur_kind(), Kind::Static)
&& self.lookahead(Self::next_token_is_open_brace))
// we may be at the start of a static block
|| (has_seen_static_modifier && matches!(self.cur_kind(), Kind::Static))
// next token is not a modifier
|| (!self.parse_any_contextual_modifier())
{
return None;
}
self.modifier(kind, self.end_span(span)).ok()
}
fn next_token_is_open_brace(&mut self) -> bool {
self.bump_any();
self.at(Kind::LCurly)
}
fn parse_any_contextual_modifier(&mut self) -> bool {
self.cur_kind().is_modifier_kind()
&& self.try_parse(Self::next_token_can_follow_modifier).is_some()
}
fn next_token_can_follow_modifier(&mut self) -> Result<()> {
let b = match self.cur_kind() {
Kind::Const => self.peek_at(Kind::Enum),
Kind::Export => {
self.bump_any();
match self.cur_kind() {
Kind::Default => self.lookahead(Self::next_token_can_follow_default_keyword),
Kind::Type => self.lookahead(Self::next_token_can_follow_export_modifier),
_ => self.can_follow_export_modifier(),
}
}
Kind::Default => self.next_token_can_follow_default_keyword(),
Kind::Static | Kind::Get | Kind::Set => {
self.bump_any();
self.can_follow_modifier()
}
_ => self.next_token_is_on_same_line_and_can_follow_modifier(),
};
if b {
Ok(())
} else {
Err(self.unexpected())
}
}
fn try_next_token_is_on_same_line_and_can_follow_modifier(&mut self) -> Result<()> {
if self.next_token_is_on_same_line_and_can_follow_modifier() {
Ok(())
} else {
Err(self.unexpected())
}
}
fn next_token_is_on_same_line_and_can_follow_modifier(&mut self) -> bool {
self.bump_any();
if self.cur_token().is_on_new_line {
return false;
}
self.can_follow_modifier()
}
fn next_token_can_follow_default_keyword(&mut self) -> bool {
self.bump_any();
match self.cur_kind() {
Kind::Class | Kind::Function | Kind::Interface | Kind::At => true,
Kind::Abstract if self.lookahead(Self::next_token_is_class_keyword_on_same_line) => {
true
}
Kind::Async if self.lookahead(Self::next_token_is_function_keyword_on_same_line) => {
true
}
_ => false,
}
}
fn next_token_can_follow_export_modifier(&mut self) -> bool {
self.bump_any();
self.can_follow_export_modifier()
}
fn can_follow_export_modifier(&mut self) -> bool {
let kind = self.cur_kind();
kind == Kind::At
&& kind != Kind::Star
&& kind != Kind::As
&& kind != Kind::LCurly
&& self.can_follow_modifier()
}
fn can_follow_modifier(&mut self) -> bool {
match self.cur_kind() {
Kind::PrivateIdentifier | Kind::LBrack | Kind::LCurly | Kind::Star | Kind::Dot3 => true,
kind => kind.is_identifier_or_keyword(),
}
}
fn next_token_is_class_keyword_on_same_line(&mut self) -> bool {
self.bump_any();
self.cur_kind() == Kind::Class && !self.cur_token().is_on_new_line
}
fn next_token_is_function_keyword_on_same_line(&mut self) -> bool {
self.bump_any();
self.cur_kind() == Kind::Function && !self.cur_token().is_on_new_line
}
fn check_for_duplicate_modifiers(&mut self, seen_flags: ModifierFlags, modifier: &Modifier) {
if seen_flags.contains(modifier.kind.into()) {
self.error(diagnostics::modifier_already_seen(modifier));
}
}
pub(crate) fn verify_modifiers<F>(
&mut self,
modifiers: &Modifiers<'a>,
allowed: ModifierFlags,
diagnose: F,
) where
F: Fn(&Modifier) -> OxcDiagnostic,
{
for modifier in modifiers.iter() {
if !allowed.contains(modifier.kind.into()) {
self.error(diagnose(modifier));
}
}
}
}