From 9f33b3380372f0f2fd40ab612e2aec49c4fb5e64 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Tue, 29 Sep 2020 02:13:10 +0100 Subject: [PATCH] parser: warn about ambiguous infix/prefix op token (#6491) --- vlib/v/ast/ast.v | 4 ++++ vlib/v/parser/pratt.v | 12 +++++++----- vlib/v/parser/tests/prefix_first.out | 28 ++++++++++++++++++++++++++++ vlib/v/parser/tests/prefix_first.vv | 28 ++++++++++++++++++++++++++++ vlib/v/token/token.v | 4 ++++ 5 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 vlib/v/parser/tests/prefix_first.out create mode 100644 vlib/v/parser/tests/prefix_first.vv diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 8e40007f27..221544880e 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -422,6 +422,8 @@ pub fn (i &Ident) var_info() IdentVar { } } +// left op right +// See: token.Kind.is_infix pub struct InfixExpr { pub: op token.Kind @@ -434,6 +436,7 @@ pub mut: auto_locked string } +// ++, -- pub struct PostfixExpr { pub: op token.Kind @@ -443,6 +446,7 @@ pub mut: auto_locked string } +// See: token.Kind.is_prefix pub struct PrefixExpr { pub: op token.Kind diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index cde83884b7..5be470b37c 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -269,11 +269,13 @@ pub fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_iden pos: pos } } else if p.tok.kind.is_infix() { - // return early for deref assign `*x = 2` goes to prefix expr - if p.tok.kind == .mul && - p.tok.line_nr != p.prev_tok.line_nr && - p.peek_tok2.kind == .assign { - return node + if p.tok.kind.is_prefix() && p.tok.line_nr != p.prev_tok.line_nr { + // return early for deref assign `*x = 2` goes to prefix expr + if p.tok.kind == .mul && p.peek_tok2.kind == .assign { + return node + } + // added 10/2020: LATER this will be parsed as PrefixExpr instead + p.warn_with_pos('move infix `$p.tok.kind` operator before new line (if infix intended) or use brackets for a prefix expression', p.tok.position()) } // continue on infix expr node = p.infix_expr(node) diff --git a/vlib/v/parser/tests/prefix_first.out b/vlib/v/parser/tests/prefix_first.out new file mode 100644 index 0000000000..c5f5b27c35 --- /dev/null +++ b/vlib/v/parser/tests/prefix_first.out @@ -0,0 +1,28 @@ +vlib/v/parser/tests/prefix_first.vv:15:3: warning: move infix `-` operator before new line (if infix intended) or use brackets for a prefix expression + 13 | _ = if true { + 14 | v = 1 + 15 | -1 + | ^ + 16 | } else {1} + 17 | } +vlib/v/parser/tests/prefix_first.vv:26:3: warning: move infix `&` operator before new line (if infix intended) or use brackets for a prefix expression + 24 | _ = opt() or { + 25 | _ = 1 + 26 | &v + | ^ + 27 | } + 28 | } +vlib/v/parser/tests/prefix_first.vv:13:6: error: `if` expression requires an expression as the last statement of every branch + 11 | + 12 | // later this should compile correctly + 13 | _ = if true { + | ~~~~~~~ + 14 | v = 1 + 15 | -1 +vlib/v/parser/tests/prefix_first.vv:24:6: error: last statement in the `or {}` block should be an expression of type `&int` or exit parent scope + 22 | // later this should compile correctly + 23 | v := 3 + 24 | _ = opt() or { + | ~~~~~ + 25 | _ = 1 + 26 | &v diff --git a/vlib/v/parser/tests/prefix_first.vv b/vlib/v/parser/tests/prefix_first.vv new file mode 100644 index 0000000000..3e9c5a5bfc --- /dev/null +++ b/vlib/v/parser/tests/prefix_first.vv @@ -0,0 +1,28 @@ +// a prefix op can be parsed as an infix op if there's an expression on the line before +// https://github.com/vlang/v/pull/6491 +fn test_prefix() { + mut v := 1 + mut p := &v + // OK, special workaround + unsafe { + v = 1 + *p = 2 + } + + // later this should compile correctly + _ = if true { + v = 1 + -1 + } else {1} +} + +fn opt() ?&int {return none} + +fn test_prefix_or() { + // later this should compile correctly + v := 3 + _ = opt() or { + _ = 1 + &v + } +} diff --git a/vlib/v/token/token.v b/vlib/v/token/token.v index 4166c3bc2c..33489bbbc2 100644 --- a/vlib/v/token/token.v +++ b/vlib/v/token/token.v @@ -409,6 +409,10 @@ pub fn (k Kind) is_start_of_type() bool { return k in [.name, .lpar, .amp, .lsbr, .question] } +pub fn (kind Kind) is_prefix() bool { + return kind in [.minus, .amp, .mul, .not, .bit_not] +} + pub fn (kind Kind) is_infix() bool { return kind in [.plus, .minus, .mod, .mul, .div, .eq, .ne, .gt, .lt, .key_in, //