checker: check for enum value duplicates/overflow (#5659)

pull/5662/head
div72 2020-07-04 18:28:01 +03:00 committed by GitHub
parent b018381f10
commit be04de98bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 74 additions and 13 deletions

View File

@ -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)

View File

@ -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 | }

View File

@ -0,0 +1,5 @@
enum Color {
red
green = 2147483647
blue
}

View File

@ -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 | }

View File

@ -0,0 +1,6 @@
enum Color {
red
green = 0
blue = 1
alpha = 1
}

View File

@ -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 | }

View File

@ -0,0 +1,5 @@
enum Color {
red
green = 2147483648
blue
}