use crate::ast::{
Arrow, ArrowBody, BindingTarget, Class, ClassMember, Expr, ForLeft, Function, MethodKind,
ObjectMember, Param, Program, PropertyKey, SourceType, Stmt, UnaryOp, VarDecl, VarDeclKind,
};
use crate::common::Span;
use crate::error::{Error, Result};
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
pub(crate) fn validate_program(program: &Program) -> Result<()> {
validate_program_with(program, false, false, false, false)
}
pub(crate) fn validate_program_with(
program: &Program,
allow_super_property: bool,
allow_super_call: bool,
allow_new_target: bool,
inherited_strict: bool,
) -> Result<()> {
let strict = inherited_strict
|| program.source_type == SourceType::Module
|| body_is_strict(&program.body);
let mut v = Validator {
private_scopes: Vec::new(),
labels: Vec::new(),
is_module: program.source_type == SourceType::Module,
in_assign_target: false,
};
let ctx = Ctx {
allow_super_property,
allow_super_call,
allow_new_target,
..Ctx::top(strict)
};
v.check_top_level_scope(&program.body, strict)?;
if program.source_type == SourceType::Script {
for stmt in &program.body {
if let Stmt::Var(decl) = stmt
&& matches!(decl.kind, VarDeclKind::Using | VarDeclKind::AwaitUsing)
{
return Err(v.err(
decl.span,
"a `using` declaration is not allowed at the top level of a script",
));
}
}
}
for stmt in &program.body {
v.stmt(stmt, &ctx)?;
}
if v.is_module {
v.check_module_exports(&program.body)?;
}
Ok(())
}
fn body_is_strict(body: &[Stmt]) -> bool {
for stmt in body {
if let Stmt::Expr { expression, .. } = stmt {
if let Expr::Str { value, span, .. } = &**expression {
if &**value == b"use strict" && span.end.saturating_sub(span.start) == 12 {
return true;
}
} else {
break;
}
} else {
break;
}
}
false
}
#[derive(Clone, Copy)]
struct Ctx {
strict: bool,
allow_super_call: bool,
allow_super_property: bool,
in_field_init: bool,
in_function: bool,
allow_new_target: bool,
in_breakable: bool,
in_iteration: bool,
in_static_block: bool,
}
impl Ctx {
fn top(strict: bool) -> Self {
Ctx {
strict,
allow_super_call: false,
allow_super_property: false,
in_field_init: false,
in_function: false,
allow_new_target: false,
in_breakable: false,
in_iteration: false,
in_static_block: false,
}
}
fn function_boundary(
strict: bool,
allow_super_call: bool,
allow_super_property: bool,
in_field_init: bool,
) -> Self {
Ctx {
strict,
allow_super_call,
allow_super_property,
in_field_init,
in_function: true,
allow_new_target: true,
in_breakable: false,
in_iteration: false,
in_static_block: false,
}
}
}
struct Validator {
private_scopes: Vec<Vec<Box<str>>>,
labels: Vec<(Box<str>, bool)>,
is_module: bool,
in_assign_target: bool,
}
#[derive(Clone, Copy, PartialEq)]
enum PrivKind {
Field,
Method,
Get,
Set,
}
impl Validator {
fn err(&self, span: Span, msg: &str) -> Error {
Error::syntax(msg, span)
}
fn stmt(&mut self, stmt: &Stmt, ctx: &Ctx) -> Result<()> {
match stmt {
Stmt::Expr { expression, .. } => self.expr(expression, ctx),
Stmt::Block { body, .. } => {
self.check_lexical_scope(body, ctx.strict)?;
for s in body {
self.stmt(s, ctx)?;
}
Ok(())
}
Stmt::Empty { .. } | Stmt::Debugger { .. } => Ok(()),
Stmt::Var(decl) => self.var_decl(decl, ctx),
Stmt::If {
test,
consequent,
alternate,
..
} => {
self.expr(test, ctx)?;
self.check_substatement(consequent, ctx)?;
self.stmt(consequent, ctx)?;
if let Some(a) = alternate {
self.check_substatement(a, ctx)?;
self.stmt(a, ctx)?;
}
Ok(())
}
Stmt::For {
init,
test,
update,
body,
..
} => {
if let Some(init) = init {
match init {
crate::ast::ForInit::Var(d) => {
self.var_decl(d, ctx)?;
if d.kind != VarDeclKind::Var {
let mut bound = Vec::new();
for decl in &d.declarations {
collect_bound_names(&decl.target, &mut bound);
}
self.check_no_var_redeclare(&bound, body)?;
}
}
crate::ast::ForInit::Expr(e) => self.expr(e, ctx)?,
}
}
if let Some(t) = test {
self.expr(t, ctx)?;
}
if let Some(u) = update {
self.expr(u, ctx)?;
}
self.check_loop_body(body)?;
self.stmt(body, &loop_ctx(ctx))
}
Stmt::ForIn {
left, right, body, ..
}
| Stmt::ForOf {
left, right, body, ..
} => {
self.for_left(left, ctx)?;
if let ForLeft::Decl { kind, target, .. } = left
&& *kind != VarDeclKind::Var
{
let mut bound = Vec::new();
collect_bound_names(target, &mut bound);
self.check_no_dup_bound_names(&bound)?;
self.check_no_var_redeclare(&bound, body)?;
}
self.expr(right, ctx)?;
self.check_loop_body(body)?;
self.stmt(body, &loop_ctx(ctx))
}
Stmt::While { test, body, .. } => {
self.expr(test, ctx)?;
self.check_loop_body(body)?;
self.stmt(body, &loop_ctx(ctx))
}
Stmt::DoWhile { body, test, .. } => {
self.check_loop_body(body)?;
self.stmt(body, &loop_ctx(ctx))?;
self.expr(test, ctx)
}
Stmt::Switch {
discriminant,
cases,
..
} => {
self.expr(discriminant, ctx)?;
let all: Vec<&Stmt> = cases.iter().flat_map(|c| c.body.iter()).collect();
self.check_lexical_scope_refs(&all, false, ctx.strict)?;
let mut c = *ctx;
c.in_breakable = true;
for case in cases {
if let Some(t) = &case.test {
self.expr(t, ctx)?;
}
for s in &case.body {
if let Stmt::Var(decl) = s
&& matches!(decl.kind, VarDeclKind::Using | VarDeclKind::AwaitUsing)
{
return Err(self.err(
decl.span,
"a `using` declaration is not allowed directly in a `switch` case",
));
}
self.stmt(s, &c)?;
}
}
Ok(())
}
Stmt::Try {
block,
handler,
finalizer,
..
} => {
self.check_lexical_scope(block, ctx.strict)?;
for s in block {
self.stmt(s, ctx)?;
}
if let Some(h) = handler {
self.check_lexical_scope(&h.body, ctx.strict)?;
if let Some(p) = &h.param {
self.binding_target(p, ctx)?;
self.check_catch_param(p, &h.body)?;
}
for s in &h.body {
self.stmt(s, ctx)?;
}
}
if let Some(f) = finalizer {
self.check_lexical_scope(f, ctx.strict)?;
for s in f {
self.stmt(s, ctx)?;
}
}
Ok(())
}
Stmt::Return { argument, span } => {
if !ctx.in_function {
return Err(self.err(*span, "`return` is only valid inside a function"));
}
if let Some(a) = argument {
self.expr(a, ctx)?;
}
Ok(())
}
Stmt::Break { label, span } => {
match label {
Some(l) => {
if !self.labels.iter().any(|(n, _)| **n == *l.name) {
return Err(self.err(l.span, "undefined break label"));
}
}
None if !ctx.in_breakable => {
return Err(self.err(*span, "`break` must be inside a loop or `switch`"));
}
None => {}
}
Ok(())
}
Stmt::Continue { label, span } => {
match label {
Some(l) => {
match self.labels.iter().find(|(n, _)| **n == *l.name) {
None => return Err(self.err(l.span, "undefined continue label")),
Some((_, is_iter)) if !*is_iter => {
return Err(self.err(
l.span,
"`continue` label does not denote an iteration statement",
));
}
Some(_) => {}
}
}
None if !ctx.in_iteration => {
return Err(self.err(*span, "`continue` must be inside a loop"));
}
None => {}
}
Ok(())
}
Stmt::Throw { argument, .. } => self.expr(argument, ctx),
Stmt::Labeled { label, body, .. } => {
if ctx.strict && is_strict_reserved_word(&label.name) {
return Err(self.err(
label.span,
"a strict-mode reserved word may not be used as a label",
));
}
if self.labels.iter().any(|(n, _)| **n == *label.name) {
return Err(self.err(label.span, "label has already been declared"));
}
self.check_labeled_body(body, ctx)?;
let is_iter = labels_an_iteration(body);
self.labels.push((label.name.clone(), is_iter));
let result = self.stmt(body, ctx);
self.labels.pop();
result
}
Stmt::With { object, body, .. } => {
if ctx.strict {
return Err(self.err(stmt.span(), "`with` is not allowed in strict mode"));
}
self.expr(object, ctx)?;
self.reject_decl_substatement(
body, ctx, false, false,
)?;
self.stmt(body, ctx)
}
Stmt::Function(f) => self.function(f, ctx),
Stmt::Class(c) => self.class(c, ctx),
Stmt::Import(_) | Stmt::Export(_) => Ok(()),
}
}
fn check_substatement(&self, body: &Stmt, ctx: &Ctx) -> Result<()> {
self.reject_decl_substatement(
body, ctx, true, false,
)
}
fn check_loop_body(&self, body: &Stmt) -> Result<()> {
self.reject_decl_substatement(
body,
&Ctx::top(true),
false,
false,
)
}
fn check_labeled_body(&self, body: &Stmt, ctx: &Ctx) -> Result<()> {
self.reject_decl_substatement(
body, ctx, true, true,
)
}
fn reject_decl_substatement(
&self,
body: &Stmt,
ctx: &Ctx,
allow_sloppy_fn: bool,
allow_labelled_fn: bool,
) -> Result<()> {
match body {
Stmt::Var(decl) if decl.kind != VarDeclKind::Var => Err(self.err(
decl.span,
"lexical declarations may not appear in single-statement position",
)),
Stmt::Class(c) => Err(self.err(
c.span,
"a class declaration may not appear in single-statement position",
)),
Stmt::Function(f) => {
if f.is_generator || f.is_async || ctx.strict || !allow_sloppy_fn {
Err(self.err(
f.span,
"a function declaration may not appear in single-statement position",
))
} else {
Ok(())
}
}
Stmt::Labeled { body, .. } => {
if allow_labelled_fn && !ctx.strict {
self.reject_decl_substatement(body, ctx, allow_sloppy_fn, allow_labelled_fn)
} else if labels_a_function(body) {
Err(self.err(
body.span(),
"a labelled function declaration may not appear in \
single-statement position",
))
} else {
Ok(())
}
}
_ => Ok(()),
}
}
fn for_left(&mut self, left: &ForLeft, ctx: &Ctx) -> Result<()> {
match left {
ForLeft::Decl { kind, target, .. } => {
if *kind != VarDeclKind::Var {
self.check_lexical_binding_names(target)?;
}
self.binding_target(target, ctx)
}
ForLeft::Target(e) => {
if !is_valid_assign_target(e) {
return Err(self.err(e.span(), "invalid for-in/of assignment target"));
}
if ctx.strict {
self.check_not_eval_arguments(e)?;
}
self.in_assign_target = true;
self.expr(e, ctx)
}
}
}
fn check_no_var_redeclare(&self, bound: &[(String, Span)], body: &Stmt) -> Result<()> {
if bound.is_empty() {
return Ok(());
}
let mut vars = Vec::new();
collect_vars_only(body, &mut vars);
for (name, span) in bound {
if vars.iter().any(|v| v.as_ref() == name.as_str()) {
return Err(self.err(
*span,
"a lexical for-head binding may not be redeclared by a `var` \
in the loop body",
));
}
}
Ok(())
}
fn check_no_dup_bound_names(&self, bound: &[(String, Span)]) -> Result<()> {
for (i, (name, _)) in bound.iter().enumerate() {
if bound[..i].iter().any(|(n, _)| n == name) {
return Err(self.err(
bound[i].1,
"a for-in/of binding list may not contain a duplicate name",
));
}
}
Ok(())
}
fn var_decl(&mut self, decl: &VarDecl, ctx: &Ctx) -> Result<()> {
for d in &decl.declarations {
if decl.kind != VarDeclKind::Var {
self.check_lexical_binding_names(&d.target)?;
}
if let Some(init) = &d.init {
self.expr(init, ctx)?;
}
self.binding_target(&d.target, ctx)?;
}
Ok(())
}
fn check_binding_ident_name(&self, name: &str, span: Span) -> Result<()> {
if name == "eval" || name == "arguments" {
return Err(self.err(
span,
"`eval` and `arguments` may not be bound in strict mode",
));
}
if is_strict_reserved_word(name) {
return Err(self.err(
span,
"a strict-mode reserved word may not be bound in strict mode",
));
}
Ok(())
}
fn check_lexical_binding_names(&self, target: &BindingTarget) -> Result<()> {
let mut names = Vec::new();
collect_bound_names(target, &mut names);
for (name, span) in names {
if name == "let" {
return Err(self.err(span, "`let` is not a valid lexical binding name"));
}
}
Ok(())
}
fn binding_target(&mut self, target: &BindingTarget, ctx: &Ctx) -> Result<()> {
match target {
BindingTarget::Ident(id) => {
if ctx.strict {
self.check_binding_ident_name(&id.name, id.span)?;
}
Ok(())
}
BindingTarget::Array(p) => {
for el in &p.elements {
match el {
crate::ast::ArrayPatternElement::Hole => {}
crate::ast::ArrayPatternElement::Item {
target, default, ..
} => {
self.binding_target(target, ctx)?;
if let Some(d) = default {
self.expr(d, ctx)?;
}
}
crate::ast::ArrayPatternElement::Rest { target, .. } => {
self.binding_target(target, ctx)?;
}
}
}
Ok(())
}
BindingTarget::Object(p) => {
for prop in &p.properties {
if let PropertyKey::Computed(e) = &prop.key {
self.expr(e, ctx)?;
}
self.binding_target(&prop.value, ctx)?;
if let Some(d) = &prop.default {
self.expr(d, ctx)?;
}
}
if let Some(r) = &p.rest {
self.binding_target(r, ctx)?;
}
Ok(())
}
}
}
fn function(&mut self, f: &Function, ctx: &Ctx) -> Result<()> {
let strict = ctx.strict || body_is_strict(&f.body);
if strict && let Some(id) = &f.id {
self.check_binding_ident_name(&id.name, id.span)?;
}
self.check_param_yield_await(&f.params, f.is_generator, f.is_async)?;
self.check_params(&f.params, strict, false, &f.body)?;
let c = Ctx::function_boundary(strict, false, false, false);
let saved_labels = core::mem::take(&mut self.labels);
for p in &f.params {
self.param(p, &c)?;
}
self.check_top_level_scope(&f.body, strict)?;
self.check_params_vs_body_lexical(&f.params, &f.body)?;
for s in &f.body {
self.stmt(s, &c)?;
}
self.labels = saved_labels;
Ok(())
}
fn method_function(
&mut self,
f: &Function,
parent: &Ctx,
allow_super_call: bool,
) -> Result<()> {
let strict = parent.strict || body_is_strict(&f.body);
self.check_param_yield_await(&f.params, f.is_generator, f.is_async)?;
self.check_params(&f.params, strict, true, &f.body)?;
let c = Ctx::function_boundary(
strict,
allow_super_call,
true,
false,
);
let saved_labels = core::mem::take(&mut self.labels);
for p in &f.params {
self.param(p, &c)?;
}
self.check_top_level_scope(&f.body, strict)?;
self.check_params_vs_body_lexical(&f.params, &f.body)?;
for s in &f.body {
self.stmt(s, &c)?;
}
self.labels = saved_labels;
Ok(())
}
fn arrow(&mut self, a: &Arrow, ctx: &Ctx) -> Result<()> {
let strict = ctx.strict
|| match &a.body {
ArrowBody::Block(body) => body_is_strict(body),
ArrowBody::Expr(_) => false,
};
let body_slice: &[Stmt] = match &a.body {
ArrowBody::Block(body) => body,
ArrowBody::Expr(_) => &[],
};
self.check_param_yield_await(
&a.params, true, true,
)?;
self.check_params(
&a.params, strict, true, body_slice,
)?;
let c = Ctx {
strict,
allow_super_call: ctx.allow_super_call,
allow_super_property: ctx.allow_super_property,
in_field_init: ctx.in_field_init,
in_function: true,
allow_new_target: ctx.allow_new_target,
in_breakable: false,
in_iteration: false,
in_static_block: ctx.in_static_block && !a.is_async,
};
let saved_labels = core::mem::take(&mut self.labels);
for p in &a.params {
self.param(p, &c)?;
}
match &a.body {
ArrowBody::Block(body) => {
self.check_top_level_scope(body, strict)?;
self.check_params_vs_body_lexical(&a.params, body)?;
for s in body {
self.stmt(s, &c)?;
}
}
ArrowBody::Expr(e) => self.expr(e, &c)?,
}
self.labels = saved_labels;
Ok(())
}
fn check_param_yield_await(
&self,
params: &[Param],
detect_yield: bool,
detect_await: bool,
) -> Result<()> {
if !detect_yield && !detect_await {
return Ok(());
}
for p in params {
if let Some((span, kind)) = first_param_yield_await(p, detect_yield, detect_await) {
return Err(self.err(
span,
match kind {
YieldOrAwait::Yield => {
"`yield` is not allowed in a generator's parameter list"
}
YieldOrAwait::Await => {
"`await` is not allowed in an async function's parameter list"
}
},
));
}
}
Ok(())
}
fn param(&mut self, p: &Param, ctx: &Ctx) -> Result<()> {
if let Some(d) = &p.default {
self.expr(d, ctx)?;
}
self.binding_target(&p.target, ctx)
}
fn check_accessor_arity(&self, is_getter: bool, f: &Function) -> Result<()> {
if is_getter {
if !f.params.is_empty() {
return Err(self.err(f.span, "a getter may not declare any parameters"));
}
} else if f.params.len() != 1 || f.params[0].rest {
return Err(self.err(
f.span,
"a setter must declare exactly one (non-rest) parameter",
));
}
Ok(())
}
fn check_params(
&self,
params: &[Param],
strict: bool,
unique_required: bool,
body: &[Stmt],
) -> Result<()> {
let simple = params
.iter()
.all(|p| !p.rest && p.default.is_none() && matches!(p.target, BindingTarget::Ident(_)));
if !simple && body_is_strict(body) {
let span = body.first().map_or(Span::new(0, 0), Stmt::span);
return Err(self.err(
span,
"a `\"use strict\"` directive is not allowed in a function with a non-simple parameter list",
));
}
if simple && !strict && !unique_required {
return Ok(());
}
let mut names: Vec<(String, Span)> = Vec::new();
for p in params {
let mut bound = Vec::new();
collect_bound_names(&p.target, &mut bound);
for (name, span) in bound {
if names.iter().any(|(n, _)| *n == name) {
return Err(self.err(span, "duplicate parameter name not allowed here"));
}
names.push((name, span));
}
}
Ok(())
}
fn class(&mut self, c: &Class, ctx: &Ctx) -> Result<()> {
if let Some(id) = &c.id {
self.check_binding_ident_name(&id.name, id.span)?;
}
let mut cls_ctx = Ctx::top(true);
cls_ctx.in_field_init = ctx.in_field_init;
if let Some(sc) = &c.super_class {
self.expr(sc, &cls_ctx)?;
}
let mut privates: Vec<Box<str>> = Vec::new();
let mut seen: Vec<(Box<str>, bool, PrivKind)> = Vec::new();
let mut ctor_count = 0u32;
for member in &c.body {
match member {
ClassMember::Method(m) => {
if let PropertyKey::Private(name) = &m.key {
if &**name == "constructor" {
return Err(
self.err(m.span, "`#constructor` is not a valid private name")
);
}
privates.push(name.clone());
let pk = match m.kind {
MethodKind::Get => PrivKind::Get,
MethodKind::Set => PrivKind::Set,
_ => PrivKind::Method,
};
self.check_private_dup(&mut seen, name, m.is_static, pk, m.span)?;
}
if matches!(m.kind, MethodKind::Constructor) {
ctor_count += 1;
if ctor_count > 1 {
return Err(self.err(m.span, "a class may have only one constructor"));
}
}
if m.is_static && key_is_named(&m.key, "prototype") {
return Err(
self.err(m.span, "a static class member may not be named `prototype`")
);
}
if !m.is_static
&& key_is_named(&m.key, "constructor")
&& (matches!(m.kind, MethodKind::Get | MethodKind::Set)
|| m.value.is_async
|| m.value.is_generator)
{
return Err(self.err(
m.span,
"class `constructor` may not be an accessor, generator, or async method",
));
}
}
ClassMember::Field(field) => {
if let PropertyKey::Private(name) = &field.key {
if &**name == "constructor" {
return Err(
self.err(field.span, "`#constructor` is not a valid private name")
);
}
privates.push(name.clone());
self.check_private_dup(
&mut seen,
name,
field.is_static,
PrivKind::Field,
field.span,
)?;
}
if key_is_named(&field.key, "constructor") {
return Err(
self.err(field.span, "a class field may not be named `constructor`")
);
}
if field.is_static && key_is_named(&field.key, "prototype") {
return Err(self.err(
field.span,
"a static class field may not be named `prototype`",
));
}
}
ClassMember::StaticBlock { .. } => {}
}
}
self.private_scopes.push(privates);
let has_heritage = c.super_class.is_some();
let result = self.class_members(c, &cls_ctx, has_heritage);
self.private_scopes.pop();
result
}
fn class_members(&mut self, c: &Class, ctx: &Ctx, has_heritage: bool) -> Result<()> {
for member in &c.body {
match member {
ClassMember::Method(m) => {
if let PropertyKey::Computed(e) = &m.key {
self.expr(e, ctx)?;
}
match m.kind {
MethodKind::Get => self.check_accessor_arity(true, &m.value)?,
MethodKind::Set => self.check_accessor_arity(false, &m.value)?,
_ => {}
}
let allow_super_call =
has_heritage && matches!(m.kind, MethodKind::Constructor);
self.method_function(&m.value, ctx, allow_super_call)?;
}
ClassMember::Field(field) => {
if let PropertyKey::Computed(e) = &field.key {
self.expr(e, ctx)?;
}
if let Some(v) = &field.value {
let mut c2 = Ctx::function_boundary(
true, false, true, true,
);
c2.in_function = false;
self.expr(v, &c2)?;
}
}
ClassMember::StaticBlock { body, .. } => {
let mut c2 =
Ctx::function_boundary(true, false, true, true);
c2.in_function = false;
c2.in_static_block = true;
let saved = core::mem::take(&mut self.labels);
self.check_top_level_scope(body, true)?;
for s in body {
self.stmt(s, &c2)?;
}
self.labels = saved;
}
}
}
Ok(())
}
fn check_private_dup(
&self,
seen: &mut Vec<(Box<str>, bool, PrivKind)>,
name: &str,
is_static: bool,
kind: PrivKind,
span: Span,
) -> Result<()> {
for (n, s, k) in seen.iter() {
if &**n != name {
continue;
}
let pair_ok = *s == is_static
&& matches!(
(*k, kind),
(PrivKind::Get, PrivKind::Set) | (PrivKind::Set, PrivKind::Get)
);
if !pair_ok {
return Err(self.err(span, "duplicate private name in class body"));
}
}
seen.push((name.into(), is_static, kind));
Ok(())
}
fn private_in_scope(&self, name: &str) -> bool {
self.private_scopes
.iter()
.any(|frame| frame.iter().any(|n| &**n == name))
}
fn expr(&mut self, e: &Expr, ctx: &Ctx) -> Result<()> {
let in_assign_target = core::mem::take(&mut self.in_assign_target);
match e {
Expr::Number {
legacy_octal: true,
span,
..
} if ctx.strict => Err(self.err(
*span,
"legacy octal / non-octal-decimal literals are not allowed in strict mode",
)),
Expr::Str {
legacy_octal: true,
span,
..
} if ctx.strict => Err(self.err(
*span,
"legacy octal / non-octal escape sequences are not allowed in strict mode",
)),
Expr::Null(_)
| Expr::Bool { .. }
| Expr::Number { .. }
| Expr::BigInt { .. }
| Expr::Str { .. }
| Expr::Regex { .. }
| Expr::This(_) => Ok(()),
Expr::NewTarget(span) => {
if !ctx.allow_new_target {
return Err(self.err(*span, "`new.target` is only valid inside a function"));
}
Ok(())
}
Expr::Ident(id) => {
if ctx.in_field_init && &*id.name == "arguments" {
return Err(self.err(
id.span,
"`arguments` is not allowed in a class field initializer or static block",
));
}
if ctx.strict && is_strict_reserved_word(&id.name) {
return Err(self.err(
id.span,
"a strict-mode reserved word may not be used as an identifier",
));
}
Ok(())
}
Expr::Super(span) => {
if !ctx.allow_super_property && !ctx.allow_super_call {
return Err(self.err(
*span,
"`super` is only valid inside a method or a derived class constructor",
));
}
Ok(())
}
Expr::PrivateName(_, span) => {
Err(self.err(
*span,
"a private name is only allowed as the left operand of `in`",
))
}
Expr::Template(t) => {
for x in &t.expressions {
self.expr(x, ctx)?;
}
Ok(())
}
Expr::TaggedTemplate { tag, quasi, .. } => {
self.expr(tag, ctx)?;
for x in &quasi.expressions {
self.expr(x, ctx)?;
}
Ok(())
}
Expr::Array { elements, .. } => {
for el in elements {
match el {
crate::ast::ArrayElement::Hole => {}
crate::ast::ArrayElement::Item(x) | crate::ast::ArrayElement::Spread(x) => {
self.in_assign_target = in_assign_target;
self.expr(x, ctx)?
}
}
}
Ok(())
}
Expr::Object { members, .. } => {
if !in_assign_target {
let mut proto_data = 0u32;
for m in members {
if let ObjectMember::Property {
key,
value,
shorthand: false,
span,
..
} = m
&& !matches!(key, PropertyKey::Computed(_))
&& !matches!(&**value, Expr::Function(_) | Expr::Arrow(_))
&& key_is_named(key, "__proto__")
{
proto_data += 1;
if proto_data > 1 {
return Err(self.err(
*span,
"an object literal may not have more than one \
`__proto__` property",
));
}
}
}
}
for m in members {
if let ObjectMember::Property {
key: PropertyKey::Private(_),
span,
..
}
| ObjectMember::Accessor {
key: PropertyKey::Private(_),
span,
..
} = m
{
return Err(
self.err(*span, "a private name is only valid as a class member")
);
}
match m {
ObjectMember::Property {
key,
value,
shorthand,
method,
span,
} => {
if let PropertyKey::Computed(k) = key {
self.expr(k, ctx)?;
}
if *method && let Expr::Function(f) = &**value {
self.method_function(f, ctx, false)?;
continue;
}
let is_cover = *shorthand
&& matches!(
&**value,
Expr::Assign {
op: crate::ast::AssignOp::Assign,
..
}
);
if is_cover && !in_assign_target {
return Err(self.err(
*span,
"a shorthand property with an initializer (`{ a = … }`) is \
only valid as a destructuring assignment target",
));
}
if in_assign_target && !is_cover {
self.in_assign_target = true;
}
self.expr(value, ctx)?;
}
ObjectMember::Spread { value, .. } => {
self.in_assign_target = in_assign_target;
self.expr(value, ctx)?
}
ObjectMember::Accessor {
key,
value,
is_getter,
..
} => {
if let PropertyKey::Computed(k) = key {
self.expr(k, ctx)?;
}
self.check_accessor_arity(*is_getter, value)?;
self.method_function(value, ctx, false)?;
}
}
}
Ok(())
}
Expr::Member {
object, property, ..
} => {
if let (Expr::Super(_), PropertyKey::Private(_)) = (&**object, property) {
return Err(self.err(
e.span(),
"private names may not be accessed through `super`",
));
}
if !self.is_module
&& let Expr::Ident(id) = &**object
&& &*id.name == "import"
&& matches!(property, PropertyKey::Ident(n) if &**n == "meta")
{
return Err(self.err(e.span(), "`import.meta` is only allowed in a module"));
}
self.expr(object, ctx)?;
if let PropertyKey::Private(name) = property
&& !self.private_in_scope(name)
{
return Err(self.err(e.span(), "reference to undeclared private name"));
}
if let PropertyKey::Computed(k) = property {
self.expr(k, ctx)?;
}
Ok(())
}
Expr::Call {
callee, arguments, ..
} => {
if let Expr::Super(span) = &**callee
&& !ctx.allow_super_call
{
return Err(self.err(
*span,
"`super()` is only valid in a derived class constructor",
));
}
self.expr(callee, ctx)?;
for a in arguments {
match a {
crate::ast::Argument::Item(x) | crate::ast::Argument::Spread(x) => {
self.expr(x, ctx)?
}
}
}
Ok(())
}
Expr::New {
callee, arguments, ..
} => {
self.expr(callee, ctx)?;
for a in arguments {
match a {
crate::ast::Argument::Item(x) | crate::ast::Argument::Spread(x) => {
self.expr(x, ctx)?
}
}
}
Ok(())
}
Expr::OptChain { expr, .. } => self.expr(expr, ctx),
Expr::Unary { op, argument, .. } => {
if matches!(op, UnaryOp::Delete) {
if contains_private_ref(argument) {
return Err(
self.err(e.span(), "`delete` of a private member is not allowed")
);
}
if ctx.strict && matches!(&**argument, Expr::Ident(_)) {
return Err(self.err(
e.span(),
"`delete` of an unqualified identifier is not allowed in strict mode",
));
}
}
self.expr(argument, ctx)
}
Expr::Update { argument, .. } => {
if !is_simple_update_target(argument) {
return Err(self.err(argument.span(), "invalid operand for `++`/`--`"));
}
if ctx.strict {
self.check_not_eval_arguments(argument)?;
}
self.expr(argument, ctx)
}
Expr::Binary { .. } | Expr::Logical { .. } => {
let mut node = e;
loop {
match node {
Expr::Binary {
op: crate::ast::BinaryOp::In,
left,
right,
..
} if matches!(&**left, Expr::PrivateName(..)) => {
if let Expr::PrivateName(name, span) = &**left
&& !self.private_in_scope(name)
{
return Err(self.err(*span, "reference to undeclared private name"));
}
break self.expr(right, ctx);
}
Expr::Binary { left, right, .. } | Expr::Logical { left, right, .. } => {
self.expr(right, ctx)?;
node = left;
}
other => break self.expr(other, ctx),
}
}
}
Expr::Conditional {
test,
consequent,
alternate,
..
} => {
self.expr(test, ctx)?;
self.expr(consequent, ctx)?;
self.expr(alternate, ctx)
}
Expr::Assign {
op, target, value, ..
} => {
let simple_assign = matches!(op, crate::ast::AssignOp::Assign);
if simple_assign {
if !is_valid_assign_target(target) {
return Err(self.err(target.span(), "invalid assignment target"));
}
} else if !is_simple_update_target(target) {
return Err(self.err(target.span(), "invalid target for compound assignment"));
}
if ctx.strict {
self.check_not_eval_arguments(target)?;
}
self.in_assign_target = simple_assign;
self.expr(target, ctx)?;
self.expr(value, ctx)
}
Expr::Sequence { expressions, .. } => {
for x in expressions {
self.expr(x, ctx)?;
}
Ok(())
}
Expr::Function(f) => self.function(f, ctx),
Expr::Arrow(a) => self.arrow(a, ctx),
Expr::Class(c) => self.class(c, ctx),
Expr::Yield { argument, .. } => {
if let Some(a) = argument {
self.expr(a, ctx)?;
}
Ok(())
}
Expr::Await { argument, span } => {
if ctx.in_static_block {
return Err(self.err(
*span,
"`await` is not allowed in a class static initialization block",
));
}
self.expr(argument, ctx)
}
}
}
fn check_lexical_scope(&self, body: &[Stmt], strict: bool) -> Result<()> {
let refs: Vec<&Stmt> = body.iter().collect();
self.check_lexical_scope_refs(&refs, false, strict)
}
fn check_top_level_scope(&self, body: &[Stmt], strict: bool) -> Result<()> {
let refs: Vec<&Stmt> = body.iter().collect();
self.check_lexical_scope_refs(&refs, true, strict)
}
fn check_module_exports(&self, body: &[Stmt]) -> Result<()> {
use crate::ast::ExportDecl;
let mut top_lex: Vec<(Box<str>, Span)> = Vec::new();
for stmt in body {
collect_module_top_lexical(stmt, &mut top_lex);
}
for i in 0..top_lex.len() {
for j in (i + 1)..top_lex.len() {
if top_lex[i].0 == top_lex[j].0 {
return Err(self.err(top_lex[j].1, "duplicate top-level declaration in module"));
}
}
}
let mut var_only: Vec<Box<str>> = Vec::new();
for stmt in body {
collect_vars_only(stmt, &mut var_only);
}
for (name, span) in &top_lex {
if var_only.iter().any(|v| v == name) {
return Err(self.err(
*span,
"a module top-level lexical declaration conflicts with a `var` of the same name",
));
}
}
let mut declared: Vec<Box<str>> = Vec::new();
let mut lexical: Vec<(Box<str>, Span, bool)> = Vec::new();
let mut vars: Vec<Box<str>> = Vec::new();
for stmt in body {
collect_top_level_decls(stmt, &mut lexical, &mut vars, true);
if let Stmt::Import(decl) = stmt {
for s in &decl.specifiers {
let (name, span) = match s {
crate::ast::ImportSpecifier::Default(id)
| crate::ast::ImportSpecifier::Namespace(id) => (id.name.clone(), id.span),
crate::ast::ImportSpecifier::Named { local, .. } => {
(local.name.clone(), local.span)
}
};
if &*name == "eval" || &*name == "arguments" {
return Err(self.err(
span,
"an imported binding may not be named `eval` or `arguments` in strict (module) code",
));
}
declared.push(name);
}
}
if let Stmt::Export(ExportDecl::Default { .. }) = stmt {
declared.push("*default*".into());
}
}
declared.extend(vars);
declared.extend(lexical.into_iter().map(|(n, _, _)| n));
let mut exported: Vec<(String, Span)> = Vec::new();
let mut bindings: Vec<(String, Span)> = Vec::new();
for stmt in body {
let Stmt::Export(decl) = stmt else { continue };
match decl {
ExportDecl::Default { span, .. } => {
exported.push((String::from("default"), *span));
}
ExportDecl::Decl { declaration, span } => {
let mut names: Vec<(Box<str>, Span, bool)> = Vec::new();
let mut vsink: Vec<Box<str>> = Vec::new();
collect_top_level_decls(declaration, &mut names, &mut vsink, true);
for n in names.into_iter().map(|(n, _, _)| n).chain(vsink) {
exported.push((n.to_string(), *span));
}
}
ExportDecl::All {
exported: Some(name),
span,
..
} => exported.push((module_export_name_str(name), *span)),
ExportDecl::All { exported: None, .. } => {}
ExportDecl::Named {
specifiers, source, ..
} => {
for sp in specifiers {
exported.push((module_export_name_str(&sp.exported), sp.span));
if source.is_none() {
match &sp.local {
crate::ast::ModuleExportName::Ident(n) => {
bindings.push((n.to_string(), sp.span));
}
crate::ast::ModuleExportName::Str(_) => {
return Err(self.err(
sp.span,
"a string module export name requires a `from` clause",
));
}
}
}
}
}
}
}
for i in 0..exported.len() {
for j in (i + 1)..exported.len() {
if exported[i].0 == exported[j].0 {
return Err(self.err(exported[j].1, "duplicate export name in module"));
}
}
}
for (name, span) in &bindings {
if !declared.iter().any(|d| d.as_ref() == name.as_str()) {
return Err(self.err(*span, "export of an undeclared identifier"));
}
}
Ok(())
}
fn check_params_vs_body_lexical(&self, params: &[Param], body: &[Stmt]) -> Result<()> {
let mut lexical: Vec<(Box<str>, Span, bool)> = Vec::new();
let mut vars: Vec<Box<str>> = Vec::new();
for stmt in body {
collect_top_level_decls(stmt, &mut lexical, &mut vars, true);
}
if lexical.is_empty() {
return Ok(());
}
let mut param_names = Vec::new();
for p in params {
collect_bound_names(&p.target, &mut param_names);
}
for (name, span, is_function) in &lexical {
if *is_function {
continue;
}
if param_names.iter().any(|(n, _)| n.as_str() == name.as_ref()) {
return Err(self.err(
*span,
"a lexical declaration conflicts with a parameter of the same name",
));
}
}
Ok(())
}
fn check_catch_param(&self, param: &BindingTarget, body: &[Stmt]) -> Result<()> {
let mut bound = Vec::new();
collect_bound_names(param, &mut bound);
self.check_no_dup_catch_names(&bound)?;
let simple = matches!(param, BindingTarget::Ident(_));
let mut lexical: Vec<(Box<str>, Span, bool)> = Vec::new();
let mut sink = Vec::new();
for stmt in body {
collect_top_level_decls(stmt, &mut lexical, &mut sink, false);
}
for (name, span, _) in &lexical {
if bound.iter().any(|(n, _)| n.as_str() == name.as_ref()) {
return Err(self.err(
*span,
"a `catch` body may not lexically redeclare the catch parameter",
));
}
}
if !simple {
let mut vars = Vec::new();
for stmt in body {
collect_vars_only(stmt, &mut vars);
}
for (name, span) in &bound {
if vars.iter().any(|v| v.as_ref() == name.as_str()) {
return Err(self.err(
*span,
"a destructuring `catch` binding may not be redeclared by a `var`",
));
}
}
}
Ok(())
}
fn check_no_dup_catch_names(&self, bound: &[(String, Span)]) -> Result<()> {
for (i, (name, _)) in bound.iter().enumerate() {
if bound[..i].iter().any(|(n, _)| n == name) {
return Err(self.err(
bound[i].1,
"a `catch` binding list may not contain a duplicate name",
));
}
}
Ok(())
}
fn check_lexical_scope_refs(
&self,
body: &[&Stmt],
top_level: bool,
strict: bool,
) -> Result<()> {
let mut lexical: Vec<(Box<str>, Span, bool)> = Vec::new();
let mut vars: Vec<Box<str>> = Vec::new();
for stmt in body {
collect_top_level_decls(stmt, &mut lexical, &mut vars, top_level);
}
for i in 0..lexical.len() {
for j in (i + 1)..lexical.len() {
if lexical[i].0 == lexical[j].0 {
let both_functions = lexical[i].2 && lexical[j].2;
if both_functions && !strict {
continue;
}
return Err(
self.err(lexical[j].1, "duplicate lexical declaration in this scope")
);
}
}
}
for (name, span, _) in &lexical {
if vars.iter().any(|v| v == name) {
return Err(self.err(
*span,
"a lexical declaration conflicts with a `var` of the same name",
));
}
}
Ok(())
}
}
fn collect_top_level_decls(
stmt: &Stmt,
lexical: &mut Vec<(Box<str>, Span, bool)>,
vars: &mut Vec<Box<str>>,
top_level: bool,
) {
match stmt {
Stmt::Var(decl) => {
if decl.kind == VarDeclKind::Var {
push_var_names(decl, vars);
} else {
for d in &decl.declarations {
let mut names = Vec::new();
collect_bound_names(&d.target, &mut names);
for (n, span) in names {
lexical.push((n.into(), span, false));
}
}
}
}
Stmt::Function(f) => {
if let Some(id) = &f.id {
let annexb_fn = !f.is_generator && !f.is_async;
if top_level {
vars.push(id.name.clone());
} else {
lexical.push((id.name.clone(), id.span, annexb_fn));
}
}
}
Stmt::Class(c) => {
if let Some(id) = &c.id {
lexical.push((id.name.clone(), id.span, false));
}
}
Stmt::Labeled { body, .. } => collect_top_level_decls(body, lexical, vars, top_level),
Stmt::Export(decl) => {
if let Some(inner) = export_inner_decl(decl) {
collect_top_level_decls(inner, lexical, vars, top_level);
}
}
other => collect_vars_only(other, vars),
}
}
fn collect_module_top_lexical(stmt: &Stmt, out: &mut Vec<(Box<str>, Span)>) {
match stmt {
Stmt::Function(f) => {
if let Some(id) = &f.id {
out.push((id.name.clone(), id.span));
}
}
Stmt::Class(c) => {
if let Some(id) = &c.id {
out.push((id.name.clone(), id.span));
}
}
Stmt::Var(decl) if decl.kind != VarDeclKind::Var => {
for d in &decl.declarations {
let mut names = Vec::new();
collect_bound_names(&d.target, &mut names);
for (n, span) in names {
out.push((n.into(), span));
}
}
}
Stmt::Export(decl) => {
if let Some(inner) = export_inner_decl(decl) {
collect_module_top_lexical(inner, out);
}
}
_ => {}
}
}
fn module_export_name_str(n: &crate::ast::ModuleExportName) -> String {
match n {
crate::ast::ModuleExportName::Ident(s) | crate::ast::ModuleExportName::Str(s) => {
s.to_string()
}
}
}
fn export_inner_decl(decl: &crate::ast::ExportDecl) -> Option<&Stmt> {
use crate::ast::ExportDecl;
match decl {
ExportDecl::Decl { declaration, .. } => Some(declaration),
ExportDecl::Default { declaration, .. } => match &**declaration {
s @ (Stmt::Function(crate::ast::Function { id: Some(_), .. })
| Stmt::Class(crate::ast::Class { id: Some(_), .. })) => Some(s),
_ => None,
},
ExportDecl::Named { .. } | ExportDecl::All { .. } => None,
}
}
fn loop_ctx(ctx: &Ctx) -> Ctx {
let mut c = *ctx;
c.in_breakable = true;
c.in_iteration = true;
c
}
fn labels_an_iteration(stmt: &Stmt) -> bool {
match stmt {
Stmt::For { .. }
| Stmt::ForIn { .. }
| Stmt::ForOf { .. }
| Stmt::While { .. }
| Stmt::DoWhile { .. } => true,
Stmt::Labeled { body, .. } => labels_an_iteration(body),
_ => false,
}
}
fn labels_a_function(stmt: &Stmt) -> bool {
match stmt {
Stmt::Function(_) => true,
Stmt::Labeled { body, .. } => labels_a_function(body),
_ => false,
}
}
fn push_var_names(decl: &VarDecl, vars: &mut Vec<Box<str>>) {
for d in &decl.declarations {
let mut names = Vec::new();
collect_bound_names(&d.target, &mut names);
for (n, _) in names {
vars.push(n.into());
}
}
}
fn collect_vars_only(stmt: &Stmt, vars: &mut Vec<Box<str>>) {
match stmt {
Stmt::Var(decl) if decl.kind == VarDeclKind::Var => push_var_names(decl, vars),
Stmt::Block { body, .. } => {
for s in body {
collect_vars_only(s, vars);
}
}
Stmt::If {
consequent,
alternate,
..
} => {
collect_vars_only(consequent, vars);
if let Some(a) = alternate {
collect_vars_only(a, vars);
}
}
Stmt::For { init, body, .. } => {
if let Some(crate::ast::ForInit::Var(d)) = init
&& d.kind == VarDeclKind::Var
{
push_var_names(d, vars);
}
collect_vars_only(body, vars);
}
Stmt::ForIn { left, body, .. } | Stmt::ForOf { left, body, .. } => {
if let ForLeft::Decl {
kind: VarDeclKind::Var,
target,
..
} = left
{
let mut names = Vec::new();
collect_bound_names(target, &mut names);
for (n, _) in names {
vars.push(n.into());
}
}
collect_vars_only(body, vars);
}
Stmt::While { body, .. } | Stmt::DoWhile { body, .. } | Stmt::With { body, .. } => {
collect_vars_only(body, vars);
}
Stmt::Labeled { body, .. } => collect_vars_only(body, vars),
Stmt::Try {
block,
handler,
finalizer,
..
} => {
for s in block {
collect_vars_only(s, vars);
}
if let Some(h) = handler {
for s in &h.body {
collect_vars_only(s, vars);
}
}
if let Some(f) = finalizer {
for s in f {
collect_vars_only(s, vars);
}
}
}
Stmt::Switch { cases, .. } => {
for case in cases {
for s in &case.body {
collect_vars_only(s, vars);
}
}
}
_ => {}
}
}
#[derive(Clone, Copy)]
enum YieldOrAwait {
Yield,
Await,
}
fn first_param_yield_await(
p: &Param,
is_generator: bool,
is_async: bool,
) -> Option<(Span, YieldOrAwait)> {
let mut found = None;
if let Some(d) = &p.default {
find_yield_await(d, is_generator, is_async, &mut found);
}
bound_target_yield_await(&p.target, is_generator, is_async, &mut found);
found
}
fn bound_target_yield_await(
target: &BindingTarget,
is_generator: bool,
is_async: bool,
found: &mut Option<(Span, YieldOrAwait)>,
) {
match target {
BindingTarget::Ident(_) => {}
BindingTarget::Array(p) => {
for el in &p.elements {
match el {
crate::ast::ArrayPatternElement::Hole => {}
crate::ast::ArrayPatternElement::Item {
target, default, ..
} => {
bound_target_yield_await(target, is_generator, is_async, found);
if let Some(d) = default {
find_yield_await(d, is_generator, is_async, found);
}
}
crate::ast::ArrayPatternElement::Rest { target, .. } => {
bound_target_yield_await(target, is_generator, is_async, found);
}
}
}
}
BindingTarget::Object(p) => {
for prop in &p.properties {
if let PropertyKey::Computed(e) = &prop.key {
find_yield_await(e, is_generator, is_async, found);
}
bound_target_yield_await(&prop.value, is_generator, is_async, found);
if let Some(d) = &prop.default {
find_yield_await(d, is_generator, is_async, found);
}
}
if let Some(r) = &p.rest {
bound_target_yield_await(r, is_generator, is_async, found);
}
}
}
}
fn find_yield_await(
e: &Expr,
is_generator: bool,
is_async: bool,
found: &mut Option<(Span, YieldOrAwait)>,
) {
if found.is_some() {
return;
}
match e {
Expr::Yield { span, .. } if is_generator => {
*found = Some((*span, YieldOrAwait::Yield));
}
Expr::Await { span, .. } if is_async => {
*found = Some((*span, YieldOrAwait::Await));
}
Expr::Function(_) | Expr::Arrow(_) | Expr::Class(_) => {}
Expr::Null(_)
| Expr::Bool { .. }
| Expr::Number { .. }
| Expr::BigInt { .. }
| Expr::Str { .. }
| Expr::Regex { .. }
| Expr::Ident(_)
| Expr::PrivateName(..)
| Expr::This(_)
| Expr::Super(_)
| Expr::NewTarget(_) => {}
Expr::Yield { argument, .. } => {
if let Some(a) = argument {
find_yield_await(a, is_generator, is_async, found);
}
}
Expr::Await { argument, .. } => {
find_yield_await(argument, is_generator, is_async, found);
}
Expr::Template(t) => {
for x in &t.expressions {
find_yield_await(x, is_generator, is_async, found);
}
}
Expr::TaggedTemplate { tag, quasi, .. } => {
find_yield_await(tag, is_generator, is_async, found);
for x in &quasi.expressions {
find_yield_await(x, is_generator, is_async, found);
}
}
Expr::Array { elements, .. } => {
for el in elements {
match el {
crate::ast::ArrayElement::Hole => {}
crate::ast::ArrayElement::Item(x) | crate::ast::ArrayElement::Spread(x) => {
find_yield_await(x, is_generator, is_async, found);
}
}
}
}
Expr::Object { members, .. } => {
for m in members {
match m {
crate::ast::ObjectMember::Property { key, value, .. } => {
if let PropertyKey::Computed(k) = key {
find_yield_await(k, is_generator, is_async, found);
}
find_yield_await(value, is_generator, is_async, found);
}
crate::ast::ObjectMember::Spread { value, .. } => {
find_yield_await(value, is_generator, is_async, found);
}
crate::ast::ObjectMember::Accessor { .. } => {}
}
}
}
Expr::Member {
object, property, ..
} => {
find_yield_await(object, is_generator, is_async, found);
if let PropertyKey::Computed(k) = property {
find_yield_await(k, is_generator, is_async, found);
}
}
Expr::Call {
callee, arguments, ..
}
| Expr::New {
callee, arguments, ..
} => {
find_yield_await(callee, is_generator, is_async, found);
for a in arguments {
match a {
crate::ast::Argument::Item(x) | crate::ast::Argument::Spread(x) => {
find_yield_await(x, is_generator, is_async, found);
}
}
}
}
Expr::OptChain { expr, .. } => {
find_yield_await(expr, is_generator, is_async, found);
}
Expr::Unary { argument, .. } | Expr::Update { argument, .. } => {
find_yield_await(argument, is_generator, is_async, found);
}
Expr::Binary { left, right, .. } | Expr::Logical { left, right, .. } => {
find_yield_await(left, is_generator, is_async, found);
find_yield_await(right, is_generator, is_async, found);
}
Expr::Conditional {
test,
consequent,
alternate,
..
} => {
find_yield_await(test, is_generator, is_async, found);
find_yield_await(consequent, is_generator, is_async, found);
find_yield_await(alternate, is_generator, is_async, found);
}
Expr::Assign { target, value, .. } => {
find_yield_await(target, is_generator, is_async, found);
find_yield_await(value, is_generator, is_async, found);
}
Expr::Sequence { expressions, .. } => {
for x in expressions {
find_yield_await(x, is_generator, is_async, found);
}
}
}
}
fn is_strict_reserved_word(name: &str) -> bool {
matches!(
name,
"implements"
| "interface"
| "let"
| "package"
| "private"
| "protected"
| "public"
| "static"
| "yield"
)
}
fn collect_bound_names(target: &BindingTarget, out: &mut Vec<(String, Span)>) {
match target {
BindingTarget::Ident(id) => out.push((id.name.to_string(), id.span)),
BindingTarget::Array(p) => {
for el in &p.elements {
match el {
crate::ast::ArrayPatternElement::Hole => {}
crate::ast::ArrayPatternElement::Item { target, .. }
| crate::ast::ArrayPatternElement::Rest { target, .. } => {
collect_bound_names(target, out)
}
}
}
}
BindingTarget::Object(p) => {
for prop in &p.properties {
collect_bound_names(&prop.value, out);
}
if let Some(r) = &p.rest {
collect_bound_names(r, out);
}
}
}
}
fn key_is_named(key: &PropertyKey, name: &str) -> bool {
match key {
PropertyKey::Ident(n) | PropertyKey::Str(n) => &**n == name,
_ => false,
}
}
fn contains_private_ref(e: &Expr) -> bool {
match e {
Expr::Member { property, .. } => matches!(property, PropertyKey::Private(_)),
Expr::OptChain { expr, .. } => contains_private_ref(expr),
_ => false,
}
}
fn is_simple_update_target(e: &Expr) -> bool {
match e {
Expr::Member { .. } => !is_import_meta(e),
Expr::Ident(_) => true,
_ => false,
}
}
fn is_import_meta(e: &Expr) -> bool {
if let Expr::Member {
object, property, ..
} = e
&& let (Expr::Ident(id), PropertyKey::Ident(name)) = (&**object, property)
{
return &*id.name == "import" && &**name == "meta";
}
false
}
fn is_valid_assign_target(e: &Expr) -> bool {
match e {
Expr::Member { .. } => !is_import_meta(e),
Expr::Ident(_) => true,
Expr::Array {
elements,
rest_trailing_comma,
..
} => {
if *rest_trailing_comma {
return false;
}
for (i, el) in elements.iter().enumerate() {
match el {
crate::ast::ArrayElement::Hole => {}
crate::ast::ArrayElement::Item(x) => {
if !is_pattern_element(x) {
return false;
}
}
crate::ast::ArrayElement::Spread(x) => {
if i + 1 != elements.len() || !is_valid_assign_target(x) {
return false;
}
}
}
}
true
}
Expr::Object { members, .. } => {
for (i, m) in members.iter().enumerate() {
match m {
ObjectMember::Property { value, .. } => {
if !is_pattern_element(value) {
return false;
}
}
ObjectMember::Spread { value, .. } => {
if i + 1 != members.len() || !is_valid_assign_target(value) {
return false;
}
}
ObjectMember::Accessor { .. } => return false,
}
}
true
}
_ => false,
}
}
fn is_pattern_element(e: &Expr) -> bool {
match e {
Expr::Assign {
op: crate::ast::AssignOp::Assign,
target,
..
} => is_valid_assign_target(target),
_ => is_valid_assign_target(e),
}
}
impl Validator {
fn check_not_eval_arguments(&self, target: &Expr) -> Result<()> {
match target {
Expr::Ident(id) if &*id.name == "eval" || &*id.name == "arguments" => Err(self.err(
id.span,
"`eval` and `arguments` may not be assigned in strict mode",
)),
Expr::Array { elements, .. } => {
for el in elements {
match el {
crate::ast::ArrayElement::Hole => {}
crate::ast::ArrayElement::Item(x) | crate::ast::ArrayElement::Spread(x) => {
self.check_not_eval_arguments(x)?;
}
}
}
Ok(())
}
Expr::Object { members, .. } => {
for m in members {
match m {
ObjectMember::Property { value, .. }
| ObjectMember::Spread { value, .. } => {
self.check_not_eval_arguments(value)?;
}
ObjectMember::Accessor { .. } => {}
}
}
Ok(())
}
Expr::Assign {
op: crate::ast::AssignOp::Assign,
target,
..
} => self.check_not_eval_arguments(target),
_ => Ok(()),
}
}
}
use alloc::string::ToString;