Semantic Analysis
Semantic analysis is the process of checking whether our source code is correct or not. We need to check against all the "Early Error" rules in the ECMAScript specification.
Context
For grammar contexts such as [Yield] or [Await], an error need to be raised when the grammar forbids them, for example:
BindingIdentifier[Yield, Await] :
Identifier
yield
await
13.1.1 Static Semantics: Early Errors
BindingIdentifier[Yield, Await] : yield
* It is a Syntax Error if this production has a [Yield] parameter.
* BindingIdentifier[Yield, Await] : await
It is a Syntax Error if this production has an [Await] parameter.
``````javascript
async function* foo() {
var yield, await;
}AsyncGeneratorBody : FunctionBody[+Yield, +Await]
rust
// https://github.com/rome/tools/blob/5a059c0413baf1d54436ac0c149a829f0dfd1f4d/crates/rome_js_parser/src/syntax/expr.rs#L1368-L1377
pub(super) fn parse_identifier(p: &mut Parser, kind: JsSyntaxKind) -> ParsedSyntax {
if !is_at_identifier(p) {
return Absent;
}
let error = match p.cur() {
T![yield] if p.state.in_generator() => Some(
p.err_builder("Illegal use of `yield` as an identifier in generator function")
.primary(p.cur_range(), ""),
),14.2.1 Static Semantics: Early Errors
Block :
- It is a Syntax Error if the LexicallyDeclaredNames of StatementList contains any duplicate entries.
- It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList also occurs in the VarDeclaredNames of StatementList.
rust
use indextree::{Arena, Node, NodeId};
use bitflags::bitflags;
pub type Scopes = Arena<Scope>;
pub type ScopeId = NodeId;
bitflags! {
#[derive(Default)]
pub struct ScopeFlags: u8 {
const TOP = 1 << 0;
const FUNCTION = 1 << 1;
const ARROW = 1 << 2;
const CLASS_STATIC_BLOCK = 1 << 4;
const VAR = Self::TOP.bits | Self::FUNCTION.bits | Self::CLASS_STATIC_BLOCK.bits;
}
}
#[derive(Debug, Clone)]
pub struct Scope {
/// [Strict Mode Code](https://tc39.es/ecma262/#sec-strict-mode-code)
/// [Use Strict Directive Prologue](https://tc39.es/ecma262/#sec-directive-prologues-and-the-use-strict-directive)
pub strict_mode: bool,
pub flags: ScopeFlags,
/// [Lexically Declared Names](https://tc39.es/ecma262/#sec-static-semantics-lexicallydeclarednames)
pub lexical: IndexMap<Atom, SymbolId, FxBuildHasher>,
/// [Var Declared Names](https://tc39.es/ecma262/#sec-static-semantics-vardeclarednames)
pub var: IndexMap<Atom, SymbolId, FxBuildHasher>,
/// Function Declarations
pub function: IndexMap<Atom, SymbolId, FxBuildHasher>,
}
``````rust
pub struct ScopeBuilder {
scopes: Scopes,
root_scope_id: ScopeId,
current_scope_id: ScopeId,
}
impl ScopeBuilder {
pub fn current_scope(&self) -> &Scope {
self.scopes[self.current_scope_id].get()
}
pub fn enter_scope(&mut self, flags: ScopeFlags) {
// Inherit strict mode for functions
// https://tc39.es/ecma262/#sec-strict-mode-code
let mut strict_mode = self.scopes[self.root_scope_id].get().strict_mode;
let parent_scope = self.current_scope();
if !strict_mode
&& parent_scope.flags.intersects(ScopeFlags::FUNCTION)
&& parent_scope.strict_mode
{
strict_mode = true;
}
let scope = Scope::new(flags, strict_mode);
let new_scope_id = self.scopes.new_node(scope);
self.current_scope_id.append(new_scope_id, &mut self.scopes);
self.current_scope_id = new_scope_id;
}
pub fn leave_scope(&mut self) {
self.current_scope_id = self.scopes[self.current_scope_id].parent().unwrap();
}
}
``````javascript reference
https://github.com/acornjs/acorn/blob/11735729c4ebe590e406f952059813f250a4cbd1/acorn/src/statement.js#L425-L437
```