ast, parser: implement simple AST poisoning (#9525)

pull/9531/head
Ned Palacios 2021-03-30 15:33:29 +08:00 committed by GitHub
parent 999c385b7f
commit b319068151
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 77 additions and 90 deletions

View File

@ -14,14 +14,14 @@ pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr |
CTempVar | CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall |
ComptimeSelector | ConcatExpr | DumpExpr | EnumVal | FloatLiteral | GoExpr | Ident |
IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr |
MapInit | MatchExpr | None | OffsetOf | OrExpr | ParExpr | PostfixExpr | PrefixExpr |
RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral |
StructInit | Type | TypeOf | UnsafeExpr
MapInit | MatchExpr | NodeError | None | OffsetOf | OrExpr | ParExpr | PostfixExpr |
PrefixExpr | RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral |
StringLiteral | StructInit | Type | TypeOf | UnsafeExpr
pub type Stmt = AsmStmt | AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl |
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl |
GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | Return |
SqlStmt | StructDecl | TypeDecl
GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | NodeError |
Return | SqlStmt | StructDecl | TypeDecl
// NB: when you add a new Expr or Stmt type with a .pos field, remember to update
// the .position() token.Position methods too.
@ -1083,7 +1083,7 @@ pub:
pub struct AsmAddressing {
pub:
displacement u32 // 8, 16 or 32 bit literal value
scale int = -1 // 1, 2, 4, or 8 literal
scale int = -1 // 1, 2, 4, or 8 literal
mode AddressingMode
pos token.Position
pub mut:
@ -1408,6 +1408,12 @@ pub mut:
sub_structs map[int]SqlExpr
}
pub struct NodeError {
pub:
idx int // index for referencing the related ast.File error
pos token.Position
}
[inline]
pub fn (expr Expr) is_blank_ident() bool {
match expr {
@ -1422,12 +1428,12 @@ pub fn (expr Expr) position() token.Position {
AnonFn {
return expr.decl.pos
}
ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, CastExpr, ChanInit,
CharLiteral, ConcatExpr, Comment, ComptimeCall, ComptimeSelector, EnumVal, DumpExpr, FloatLiteral,
GoExpr, Ident, IfExpr, IndexExpr, IntegerLiteral, Likely, LockExpr, MapInit, MatchExpr,
None, OffsetOf, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr,
SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit, Type, TypeOf, UnsafeExpr
{
NodeError, ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, CastExpr,
ChanInit, CharLiteral, ConcatExpr, Comment, ComptimeCall, ComptimeSelector, EnumVal, DumpExpr,
FloatLiteral, GoExpr, Ident, IfExpr, IndexExpr, IntegerLiteral, Likely, LockExpr, MapInit,
MatchExpr, None, OffsetOf, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr,
SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit, Type, TypeOf,
UnsafeExpr {
return expr.pos
}
IfGuardExpr {

View File

@ -3324,6 +3324,7 @@ fn (mut c Checker) stmt(node ast.Stmt) {
}
// c.expected_type = table.void_type
match mut node {
ast.NodeError {}
ast.AsmStmt {
c.asm_stmt(mut node)
}
@ -3950,6 +3951,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
return table.void_type
}
match mut node {
ast.NodeError {}
ast.CTempVar {
return node.typ
}

View File

@ -382,6 +382,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
eprintln('stmt: ${node.pos:-42} | node: ${node.type_name():-20}')
}
match node {
ast.NodeError {}
ast.AsmStmt {
f.asm_stmt(node)
}
@ -480,6 +481,7 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
eprintln('expr: ${node.position():-42} | node: ${node.type_name():-20} | $node.str()')
}
match mut node {
ast.NodeError {}
ast.AnonFn {
f.fn_decl(node.decl)
}

View File

@ -1187,6 +1187,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
// g.cur_mod = node.name
g.cur_mod = node
}
ast.NodeError {}
ast.Return {
g.write_defer_stmts_when_needed()
// af := g.autofree && node.exprs.len > 0 && node.exprs[0] is ast.CallExpr && !g.is_builtin_mod
@ -3013,6 +3014,7 @@ fn (mut g Gen) expr(node ast.Expr) {
ast.MapInit {
g.map_init(node)
}
ast.NodeError {}
ast.None {
g.write('_const_none__')
}

View File

@ -427,6 +427,7 @@ fn (mut g JsGen) stmt(node ast.Stmt) {
ast.Module {
// skip: namespacing implemented externally
}
ast.NodeError {}
ast.Return {
if g.defer_stmts.len > 0 {
g.gen_defer_stmts()
@ -445,6 +446,7 @@ fn (mut g JsGen) stmt(node ast.Stmt) {
fn (mut g JsGen) expr(node ast.Expr) {
match node {
ast.NodeError {}
ast.CTempVar {
g.write('/* ast.CTempVar: node.name */')
}
@ -693,7 +695,7 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) {
} else {
g.write(' $op ')
// TODO: Multiple types??
should_cast :=
should_cast :=
(g.table.type_kind(stmt.left_types.first()) in js.shallow_equatables)
&& (g.cast_stack.len <= 0 || stmt.left_types.first() != g.cast_stack.last())

View File

@ -123,6 +123,7 @@ pub fn (mut w Walker) stmt(node ast.Stmt) {
ast.InterfaceDecl {}
ast.Module {}
ast.TypeDecl {}
ast.NodeError {}
}
}
@ -335,6 +336,7 @@ fn (mut w Walker) expr(node ast.Expr) {
ast.UnsafeExpr {
w.expr(node.expr)
}
ast.NodeError {}
}
}

View File

@ -110,8 +110,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
// a, b := a + 1, b
for r in right {
p.check_undefined_variables(left, r) or {
p.error('check_undefined_variables failed')
return ast.Stmt{}
return p.error('check_undefined_variables failed')
}
}
} else if left.len > 1 {
@ -119,8 +118,8 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
for r in right {
has_cross_var = p.check_cross_variables(left, r)
if op !in [.assign, .decl_assign] {
p.error_with_pos('unexpected $op.str(), expecting := or = or comma', pos)
return ast.Stmt{}
return p.error_with_pos('unexpected $op.str(), expecting := or = or comma',
pos)
}
if has_cross_var {
break
@ -133,8 +132,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
ast.Ident {
if op == .decl_assign {
if p.scope.known_var(lx.name) {
p.error_with_pos('redefinition of `$lx.name`', lx.pos)
return ast.Stmt{}
return p.error_with_pos('redefinition of `$lx.name`', lx.pos)
}
mut share := table.ShareType(0)
if lx.info is ast.IdentVar {
@ -142,9 +140,8 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
share = iv.share
if iv.is_static {
if !p.pref.translated && !p.pref.is_fmt && !p.inside_unsafe_fn {
p.error_with_pos('static variables are supported only in -translated mode or in [unsafe] fn',
return p.error_with_pos('static variables are supported only in -translated mode or in [unsafe] fn',
lx.pos)
return ast.Stmt{}
}
is_static = true
}
@ -175,9 +172,8 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
}
ast.IndexExpr {
if op == .decl_assign {
p.error_with_pos('non-name `$lx.left[$lx.index]` on left side of `:=`',
return p.error_with_pos('non-name `$lx.left[$lx.index]` on left side of `:=`',
lx.pos)
return ast.Stmt{}
}
lx.is_setter = true
}
@ -185,9 +181,8 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
ast.PrefixExpr {}
ast.SelectorExpr {
if op == .decl_assign {
p.error_with_pos('struct fields can only be declared during the initialization',
return p.error_with_pos('struct fields can only be declared during the initialization',
lx.pos)
return ast.Stmt{}
}
}
else {

View File

@ -12,8 +12,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
p.open_scope()
p.inside_for = true
if p.tok.kind == .key_match {
p.error('cannot use `match` in `for` loop')
return ast.Stmt{}
return p.error('cannot use `match` in `for` loop')
}
// defer { p.close_scope() }
// Infinite loop
@ -34,8 +33,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
&& p.peek_token(2).kind != .key_mut && p.peek_token(3).kind != .key_in) {
// `for i := 0; i < 10; i++ {` or `for a,b := 0,1; a < 10; a++ {`
if p.tok.kind == .key_mut {
p.error('`mut` is not needed in `for ;;` loops: use `for i := 0; i < n; i ++ {`')
return ast.Stmt{}
return p.error('`mut` is not needed in `for ;;` loops: use `for i := 0; i < n; i ++ {`')
}
mut init := ast.Stmt{}
mut cond := p.new_true_expr()
@ -55,8 +53,7 @@ fn (mut p Parser) for_stmt() ast.Stmt {
if p.tok.kind != .semicolon {
// Disallow `for i := 0; i++; i < ...`
if p.tok.kind == .name && p.peek_tok.kind in [.inc, .dec] {
p.error('cannot use $p.tok.lit$p.peek_tok.kind as value')
return ast.Stmt{}
return p.error('cannot use $p.tok.lit$p.peek_tok.kind as value')
}
cond = p.expr(0)
has_cond = true
@ -112,16 +109,14 @@ fn (mut p Parser) for_stmt() ast.Stmt {
val_var_pos = p.tok.position()
val_var_name = p.check_name()
if key_var_name == val_var_name && key_var_name != '_' {
p.error_with_pos('key and value in a for loop cannot be the same', val_var_pos)
return ast.Stmt{}
return p.error_with_pos('key and value in a for loop cannot be the same',
val_var_pos)
}
if p.scope.known_var(key_var_name) {
p.error('redefinition of key iteration variable `$key_var_name`')
return ast.Stmt{}
return p.error('redefinition of key iteration variable `$key_var_name`')
}
if p.scope.known_var(val_var_name) {
p.error('redefinition of value iteration variable `$val_var_name`')
return ast.Stmt{}
return p.error('redefinition of value iteration variable `$val_var_name`')
}
p.scope.register(ast.Var{
name: key_var_name
@ -130,13 +125,11 @@ fn (mut p Parser) for_stmt() ast.Stmt {
is_tmp: true
})
} else if p.scope.known_var(val_var_name) {
p.error('redefinition of value iteration variable `$val_var_name`')
return ast.Stmt{}
return p.error('redefinition of value iteration variable `$val_var_name`')
}
p.check(.key_in)
if p.tok.kind == .name && p.tok.lit in [key_var_name, val_var_name] {
p.error('in a `for x in array` loop, the key or value iteration variable `$p.tok.lit` can not be the same as the array variable')
return ast.Stmt{}
return p.error('in a `for x in array` loop, the key or value iteration variable `$p.tok.lit` can not be the same as the array variable')
}
// arr_expr
cond := p.expr(0)
@ -156,8 +149,8 @@ fn (mut p Parser) for_stmt() ast.Stmt {
is_tmp: true
})
if key_var_name.len > 0 {
p.error_with_pos('cannot declare index variable with range `for`', key_var_pos)
return ast.Stmt{}
return p.error_with_pos('cannot declare index variable with range `for`',
key_var_pos)
}
} else {
// this type will be set in checker

View File

@ -516,8 +516,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
return p.type_decl()
}
else {
p.error('wrong pub keyword usage')
return ast.Stmt{}
return p.error('wrong pub keyword usage')
}
}
}
@ -591,8 +590,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
} else if p.pref.is_fmt {
return p.stmt(false)
} else {
p.error('bad top level statement ' + p.tok.str())
return ast.Stmt{}
return p.error('bad top level statement ' + p.tok.str())
}
}
}
@ -720,12 +718,10 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
pos: spos.extend(p.tok.position())
}
} else if p.peek_tok.kind == .name {
p.error_with_pos('unexpected name `$p.peek_tok.lit`', p.peek_tok.position())
return ast.Stmt{}
return p.error_with_pos('unexpected name `$p.peek_tok.lit`', p.peek_tok.position())
} else if !p.inside_if_expr && !p.inside_match_body && !p.inside_or_expr
&& p.peek_tok.kind in [.rcbr, .eof] && !p.mark_var_as_used(p.tok.lit) {
p.error_with_pos('`$p.tok.lit` evaluated but not used', p.tok.position())
return ast.Stmt{}
return p.error_with_pos('`$p.tok.lit` evaluated but not used', p.tok.position())
}
return p.parse_multi_expr(is_top_level)
}
@ -759,8 +755,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
}
}
else {
p.error_with_pos('unexpected \$', p.tok.position())
return ast.Stmt{}
return p.error_with_pos('unexpected \$', p.tok.position())
}
}
}
@ -820,9 +815,8 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
}
}
.key_const {
p.error_with_pos('const can only be defined at the top level (outside of functions)',
return p.error_with_pos('const can only be defined at the top level (outside of functions)',
p.tok.position())
return ast.Stmt{}
}
.key_asm {
return p.asm_stmt(false)
@ -1483,8 +1477,8 @@ pub fn (mut p Parser) check_for_impure_v(language table.Language, pos token.Posi
}
}
pub fn (mut p Parser) error(s string) {
p.error_with_pos(s, p.tok.position())
pub fn (mut p Parser) error(s string) ast.NodeError {
return p.error_with_pos(s, p.tok.position())
}
pub fn (mut p Parser) warn(s string) {
@ -1495,7 +1489,7 @@ pub fn (mut p Parser) note(s string) {
p.note_with_pos(s, p.tok.position())
}
pub fn (mut p Parser) error_with_pos(s string, pos token.Position) {
pub fn (mut p Parser) error_with_pos(s string, pos token.Position) ast.NodeError {
if p.pref.fatal_errors {
exit(1)
}
@ -1523,6 +1517,10 @@ pub fn (mut p Parser) error_with_pos(s string, pos token.Position) {
// The p.next() here is needed, so the parser is more robust, and *always* advances, even in the -silent mode.
p.next()
}
return ast.NodeError{
idx: p.errors.len - 1
pos: pos
}
}
pub fn (mut p Parser) error_with_error(error errors.Error) {
@ -1611,8 +1609,7 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt {
left, left_comments := p.expr_list()
left0 := left[0]
if tok.kind == .key_mut && p.tok.kind != .decl_assign {
p.error('expecting `:=` (e.g. `mut x :=`)')
return ast.Stmt{}
return p.error('expecting `:=` (e.g. `mut x :=`)')
}
// TODO remove translated
if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() {
@ -1624,8 +1621,7 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt {
&& node !is ast.PostfixExpr && !(node is ast.InfixExpr
&& (node as ast.InfixExpr).op in [.left_shift, .arrow]) && node !is ast.ComptimeCall
&& node !is ast.SelectorExpr && node !is ast.DumpExpr {
p.error_with_pos('expression evaluated but not used', node.position())
return ast.Stmt{}
return p.error_with_pos('expression evaluated but not used', node.position())
}
}
}
@ -1819,12 +1815,10 @@ pub fn (mut p Parser) name_expr() ast.Expr {
cap_expr = p.expr(0)
}
'len', 'init' {
p.error('`$key` cannot be initialized for `chan`. Did you mean `cap`?')
return ast.Expr{}
return p.error('`$key` cannot be initialized for `chan`. Did you mean `cap`?')
}
else {
p.error('wrong field `$key`, expecting `cap`')
return ast.Expr{}
return p.error('wrong field `$key`, expecting `cap`')
}
}
last_pos = p.tok.position()
@ -1843,15 +1837,13 @@ pub fn (mut p Parser) name_expr() ast.Expr {
return p.string_expr()
} else {
// don't allow any other string prefix except `r`, `js` and `c`
p.error('only `c`, `r`, `js` are recognized string prefixes, but you tried to use `$p.tok.lit`')
return ast.Expr{}
return p.error('only `c`, `r`, `js` are recognized string prefixes, but you tried to use `$p.tok.lit`')
}
}
// don't allow r`byte` and c`byte`
if p.tok.lit in ['r', 'c'] && p.peek_tok.kind == .chartoken {
opt := if p.tok.lit == 'r' { '`r` (raw string)' } else { '`c` (c string)' }
p.error('cannot use $opt with `byte` and `rune`')
return ast.Expr{}
return p.error('cannot use $opt with `byte` and `rune`')
}
known_var := p.mark_var_as_used(p.tok.lit)
mut is_mod_cast := false
@ -2342,8 +2334,7 @@ fn (mut p Parser) string_expr() ast.Expr {
has_fmt = true
p.next()
} else {
p.error('format specifier may only be one letter')
return ast.Expr{}
return p.error('format specifier may only be one letter')
}
}
}
@ -3072,13 +3063,11 @@ fn (mut p Parser) unsafe_stmt() ast.Stmt {
mut pos := p.tok.position()
p.next()
if p.tok.kind != .lcbr {
p.error_with_pos('please use `unsafe {`', p.tok.position())
return ast.Stmt{}
return p.error_with_pos('please use `unsafe {`', p.tok.position())
}
p.next()
if p.inside_unsafe {
p.error_with_pos('already inside `unsafe` block', pos)
return ast.Stmt{}
return p.error_with_pos('already inside `unsafe` block', pos)
}
if p.tok.kind == .rcbr {
// `unsafe {}`

View File

@ -69,8 +69,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
return p.if_expr(true)
}
else {
p.error_with_pos('unexpected `$`', p.peek_tok.position())
return ast.Expr{}
return p.error_with_pos('unexpected `$`', p.peek_tok.position())
}
}
}
@ -135,8 +134,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
mut pos := p.tok.position()
p.next()
if p.inside_unsafe {
p.error_with_pos('already inside `unsafe` block', pos)
return ast.Expr{}
return p.error_with_pos('already inside `unsafe` block', pos)
}
p.inside_unsafe = true
p.check(.lcbr)
@ -240,8 +238,8 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
st := p.parse_type()
p.check(.comma)
if p.tok.kind != .name {
p.error_with_pos('unexpected `$p.tok.lit`, expecting struct field', p.tok.position())
return ast.Expr{}
return p.error_with_pos('unexpected `$p.tok.lit`, expecting struct field',
p.tok.position())
}
field := p.tok.lit
p.next()
@ -281,13 +279,11 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
node = p.struct_init(true) // short_syntax: true
} else if p.tok.kind == .name {
p.next()
p.error_with_pos('unexpected $p.tok, expecting `:` after struct field name',
return p.error_with_pos('unexpected $p.tok, expecting `:` after struct field name',
p.tok.position())
return ast.Expr{}
} else {
p.error_with_pos('unexpected $p.tok, expecting struct field name',
return p.error_with_pos('unexpected $p.tok, expecting struct field name',
p.tok.position())
return ast.Expr{}
}
}
p.check(.rcbr)
@ -326,8 +322,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
else {
if p.tok.kind != .eof && !(p.tok.kind == .rsbr && p.inside_asm) {
// eof should be handled where it happens
p.error_with_pos('invalid expression: unexpected $p.tok', p.tok.position())
return ast.Expr{}
return p.error_with_pos('invalid expression: unexpected $p.tok', p.tok.position())
}
}
}

View File

@ -52,8 +52,7 @@ fn (mut p Parser) sql_expr() ast.Expr {
if p.tok.kind == .name && p.tok.lit == 'by' {
p.check_name() // `by`
} else {
p.error_with_pos('use `order by` in ORM queries', order_pos)
return ast.Expr{}
return p.error_with_pos('use `order by` in ORM queries', order_pos)
}
has_order = true
order_expr = p.expr(0)