checker: check for enum value duplicates/overflow (#5659)
parent
b018381f10
commit
be04de98bb
|
@ -12,6 +12,8 @@ import v.errors
|
||||||
|
|
||||||
const (
|
const (
|
||||||
max_nr_errors = 300
|
max_nr_errors = 300
|
||||||
|
enum_min = int(0x80000000)
|
||||||
|
enum_max = 0x7FFFFFFF
|
||||||
)
|
)
|
||||||
|
|
||||||
pub struct Checker {
|
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)
|
c.warn('const $no_pub_in_main_warning', stmt.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
// TODO not a Stmt
|
// TODO not a Stmt
|
||||||
ast.ConstField {
|
ast.ConstField {
|
||||||
if stmt.is_pub {
|
if stmt.is_pub {
|
||||||
c.warn('const field `$stmt.name` $no_pub_in_main_warning', stmt.pos)
|
c.warn('const field `$stmt.name` $no_pub_in_main_warning', stmt.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
ast.EnumDecl {
|
ast.EnumDecl {
|
||||||
if stmt.is_pub {
|
if stmt.is_pub {
|
||||||
c.warn('enum `$stmt.name` $no_pub_in_main_warning', stmt.pos)
|
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 {
|
.placeholder {
|
||||||
c.error('unknown struct: $type_sym.name', struct_init.pos)
|
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 {
|
.struct_, .string, .array, .alias {
|
||||||
mut info := table.Struct{}
|
mut info := table.Struct{}
|
||||||
if type_sym.kind == .alias {
|
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 {
|
ast.AnonFn {
|
||||||
if arg_expr.decl.args.len > 1 {
|
if arg_expr.decl.args.len > 1 {
|
||||||
c.error('function needs exactly 1 argument', call_expr.pos)
|
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 {...}`',
|
c.error('type mismatch, should use `fn(a $elem_sym.name) $elem_sym.name {...}`',
|
||||||
call_expr.pos)
|
call_expr.pos)
|
||||||
} else if !is_map && (arg_expr.decl.return_type != table.bool_type ||
|
} 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 {
|
if call_arg.is_mut {
|
||||||
c.fail_if_immutable(call_arg.expr)
|
c.fail_if_immutable(call_arg.expr)
|
||||||
|
|
||||||
if !arg.is_mut {
|
if !arg.is_mut {
|
||||||
mut words := 'mutable'
|
mut words := 'mutable'
|
||||||
mut tok := 'mut'
|
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) {
|
pub fn (mut c Checker) enum_decl(decl ast.EnumDecl) {
|
||||||
c.check_valid_pascal_case(decl.name, 'enum name', decl.pos)
|
c.check_valid_pascal_case(decl.name, 'enum name', decl.pos)
|
||||||
|
mut seen := []int{}
|
||||||
for i, field in decl.fields {
|
for i, field in decl.fields {
|
||||||
if util.contains_capital(field.name) {
|
if util.contains_capital(field.name) {
|
||||||
c.error('field name `$field.name` cannot contain uppercase letters, use snake_case instead',
|
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 {
|
if field.has_expr {
|
||||||
match field.expr {
|
match field.expr as field_expr {
|
||||||
ast.IntegerLiteral {}
|
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 {}
|
ast.PrefixExpr {}
|
||||||
else {
|
else {
|
||||||
if field.expr is ast.Ident {
|
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)
|
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)
|
node.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ast.Attr {}
|
// ast.Attr {}
|
||||||
ast.AssignStmt {
|
ast.AssignStmt {
|
||||||
c.assign_stmt(mut node)
|
c.assign_stmt(mut node)
|
||||||
}
|
}
|
||||||
|
@ -1944,7 +1965,7 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ast.HashStmt {}
|
// ast.HashStmt {}
|
||||||
ast.Import {}
|
ast.Import {}
|
||||||
ast.InterfaceDecl {
|
ast.InterfaceDecl {
|
||||||
c.interface_decl(node)
|
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`'
|
unhandled << '`$v_str`'
|
||||||
}
|
}
|
||||||
} }
|
} }
|
||||||
//
|
//
|
||||||
table.Enum { for v in info.vals {
|
table.Enum { for v in info.vals {
|
||||||
if v !in branch_exprs {
|
if v !in branch_exprs {
|
||||||
is_exhaustive = false
|
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.language == .v && node.is_method && node.name == 'str' {
|
||||||
if node.return_type != table.string_type {
|
if node.return_type != table.string_type {
|
||||||
c.error('.str() methods should return `string`', node.pos)
|
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.error('.str() methods should have 0 arguments', node.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.expected_type = table.void_type
|
c.expected_type = table.void_type
|
||||||
c.cur_fn = &node
|
c.cur_fn = &node
|
||||||
c.stmts(node.stmts)
|
c.stmts(node.stmts)
|
||||||
|
|
|
@ -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 | }
|
|
@ -0,0 +1,5 @@
|
||||||
|
enum Color {
|
||||||
|
red
|
||||||
|
green = 2147483647
|
||||||
|
blue
|
||||||
|
}
|
|
@ -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 | }
|
|
@ -0,0 +1,6 @@
|
||||||
|
enum Color {
|
||||||
|
red
|
||||||
|
green = 0
|
||||||
|
blue = 1
|
||||||
|
alpha = 1
|
||||||
|
}
|
|
@ -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 | }
|
|
@ -0,0 +1,5 @@
|
||||||
|
enum Color {
|
||||||
|
red
|
||||||
|
green = 2147483648
|
||||||
|
blue
|
||||||
|
}
|
Loading…
Reference in New Issue