From 69f31d8d5c4accf766e41b46cd7a2758a377add7 Mon Sep 17 00:00:00 2001 From: Florian Blasius Date: Mon, 2 Aug 2021 06:12:29 +0200 Subject: [PATCH] checker: check int literal to enum cast (fix #10125) (#10348) --- vlib/v/ast/table.v | 6 +++++ vlib/v/checker/checker.v | 35 ++++++++++++++++++++++++++++++ vlib/v/checker/tests/enum_cast.out | 6 +++++ vlib/v/checker/tests/enum_cast.vv | 7 ++++++ vlib/v/parser/parser.v | 7 +++++- 5 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 vlib/v/checker/tests/enum_cast.out create mode 100644 vlib/v/checker/tests/enum_cast.vv diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 4b19dcf7fc..f97238a941 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -35,6 +35,7 @@ pub mut: cur_fn &FnDecl = 0 // previously stored in Checker.cur_fn and Gen.cur_fn cur_concrete_types []Type // current concrete types, e.g. gostmts int // how many `go` statements there were in the parsed files. + enum_decls map[string]EnumDecl // When table.gostmts > 0, __VTHREADS__ is defined, which can be checked with `$if threads {` } @@ -657,6 +658,11 @@ pub fn (mut t Table) register_type_symbol(typ TypeSymbol) int { return typ_idx } +[inline] +pub fn (mut t Table) register_enum_decl(enum_decl EnumDecl) { + t.enum_decls[enum_decl.name] = enum_decl +} + pub fn (t &Table) known_type(name string) bool { return t.find_type_idx(name) != 0 } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 6b6b6be6ad..dd179c99c2 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -5607,6 +5607,41 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type { if node.has_arg { c.expr(node.arg) } + + // checks on int literal to enum cast if the value represents a value on the enum + if to_type_sym.kind == .enum_ { + if node.expr is ast.IntegerLiteral { + enum_typ_name := c.table.get_type_name(node.typ) + node_val := (node.expr as ast.IntegerLiteral).val.int() + + if enum_decl := c.table.enum_decls[to_type_sym.name] { + mut in_range := false + mut enum_val := 0 + + for enum_field in enum_decl.fields { + // check if the field of the enum value is an integer literal + if enum_field.expr is ast.IntegerLiteral { + enum_val = enum_field.expr.val.int() + } + + if node_val == enum_val { + in_range = true + break + } + + enum_val += 1 + } + + if !in_range { + c.warn('$node_val does not represents a value of enum $enum_typ_name', + node.pos) + } + } + } + } + + node.typname = c.table.get_type_symbol(node.typ).name + return node.typ } diff --git a/vlib/v/checker/tests/enum_cast.out b/vlib/v/checker/tests/enum_cast.out new file mode 100644 index 0000000000..053fe1b470 --- /dev/null +++ b/vlib/v/checker/tests/enum_cast.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/enum_cast.vv:6:13: error: 12 does not represents a value of enum Color + 4 | println(Color(0)) + 5 | println(Color(10)) + 6 | println(Color(12)) + | ~~~~~~~~~ + 7 | } diff --git a/vlib/v/checker/tests/enum_cast.vv b/vlib/v/checker/tests/enum_cast.vv new file mode 100644 index 0000000000..40e9ec7e84 --- /dev/null +++ b/vlib/v/checker/tests/enum_cast.vv @@ -0,0 +1,7 @@ +enum Color { red green = 10 blue } + +fn main() { + println(Color(0)) + println(Color(10)) + println(Color(12)) +} \ No newline at end of file diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index f4575e8986..e17295e67c 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -3097,7 +3097,8 @@ fn (mut p Parser) enum_decl() ast.EnumDecl { p.error_with_pos('cannot register enum `$name`, another type with this name exists', end_pos) } - return ast.EnumDecl{ + + enum_decl := ast.EnumDecl{ name: name is_pub: is_pub is_flag: is_flag @@ -3107,6 +3108,10 @@ fn (mut p Parser) enum_decl() ast.EnumDecl { attrs: p.attrs comments: enum_decl_comments } + + p.table.register_enum_decl(enum_decl) + + return enum_decl } fn (mut p Parser) type_decl() ast.TypeDecl {