From be04de98bbab1e2c558c40ee4a91f022ee58cc31 Mon Sep 17 00:00:00 2001 From: div72 <60045611+div72@users.noreply.github.com> Date: Sat, 4 Jul 2020 18:28:01 +0300 Subject: [PATCH] checker: check for enum value duplicates/overflow (#5659) --- vlib/v/checker/checker.v | 45 +++++++++++++------ vlib/v/checker/tests/enum_field_overflow.out | 6 +++ vlib/v/checker/tests/enum_field_overflow.vv | 5 +++ .../tests/enum_field_value_duplicate_err.out | 13 ++++++ .../tests/enum_field_value_duplicate_err.vv | 6 +++ .../tests/enum_field_value_overflow.out | 7 +++ .../tests/enum_field_value_overflow.vv | 5 +++ 7 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 vlib/v/checker/tests/enum_field_overflow.out create mode 100644 vlib/v/checker/tests/enum_field_overflow.vv create mode 100644 vlib/v/checker/tests/enum_field_value_duplicate_err.out create mode 100644 vlib/v/checker/tests/enum_field_value_duplicate_err.vv create mode 100644 vlib/v/checker/tests/enum_field_value_overflow.out create mode 100644 vlib/v/checker/tests/enum_field_value_overflow.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index a160f0daab..53d41e54c2 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -12,6 +12,8 @@ import v.errors const ( max_nr_errors = 300 + enum_min = int(0x80000000) + enum_max = 0x7FFFFFFF ) pub struct Checker { @@ -160,14 +162,14 @@ fn (mut c Checker) check_file_in_main(file ast.File) bool { c.warn('const $no_pub_in_main_warning', stmt.pos) } } - /* - // TODO not a Stmt + /* + // TODO not a Stmt ast.ConstField { if stmt.is_pub { c.warn('const field `$stmt.name` $no_pub_in_main_warning', stmt.pos) } } - */ + */ ast.EnumDecl { if stmt.is_pub { c.warn('enum `$stmt.name` $no_pub_in_main_warning', stmt.pos) @@ -375,7 +377,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type { .placeholder { c.error('unknown struct: $type_sym.name', struct_init.pos) } - // string & array are also structs but .kind of string/array + // string & array are also structs but .kind of string/array .struct_, .string, .array, .alias { mut info := table.Struct{} if type_sym.kind == .alias { @@ -740,7 +742,8 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_e ast.AnonFn { if arg_expr.decl.args.len > 1 { c.error('function needs exactly 1 argument', call_expr.pos) - } else if is_map && (arg_expr.decl.return_type != elem_typ || arg_expr.decl.args[0].typ != elem_typ) { + } else if is_map && (arg_expr.decl.return_type != elem_typ || + arg_expr.decl.args[0].typ != elem_typ) { c.error('type mismatch, should use `fn(a $elem_sym.name) $elem_sym.name {...}`', call_expr.pos) } else if !is_map && (arg_expr.decl.return_type != table.bool_type || @@ -1131,7 +1134,6 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type { } if call_arg.is_mut { c.fail_if_immutable(call_arg.expr) - if !arg.is_mut { mut words := 'mutable' mut tok := 'mut' @@ -1427,6 +1429,7 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) { pub fn (mut c Checker) enum_decl(decl ast.EnumDecl) { c.check_valid_pascal_case(decl.name, 'enum name', decl.pos) + mut seen := []int{} for i, field in decl.fields { if util.contains_capital(field.name) { c.error('field name `$field.name` cannot contain uppercase letters, use snake_case instead', @@ -1438,8 +1441,16 @@ pub fn (mut c Checker) enum_decl(decl ast.EnumDecl) { } } if field.has_expr { - match field.expr { - ast.IntegerLiteral {} + match field.expr as field_expr { + ast.IntegerLiteral { + val := field_expr.val.i64() + if val < enum_min || val > enum_max { + c.error('enum value `$val` overflows int', field_expr.pos) + } else if int(val) in seen { + c.error('enum value `$val` already exists', field_expr.pos) + } + seen << int(val) + } ast.PrefixExpr {} else { if field.expr is ast.Ident { @@ -1455,6 +1466,16 @@ pub fn (mut c Checker) enum_decl(decl ast.EnumDecl) { c.error('default value for enum has to be an integer', pos) } } + } else { + if seen.len > 0 { + last := seen[seen.len - 1] + if last == enum_max { + c.error('enum value overflows', field.pos) + } + seen << last + 1 + } else { + seen << 0 + } } } } @@ -1776,7 +1797,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { node.pos) } } - // ast.Attr {} + // ast.Attr {} ast.AssignStmt { c.assign_stmt(mut node) } @@ -1944,7 +1965,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { } } } - // ast.HashStmt {} + // ast.HashStmt {} ast.Import {} ast.InterfaceDecl { c.interface_decl(node) @@ -2496,7 +2517,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol unhandled << '`$v_str`' } } } - // + // table.Enum { for v in info.vals { if v !in branch_exprs { is_exhaustive = false @@ -2953,7 +2974,6 @@ fn (mut c Checker) fn_decl(node ast.FnDecl) { } } } - if node.language == .v && node.is_method && node.name == 'str' { if node.return_type != table.string_type { c.error('.str() methods should return `string`', node.pos) @@ -2962,7 +2982,6 @@ fn (mut c Checker) fn_decl(node ast.FnDecl) { c.error('.str() methods should have 0 arguments', node.pos) } } - c.expected_type = table.void_type c.cur_fn = &node c.stmts(node.stmts) diff --git a/vlib/v/checker/tests/enum_field_overflow.out b/vlib/v/checker/tests/enum_field_overflow.out new file mode 100644 index 0000000000..5e16001122 --- /dev/null +++ b/vlib/v/checker/tests/enum_field_overflow.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/enum_field_overflow.v:4:2: error: enum value overflows + 2 | red + 3 | green = 2147483647 + 4 | blue + | ~~~~ + 5 | } \ No newline at end of file diff --git a/vlib/v/checker/tests/enum_field_overflow.vv b/vlib/v/checker/tests/enum_field_overflow.vv new file mode 100644 index 0000000000..8c79d29003 --- /dev/null +++ b/vlib/v/checker/tests/enum_field_overflow.vv @@ -0,0 +1,5 @@ +enum Color { + red + green = 2147483647 + blue +} diff --git a/vlib/v/checker/tests/enum_field_value_duplicate_err.out b/vlib/v/checker/tests/enum_field_value_duplicate_err.out new file mode 100644 index 0000000000..a75d693e2a --- /dev/null +++ b/vlib/v/checker/tests/enum_field_value_duplicate_err.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/enum_field_value_duplicate_err.v:3:10: error: enum value `0` already exists + 1 | enum Color { + 2 | red + 3 | green = 0 + | ^ + 4 | blue = 1 + 5 | alpha = 1 +vlib/v/checker/tests/enum_field_value_duplicate_err.v:5:10: error: enum value `1` already exists + 3 | green = 0 + 4 | blue = 1 + 5 | alpha = 1 + | ^ + 6 | } diff --git a/vlib/v/checker/tests/enum_field_value_duplicate_err.vv b/vlib/v/checker/tests/enum_field_value_duplicate_err.vv new file mode 100644 index 0000000000..49cf3c2ea0 --- /dev/null +++ b/vlib/v/checker/tests/enum_field_value_duplicate_err.vv @@ -0,0 +1,6 @@ +enum Color { + red + green = 0 + blue = 1 + alpha = 1 +} diff --git a/vlib/v/checker/tests/enum_field_value_overflow.out b/vlib/v/checker/tests/enum_field_value_overflow.out new file mode 100644 index 0000000000..08c7236129 --- /dev/null +++ b/vlib/v/checker/tests/enum_field_value_overflow.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/enum_field_value_overflow.v:3:10: error: enum value `2147483648` overflows int + 1 | enum Color { + 2 | red + 3 | green = 2147483648 + | ~~~~~~~~~~ + 4 | blue + 5 | } \ No newline at end of file diff --git a/vlib/v/checker/tests/enum_field_value_overflow.vv b/vlib/v/checker/tests/enum_field_value_overflow.vv new file mode 100644 index 0000000000..cb9fb9af08 --- /dev/null +++ b/vlib/v/checker/tests/enum_field_value_overflow.vv @@ -0,0 +1,5 @@ +enum Color { + red + green = 2147483648 + blue +}