From 9b48ffa98af149408b8e4734676bcc2cf95278fd Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 17 Aug 2022 20:11:57 -0700 Subject: [PATCH] go/parser: disallow parenthesizing embedded types in structs This was never permitted in Go but the flexibility to do so was introduced through the generics prototype code where we experimented with parentheses to enclose type parameters. Restore original (pre-generics) behavior. Fixes #51655. Change-Id: Ia7a4b2e393e0214a70e840c8663cf4474c5c754b Reviewed-on: https://go-review.googlesource.com/c/go/+/424694 Run-TryBot: Robert Griesemer Reviewed-by: Robert Griesemer Reviewed-by: Robert Findley TryBot-Result: Gopher Robot --- src/go/parser/parser.go | 48 ++++++++++++++++++++++++++++++++----- src/go/parser/short_test.go | 12 +++++++--- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index d4ad36dc67..acb71ee9ac 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -643,7 +643,8 @@ func (p *parser) parseFieldDecl() *ast.Field { var names []*ast.Ident var typ ast.Expr - if p.tok == token.IDENT { + switch p.tok { + case token.IDENT: name := p.parseIdent() if p.tok == token.PERIOD || p.tok == token.STRING || p.tok == token.SEMICOLON || p.tok == token.RBRACE { // embedded type @@ -670,11 +671,46 @@ func (p *parser) parseFieldDecl() *ast.Field { typ = p.parseType() } } - } else { - // embedded, possibly generic type - // (using the enclosing parentheses to distinguish it from a named field declaration) - // TODO(rFindley) confirm that this doesn't allow parenthesized embedded type - typ = p.parseType() + case token.MUL: + star := p.pos + p.next() + if p.tok == token.LPAREN { + // *(T) + p.error(p.pos, "cannot parenthesize embedded type") + p.next() + typ = p.parseQualifiedIdent(nil) + // expect closing ')' but no need to complain if missing + if p.tok == token.RPAREN { + p.next() + } + } else { + // *T + typ = p.parseQualifiedIdent(nil) + } + typ = &ast.StarExpr{Star: star, X: typ} + + case token.LPAREN: + p.error(p.pos, "cannot parenthesize embedded type") + p.next() + if p.tok == token.MUL { + // (*T) + star := p.pos + p.next() + typ = &ast.StarExpr{Star: star, X: p.parseQualifiedIdent(nil)} + } else { + // (T) + typ = p.parseQualifiedIdent(nil) + } + // expect closing ')' but no need to complain if missing + if p.tok == token.RPAREN { + p.next() + } + + default: + pos := p.pos + p.errorExpected(pos, "field name or embedded type") + p.advance(exprEnd) + typ = &ast.BadExpr{From: pos, To: p.pos} } var tag *ast.BasicLit diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go index d117f0d381..be8be6450c 100644 --- a/src/go/parser/short_test.go +++ b/src/go/parser/short_test.go @@ -52,9 +52,9 @@ var valids = []string{ `package p; type T = int`, `package p; type (T = p.T; _ = struct{}; x = *T)`, `package p; type T (*int)`, - `package p; type _ struct{ ((int)) }`, - `package p; type _ struct{ (*(int)) }`, - `package p; type _ struct{ ([]byte) }`, // disallowed by type-checker + `package p; type _ struct{ int }`, + `package p; type _ struct{ pkg.T }`, + `package p; type _ struct{ *pkg.T }`, `package p; var _ = func()T(nil)`, `package p; func _(T (P))`, `package p; func _(T []E)`, @@ -195,6 +195,12 @@ var invalids = []string{ `package p; func (type /* ERROR "found 'type'" */ T)(T) _()`, `package p; type _[A+B, /* ERROR "unexpected comma" */ ] int`, + `package p; type _ struct{ [ /* ERROR "expected '}', found '\['" */ ]byte }`, + `package p; type _ struct{ ( /* ERROR "cannot parenthesize embedded type" */ int) }`, + `package p; type _ struct{ ( /* ERROR "cannot parenthesize embedded type" */ []byte) }`, + `package p; type _ struct{ *( /* ERROR "cannot parenthesize embedded type" */ int) }`, + `package p; type _ struct{ *( /* ERROR "cannot parenthesize embedded type" */ []byte) }`, + // TODO(rfindley): this error should be positioned on the ':' `package p; var a = a[[]int:[ /* ERROR "expected expression" */ ]int];`, -- 2.50.0