ASTVerifier
The ASTVerifier is a class that checks whether specific invariants hold over an Abstract Syntax Tree (AST) from a source code perspective. For es2panda users, it provides a set of configurable checks that can be controlled through CLI options.
(Some of) Currently Implemented Checks
- For every node, each child has its
Parent()pointer correctly set to that node - (By the time of lowerings) Every typed node has a valid
TsType - Every identifier has a non-null
Variable()reference - All
LocalVariablesreferred to by identifiers have scopes that properly enclose those identifiers - No visibility rules are violated
- Scopes are properly nested
- Operands of arithmetic expressions have numeric types (except for
+operator on strings) - In
forIn,forOf, andforUpdatestatements:- The
left_field is either an expression or a single variable declaration - In
forUpdate, the variable has an initializer - In
forInandforOf, the variable has no initializer
- The
- In sequence expressions, the type of the expression matches the type of its last member
CLI Options
For complete and up-to-date options, run es2panda --help.
Usage Examples
# Run verifier after each phase
es2panda --ast-verifier:phases=each
Note:
beforeruns only after parsingafterruns only after all loweringseachruns after each phase
# Customize warning/error behavior
es2panda --ast-verifier:warnings=NodeHasParent --ast-verifier:errors=ArithmeticOperationsValid,NodeHasType,NoPrimitiveTypes
# Check dependencies as well as main program
es2panda --ast-verifier:full-program
Adding a New Invariant
Invariant Design Principles
An invariant should:
- Match patterns in AST subtrees that will be processed
- Explicitly handle exceptions by skipping non-matching subtrees
- Determine subtree correctness and report errors when needed
An invariant should not:
- Iterate over the AST or apply itself recursively (this would slow down verification as all invariants run in parallel)
- Call other invariants directly
- If invariant
Adepends on invariantB:- Explicitly mark the dependency
Bshould execute first and prepare required data- Example:
NoPrimitiveTypedepends onNodeHasType, soNodeHasTypeexecutes first and provides type information
- If invariant
Implementation Steps
-
Register the invariant:
- Add to
VerifierInvariantsenum inutil/options.yaml - This generates the corresponding C++ enum entry
- Add to
-
Implement the check class:
- Location:
<repo_root>/ets2panda/ast_verifier/invariants - Structure:
class SomeCheck : public InvariantBase<VerifierInvariants::SOME_CHECK> { public: using Base::Base; [[nodiscard]] CheckResult operator()(const ir::AstNode *ast) {...} }; - Naming convention: Use descriptive names (e.g.,
VariableInitializedfor variable initialization checks)
- Location:
-
Add documentation:
- Describe the expected condition
- Specify when the check becomes relevant (which phase)
- Control execution timing via
ASTVerifier::IntroduceNewInvariants(phaseName)inASTVerifier.h
- Control execution timing via
- Indicate whether it applies to:
- Main program only
- External sources as well
-
Error handling:
- Use
AddCheckMessagefor error reporting - Return
CheckResulttuples:CheckDecision::CORRECTfor passing analysisCheckDecision::INCORRECTfor failuresCheckAction::CONTINUEto proceed to children (inForAllmode)CheckAction::SKIP_SUBTREEto skip subtree processing
- Use
-
Best practices:
- Use descriptive error messages
- Study existing checks for implementation patterns
- Maintain clear separation of concerns between invariants