ast: expr.Position; struct field refactoring

pull/4460/head
Daniel Däschle 2020-04-17 02:38:39 +02:00 committed by GitHub
parent 8bb11d9035
commit 402e55d115
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 223 additions and 173 deletions

View File

@ -157,15 +157,23 @@ pub:
field_names []string field_names []string
} }
pub struct StructInitField {
pub:
name string
expr Expr
pos token.Position
mut:
typ table.Type
expected_type table.Type
}
pub struct StructInit { pub struct StructInit {
pub: pub:
pos token.Position pos token.Position
fields []string fields []StructInitField
exprs []Expr is_short bool
mut: mut:
typ table.Type typ table.Type
expr_types []table.Type
expected_types []table.Type
} }
// import statement // import statement
@ -723,3 +731,86 @@ pub fn expr_is_call(expr Expr) bool {
} }
} }
} }
fn (expr Expr) position() token.Position {
// all uncommented have to be implemented
match mut expr {
ArrayInit {
return it.pos
}
AsCast {
return it.pos
}
// ast.Ident { }
AssignExpr {
return it.pos
}
// ast.CastExpr { }
Assoc {
return it.pos
}
// ast.BoolLiteral { }
CallExpr {
return it.pos
}
// ast.CharLiteral { }
EnumVal {
return it.pos
}
// ast.FloatLiteral { }
IfExpr {
return it.pos
}
// ast.IfGuardExpr { }
IndexExpr {
return it.pos
}
InfixExpr {
left_pos := it.left.position()
right_pos := it.right.position()
if left_pos.pos == 0 || right_pos.pos == 0 {
return it.pos
}
return token.Position{
line_nr: it.pos.line_nr
pos: left_pos.pos
len: right_pos.pos - left_pos.pos + right_pos.len
}
}
IntegerLiteral {
return it.pos
}
MapInit {
return it.pos
}
MatchExpr {
return it.pos
}
PostfixExpr {
return it.pos
}
// ast.None { }
PrefixExpr {
return it.pos
}
// ast.ParExpr { }
SelectorExpr {
return it.pos
}
// ast.SizeOf { }
StringLiteral {
return it.pos
}
StringInterLiteral {
return it.pos
}
// ast.Type { }
StructInit {
return it.pos
}
// ast.TypeOf { }
else {
return token.Position{}
}
}
}

View File

@ -123,56 +123,51 @@ pub fn (c mut Checker) struct_init(struct_init mut ast.StructInit) table.Type {
// string & array are also structs but .kind of string/array // string & array are also structs but .kind of string/array
.struct_, .string, .array { .struct_, .string, .array {
info := type_sym.info as table.Struct info := type_sym.info as table.Struct
is_short_syntax := struct_init.fields.len == 0 if struct_init.is_short && struct_init.fields.len > info.fields.len {
if struct_init.exprs.len > info.fields.len {
c.error('too many fields', struct_init.pos) c.error('too many fields', struct_init.pos)
} }
mut inited_fields := []string mut inited_fields := []string
for i, expr in struct_init.exprs { for i, field in struct_init.fields {
if is_short_syntax && i >= info.fields.len { mut info_field := table.Field{}
// It doesn't make sense to check for fields that don't exist. mut field_name := ''
// We should just stop here. if struct_init.is_short {
break if i >= info.fields.len {
} // It doesn't make sense to check for fields that don't exist.
// struct_field info. // We should just stop here.
field_name := if is_short_syntax { info.fields[i].name } else { struct_init.fields[i] } break
if field_name in inited_fields { }
c.error('duplicate field name in struct literal: `$field_name`', struct_init.pos) info_field = info.fields[i]
continue field_name = info_field.name
} struct_init.fields[i].name = field_name
inited_fields << field_name
mut field := if is_short_syntax {
info.fields[i]
} else { } else {
// There is no guarantee that `i` will not be out of bounds of `info.fields` field_name = field.name
// So we just use an empty field as placeholder here. mut exists := false
table.Field{}
}
if !is_short_syntax {
mut found_field := false
for f in info.fields { for f in info.fields {
if f.name == field_name { if f.name == field_name {
field = f info_field = f
found_field = true exists = true
break break
} }
} }
if !found_field { if !exists {
c.error('struct init: no such field `$field_name` for struct `$type_sym.name`', c.error('struct init: no such field `$field.name` for struct `$type_sym.name`', field.pos)
struct_init.pos) continue
}
if field_name in inited_fields {
c.error('duplicate field name in struct literal: `$field_name`', field.pos)
continue continue
} }
} }
c.expected_type = field.typ inited_fields << field_name
expr_type := c.expr(expr) c.expected_type = info_field.typ
expr_type := c.expr(field.expr)
expr_type_sym := c.table.get_type_symbol(expr_type) expr_type_sym := c.table.get_type_symbol(expr_type)
field_type_sym := c.table.get_type_symbol(field.typ) field_type_sym := c.table.get_type_symbol(info_field.typ)
if !c.table.check(expr_type, field.typ) { if !c.table.check(expr_type, info_field.typ) {
c.error('cannot assign `$expr_type_sym.name` as `$field_type_sym.name` for field `$field.name`', c.error('cannot assign `$expr_type_sym.name` as `$field_type_sym.name` for field `$info_field.name`', field.pos)
struct_init.pos)
} }
struct_init.expr_types << expr_type struct_init.fields[i].typ = expr_type
struct_init.expected_types << field.typ struct_init.fields[i].expected_type = info_field.typ
} }
// Check uninitialized refs // Check uninitialized refs
for field in info.fields { for field in info.fields {
@ -206,7 +201,7 @@ pub fn (c mut Checker) infix_expr(infix_expr mut ast.InfixExpr) table.Type {
if infix_expr.op == .left_shift { if infix_expr.op == .left_shift {
if left.kind != .array && !left.is_int() { if left.kind != .array && !left.is_int() {
// c.error('<< can only be used with numbers and arrays', infix_expr.pos) // c.error('<< can only be used with numbers and arrays', infix_expr.pos)
c.error('cannot shift type $right.name into $left.name', expr_pos(infix_expr.right)) c.error('cannot shift type $right.name into $left.name', infix_expr.right.position())
return table.void_type return table.void_type
} }
if left.kind == .array { if left.kind == .array {
@ -220,7 +215,7 @@ pub fn (c mut Checker) infix_expr(infix_expr mut ast.InfixExpr) table.Type {
// []T << []T // []T << []T
return table.void_type return table.void_type
} }
c.error('cannot shift type $right.name into $left.name', expr_pos(infix_expr.right)) c.error('cannot shift type $right.name into $left.name', infix_expr.right.position())
return table.void_type return table.void_type
} }
} }
@ -279,7 +274,7 @@ fn (c mut Checker) assign_expr(assign_expr mut ast.AssignExpr) {
left_type_sym := c.table.get_type_symbol(left_type) left_type_sym := c.table.get_type_symbol(left_type)
right_type_sym := c.table.get_type_symbol(right_type) right_type_sym := c.table.get_type_symbol(right_type)
c.error('cannot assign `$right_type_sym.name` to variable `${assign_expr.left.str()}` of type `$left_type_sym.name`', c.error('cannot assign `$right_type_sym.name` to variable `${assign_expr.left.str()}` of type `$left_type_sym.name`',
expr_pos(assign_expr.val)) assign_expr.val.position())
} }
c.check_expr_opt_call(assign_expr.val, right_type, true) c.check_expr_opt_call(assign_expr.val, right_type, true)
} }
@ -662,7 +657,7 @@ pub fn (c mut Checker) enum_decl(decl ast.EnumDecl) {
ast.IntegerLiteral {} ast.IntegerLiteral {}
ast.PrefixExpr {} ast.PrefixExpr {}
else { else {
mut pos := expr_pos(field.expr) mut pos := field.expr.position()
if pos.pos == 0 { if pos.pos == 0 {
pos = field.pos pos = field.pos
} }
@ -999,7 +994,7 @@ fn (c mut Checker) stmt(node ast.Stmt) {
value_type := c.table.value_type(typ) value_type := c.table.value_type(typ)
if value_type == table.void_type { if value_type == table.void_type {
typ_sym := c.table.get_type_symbol(typ) typ_sym := c.table.get_type_symbol(typ)
c.error('for in: cannot index `$typ_sym.name`', expr_pos(it.cond)) c.error('for in: cannot index `$typ_sym.name`', it.cond.position())
} }
it.cond_type = typ it.cond_type = typ
it.kind = sym.kind it.kind = sym.kind
@ -1011,7 +1006,7 @@ fn (c mut Checker) stmt(node ast.Stmt) {
} }
ast.GoStmt { ast.GoStmt {
if !is_call_expr(it.call_expr) { if !is_call_expr(it.call_expr) {
c.error('expression in `go` must be a function call', expr_pos(it.call_expr))1 c.error('expression in `go` must be a function call', it.call_expr.position())
} }
c.expr(it.call_expr) c.expr(it.call_expr)
} }
@ -1196,89 +1191,6 @@ pub fn (c mut Checker) expr(node ast.Expr) table.Type {
return table.void_type return table.void_type
} }
fn expr_pos(node ast.Expr) token.Position {
// all uncommented have to be implemented
match mut node {
ast.ArrayInit {
return it.pos
}
ast.AsCast {
return it.pos
}
// ast.Ident { }
ast.AssignExpr {
return it.pos
}
// ast.CastExpr { }
ast.Assoc {
return it.pos
}
// ast.BoolLiteral { }
ast.CallExpr {
return it.pos
}
// ast.CharLiteral { }
ast.EnumVal {
return it.pos
}
// ast.FloatLiteral { }
ast.IfExpr {
return it.pos
}
// ast.IfGuardExpr { }
ast.IndexExpr {
return it.pos
}
ast.InfixExpr {
left_pos := expr_pos(it.left)
right_pos := expr_pos(it.right)
if left_pos.pos == 0 || right_pos.pos == 0 {
return it.pos
}
return token.Position{
line_nr: it.pos.line_nr
pos: left_pos.pos
len: right_pos.pos - left_pos.pos + right_pos.len
}
}
ast.IntegerLiteral {
return it.pos
}
ast.MapInit {
return it.pos
}
ast.MatchExpr {
return it.pos
}
ast.PostfixExpr {
return it.pos
}
// ast.None { }
ast.PrefixExpr {
return it.pos
}
// ast.ParExpr { }
ast.SelectorExpr {
return it.pos
}
// ast.SizeOf { }
ast.StringLiteral {
return it.pos
}
ast.StringInterLiteral {
return it.pos
}
// ast.Type { }
ast.StructInit {
return it.pos
}
// ast.TypeOf { }
else {
return token.Position{}
}
}
}
pub fn (c mut Checker) ident(ident mut ast.Ident) table.Type { pub fn (c mut Checker) ident(ident mut ast.Ident) table.Type {
if ident.name == c.var_decl_name { // c.checked_ident { if ident.name == c.var_decl_name { // c.checked_ident {
c.error('unresolved: `$ident.name`', ident.pos) c.error('unresolved: `$ident.name`', ident.pos)

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/inout/short_struct_too_many.v:6:7: error: too many fields
4|
5| fn main() {
6| t := Test{true, false}
~~~~~~~~~~~~~~~~~
7| }

View File

@ -0,0 +1,7 @@
struct Test {
foo bool
}
fn main() {
t := Test{true, false}
}

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/inout/struct_unknown_field.v:8:9: error: struct init: no such field `bar` for struct `Test`
6| t := Test{
7| foo: true
8| bar: false
^
9| }
10| }

View File

@ -0,0 +1,10 @@
struct Test {
foo bool
}
fn main() {
t := Test{
foo: true
bar: false
}
}

View File

@ -695,15 +695,15 @@ fn (f mut Fmt) expr(node ast.Expr) {
if name == 'void' { if name == 'void' {
name = '' name = ''
} }
if it.fields.len == 0 && it.exprs.len == 0 { if it.fields.len == 0 {
// `Foo{}` on one line if there are no fields // `Foo{}` on one line if there are no fields
f.write('$name{}') f.write('$name{}')
} else if it.fields.len == 0 { } else if it.fields.len == 0 {
// `Foo{1,2,3}` (short syntax ) // `Foo{1,2,3}` (short syntax )
f.write('$name{') f.write('$name{')
for i, expr in it.exprs { for i, field in it.fields {
f.expr(expr) f.expr(field.expr)
if i < it.exprs.len - 1 { if i < it.fields.len - 1 {
f.write(', ') f.write(', ')
} }
} }
@ -712,8 +712,8 @@ fn (f mut Fmt) expr(node ast.Expr) {
f.writeln('$name{') f.writeln('$name{')
f.indent++ f.indent++
for i, field in it.fields { for i, field in it.fields {
f.write('$field: ') f.write('$field.name: ')
f.expr(it.exprs[i]) f.expr(field.expr)
f.writeln('') f.writeln('')
} }
f.indent-- f.indent--

View File

@ -1918,22 +1918,22 @@ fn (var g Gen) struct_init(struct_init ast.StructInit) {
} else { } else {
g.writeln('($styp){') g.writeln('($styp){')
} }
var fields := []string // var fields := []string
var inited_fields := []string // TODO this is done in checker, move to ast node var inited_fields := []string // TODO this is done in checker, move to ast node
if struct_init.fields.len == 0 && struct_init.exprs.len > 0 { /*if struct_init.fields.len == 0 && struct_init.exprs.len > 0 {
// Get fields for {a,b} short syntax. Fields array wasn't set in the parser. // Get fields for {a,b} short syntax. Fields array wasn't set in the parser.
for f in info.fields { for f in info.fields {
fields << f.name fields << f.name
} }
} else { } else {
fields = struct_init.fields fields = struct_init.fields
} }*/
// User set fields // User set fields
for i, field in fields { for i, field in struct_init.fields {
field_name := c_name(field) field_name := c_name(field.name)
inited_fields << field inited_fields << field.name
g.write('\t.$field_name = ') g.write('\t.$field_name = ')
g.expr_with_cast(struct_init.exprs[i], struct_init.expr_types[i], struct_init.expected_types[i]) g.expr_with_cast(field.expr, field.typ, field.expected_type)
g.writeln(',') g.writeln(',')
} }
// The rest of the fields are zeroed. // The rest of the fields are zeroed.

View File

@ -964,8 +964,8 @@ fn (g mut JsGen) gen_struct_init(it ast.StructInit) {
g.writeln('new ${type_sym.name}({') g.writeln('new ${type_sym.name}({')
g.inc_indent() g.inc_indent()
for i, field in it.fields { for i, field in it.fields {
g.write('$field: ') g.write('$field.name: ')
g.expr(it.exprs[i]) g.expr(field.expr)
if i < it.fields.len - 1 { if i < it.fields.len - 1 {
g.write(', ') g.write(', ')
} }

View File

@ -570,6 +570,7 @@ pub fn (var p Parser) parse_ident(is_c, is_js bool) ast.Ident {
} }
fn (var p Parser) struct_init(short_syntax bool) ast.StructInit { fn (var p Parser) struct_init(short_syntax bool) ast.StructInit {
first_pos := p.tok.position()
typ := if short_syntax { table.void_type } else { p.parse_type() } typ := if short_syntax { table.void_type } else { p.parse_type() }
p.expr_mod = '' p.expr_mod = ''
// sym := p.table.get_type_symbol(typ) // sym := p.table.get_type_symbol(typ)
@ -577,8 +578,7 @@ fn (var p Parser) struct_init(short_syntax bool) ast.StructInit {
if !short_syntax { if !short_syntax {
p.check(.lcbr) p.check(.lcbr)
} }
var field_names := []string var fields := []ast.StructInitField
var exprs := []ast.Expr
var i := 0 var i := 0
is_short_syntax := p.peek_tok.kind != .colon && p.tok.kind != .rcbr // `Vec{a,b,c} is_short_syntax := p.peek_tok.kind != .colon && p.tok.kind != .rcbr // `Vec{a,b,c}
// p.warn(is_short_syntax.str()) // p.warn(is_short_syntax.str())
@ -587,15 +587,27 @@ fn (var p Parser) struct_init(short_syntax bool) ast.StructInit {
var field_name := '' var field_name := ''
if is_short_syntax { if is_short_syntax {
expr := p.expr(0) expr := p.expr(0)
exprs << expr fields << ast.StructInitField{
// name will be set later in checker
expr: expr
pos: expr.position()
}
} else { } else {
first_field_pos := p.tok.position()
field_name = p.check_name() field_name = p.check_name()
field_names << field_name
}
if !is_short_syntax {
p.check(.colon) p.check(.colon)
expr := p.expr(0) expr := p.expr(0)
exprs << expr last_field_pos := expr.position()
field_pos := token.Position{
line_nr: first_field_pos.line_nr
pos: first_field_pos.pos
len: last_field_pos.pos - first_field_pos.pos + last_field_pos.len
}
fields << ast.StructInitField{
name: field_name
expr: expr
pos: field_pos
}
} }
i++ i++
if p.tok.kind == .comma { if p.tok.kind == .comma {
@ -603,15 +615,20 @@ fn (var p Parser) struct_init(short_syntax bool) ast.StructInit {
} }
p.check_comment() p.check_comment()
} }
node := ast.StructInit{ last_pos := p.tok.position()
typ: typ
exprs: exprs
fields: field_names
pos: p.tok.position()
}
if !short_syntax { if !short_syntax {
p.check(.rcbr) p.check(.rcbr)
} }
node := ast.StructInit{
typ: typ
fields: fields
pos: token.Position{
line_nr: first_pos.line_nr
pos: first_pos.pos
len: last_pos.pos - first_pos.pos + last_pos.len
}
is_short: is_short_syntax
}
return node return node
} }