From 5b3f29a2e791c2489e6de16d44f3a626ce81a0ec Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 14 Sep 2015 16:30:37 -0700 Subject: [PATCH] go/parser: comma is not permitted at the end of a struct field list Fixes #11611. Change-Id: I63d35cf15c3be759c899e3e561e631330dcc0bbb Reviewed-on: https://go-review.googlesource.com/14565 Run-TryBot: Robert Griesemer TryBot-Result: Gobot Gobot Reviewed-by: Chris Manghane --- src/go/parser/parser.go | 71 ++++++++++++++++++------------------- src/go/parser/short_test.go | 27 ++++++++++---- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index 855caa3daa..521bdce1dd 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -695,16 +695,19 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field { doc := p.leadComment - // FieldDecl - list, typ := p.parseVarList(false) - - // Tag - var tag *ast.BasicLit - if p.tok == token.STRING { - tag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit} + // 1st FieldDecl + // A type name used as an anonymous field looks like a field identifier. + var list []ast.Expr + for { + list = append(list, p.parseVarType(false)) + if p.tok != token.COMMA { + break + } p.next() } + typ := p.tryVarType(false) + // analyze case var idents []*ast.Ident if typ != nil { @@ -713,13 +716,22 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field { } else { // ["*"] TypeName (AnonymousField) typ = list[0] // we always have at least one element - if n := len(list); n > 1 || !isTypeName(deref(typ)) { - pos := typ.Pos() - p.errorExpected(pos, "anonymous field") - typ = &ast.BadExpr{From: pos, To: p.safePos(list[n-1].End())} + if n := len(list); n > 1 { + p.errorExpected(p.pos, "type") + typ = &ast.BadExpr{From: p.pos, To: p.pos} + } else if !isTypeName(deref(typ)) { + p.errorExpected(typ.Pos(), "anonymous field") + typ = &ast.BadExpr{From: typ.Pos(), To: p.safePos(typ.End())} } } + // Tag + var tag *ast.BasicLit + if p.tok == token.STRING { + tag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit} + p.next() + } + p.expectSemi() // call before accessing p.linecomment field := &ast.Field{Doc: doc, Names: idents, Type: typ, Tag: tag, Comment: p.lineComment} @@ -796,42 +808,27 @@ func (p *parser) parseVarType(isParam bool) ast.Expr { return typ } -// If any of the results are identifiers, they are not resolved. -func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) { +func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params []*ast.Field) { if p.trace { - defer un(trace(p, "VarList")) + defer un(trace(p, "ParameterList")) } - // a list of identifiers looks like a list of type names - // - // parse/tryVarType accepts any type (including parenthesized - // ones) even though the syntax does not permit them here: we - // accept them all for more robust parsing and complain later - for typ := p.parseVarType(isParam); typ != nil; { - list = append(list, typ) + // 1st ParameterDecl + // A list of identifiers looks like a list of type names. + var list []ast.Expr + for { + list = append(list, p.parseVarType(ellipsisOk)) if p.tok != token.COMMA { break } p.next() - typ = p.tryVarType(isParam) // maybe nil as in: func f(int,) {} - } - - // if we had a list of identifiers, it must be followed by a type - typ = p.tryVarType(isParam) - - return -} - -func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params []*ast.Field) { - if p.trace { - defer un(trace(p, "ParameterList")) + if p.tok == token.RPAREN { + break + } } - // ParameterDecl - list, typ := p.parseVarList(ellipsisOk) - // analyze case - if typ != nil { + if typ := p.tryVarType(ellipsisOk); typ != nil { // IdentifierList Type idents := p.makeIdentList(list) field := &ast.Field{Names: idents, Type: typ} diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go index 7cbdaf2e24..6ef3b07255 100644 --- a/src/go/parser/short_test.go +++ b/src/go/parser/short_test.go @@ -101,13 +101,26 @@ var invalids = []string{ `package p; func f() { defer func() {} /* ERROR HERE "function must be invoked" */ }`, `package p; func f() { go func() { func() { f(x func /* ERROR "missing ','" */ (){}) } } }`, `package p; func f(x func(), u v func /* ERROR "missing ','" */ ()){}`, - `package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`, // issue 8656 - `package p; var x /* ERROR "missing variable type or initialization" */ , y, z;`, // issue 9639 - `package p; const x /* ERROR "missing constant value" */ ;`, // issue 9639 - `package p; const x /* ERROR "missing constant value" */ int;`, // issue 9639 - `package p; const (x = 0; y; z /* ERROR "missing constant value" */ int);`, // issue 9639 - `package p; var _ = struct { x int, /* ERROR "expected ';', found ','" */ }{}`, // issue 12437 - `package p; var _ = struct { x int, /* ERROR "expected ';', found ','" */ y float }{}`, // issue 12437 + + // issue 8656 + `package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`, + + // issue 9639 + `package p; var x /* ERROR "missing variable type or initialization" */ , y, z;`, + `package p; const x /* ERROR "missing constant value" */ ;`, + `package p; const x /* ERROR "missing constant value" */ int;`, + `package p; const (x = 0; y; z /* ERROR "missing constant value" */ int);`, + + // issue 12437 + `package p; var _ = struct { x int, /* ERROR "expected ';', found ','" */ }{};`, + `package p; var _ = struct { x int, /* ERROR "expected ';', found ','" */ y float }{};`, + + // issue 11611 + `package p; type _ struct { int, } /* ERROR "expected type, found '}'" */ ;`, + `package p; type _ struct { int, float } /* ERROR "expected type, found '}'" */ ;`, + `package p; type _ struct { ( /* ERROR "expected anonymous field" */ int) };`, + `package p; func _()(x, y, z ... /* ERROR "expected '\)', found '...'" */ int){}`, + `package p; func _()(... /* ERROR "expected type, found '...'" */ int){}`, } func TestInvalid(t *testing.T) { -- 2.48.1