mirror of
https://github.com/golang/go
synced 2024-11-25 21:47:59 -07:00
gofmt/go/parser: strengthen syntax checks
- don't allow parenthesized receiver base types or anonymous fields - fixed a couple of other omissions - adjusted gofmt test script - removed several TODOs R=rsc CC=golang-dev https://golang.org/cl/1897043
This commit is contained in:
parent
12576f9e70
commit
03f42934c1
@ -41,7 +41,7 @@ apply1() {
|
|||||||
bug106.go | bug121.go | bug125.go | bug133.go | bug160.go | \
|
bug106.go | bug121.go | bug125.go | bug133.go | bug160.go | \
|
||||||
bug163.go | bug166.go | bug169.go | bug217.go | bug222.go | \
|
bug163.go | bug166.go | bug169.go | bug217.go | bug222.go | \
|
||||||
bug226.go | bug228.go | bug248.go | bug274.go | bug280.go | \
|
bug226.go | bug228.go | bug248.go | bug274.go | bug280.go | \
|
||||||
bug282.go | bug287.go ) return ;;
|
bug282.go | bug287.go | bug298.go | bug299.go | bug300.go ) return ;;
|
||||||
esac
|
esac
|
||||||
# the following directories are skipped because they contain test
|
# the following directories are skipped because they contain test
|
||||||
# cases for syntax errors and thus won't parse in the first place:
|
# cases for syntax errors and thus won't parse in the first place:
|
||||||
|
@ -508,19 +508,8 @@ func (p *parser) parseFieldDecl() *ast.Field {
|
|||||||
|
|
||||||
doc := p.leadComment
|
doc := p.leadComment
|
||||||
|
|
||||||
// a list of identifiers looks like a list of type names
|
// fields
|
||||||
var list vector.Vector
|
list, typ := p.parseVarList(false)
|
||||||
for {
|
|
||||||
// TODO(gri): do not allow ()'s here
|
|
||||||
list.Push(p.parseType())
|
|
||||||
if p.tok != token.COMMA {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we had a list of identifiers, it must be followed by a type
|
|
||||||
typ := p.tryType()
|
|
||||||
|
|
||||||
// optional tag
|
// optional tag
|
||||||
var tag *ast.BasicLit
|
var tag *ast.BasicLit
|
||||||
@ -533,15 +522,14 @@ func (p *parser) parseFieldDecl() *ast.Field {
|
|||||||
var idents []*ast.Ident
|
var idents []*ast.Ident
|
||||||
if typ != nil {
|
if typ != nil {
|
||||||
// IdentifierList Type
|
// IdentifierList Type
|
||||||
idents = p.makeIdentList(&list)
|
idents = p.makeIdentList(list)
|
||||||
} else {
|
} else {
|
||||||
// Type (anonymous field)
|
// ["*"] TypeName (AnonymousField)
|
||||||
if len(list) == 1 {
|
typ = (*list)[0].(ast.Expr) // we always have at least one element
|
||||||
// TODO(gri): check that this looks like a type
|
if len(*list) > 1 || !isTypeName(deref(typ)) {
|
||||||
typ = list.At(0).(ast.Expr)
|
pos := typ.Pos()
|
||||||
} else {
|
p.errorExpected(pos, "anonymous field")
|
||||||
p.errorExpected(p.pos, "anonymous field")
|
typ = &ast.BadExpr{pos}
|
||||||
typ = &ast.BadExpr{p.pos}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,7 +547,10 @@ func (p *parser) parseStructType() *ast.StructType {
|
|||||||
pos := p.expect(token.STRUCT)
|
pos := p.expect(token.STRUCT)
|
||||||
lbrace := p.expect(token.LBRACE)
|
lbrace := p.expect(token.LBRACE)
|
||||||
var list vector.Vector
|
var list vector.Vector
|
||||||
for p.tok == token.IDENT || p.tok == token.MUL {
|
for p.tok == token.IDENT || p.tok == token.MUL || p.tok == token.LPAREN {
|
||||||
|
// a field declaration cannot start with a '(' but we accept
|
||||||
|
// it here for more robust parsing and better error messages
|
||||||
|
// (parseFieldDecl will check and complain if necessary)
|
||||||
list.Push(p.parseFieldDecl())
|
list.Push(p.parseFieldDecl())
|
||||||
}
|
}
|
||||||
rbrace := p.expect(token.RBRACE)
|
rbrace := p.expect(token.RBRACE)
|
||||||
@ -589,8 +580,8 @@ func (p *parser) parsePointerType() *ast.StarExpr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) tryParameterType(ellipsisOk bool) ast.Expr {
|
func (p *parser) tryVarType(isParam bool) ast.Expr {
|
||||||
if ellipsisOk && p.tok == token.ELLIPSIS {
|
if isParam && p.tok == token.ELLIPSIS {
|
||||||
pos := p.pos
|
pos := p.pos
|
||||||
p.next()
|
p.next()
|
||||||
typ := p.tryType() // don't use parseType so we can provide better error message
|
typ := p.tryType() // don't use parseType so we can provide better error message
|
||||||
@ -607,8 +598,8 @@ func (p *parser) tryParameterType(ellipsisOk bool) ast.Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseParameterType(ellipsisOk bool) ast.Expr {
|
func (p *parser) parseVarType(isParam bool) ast.Expr {
|
||||||
typ := p.tryParameterType(ellipsisOk)
|
typ := p.tryVarType(isParam)
|
||||||
if typ == nil {
|
if typ == nil {
|
||||||
p.errorExpected(p.pos, "type")
|
p.errorExpected(p.pos, "type")
|
||||||
p.next() // make progress
|
p.next() // make progress
|
||||||
@ -618,16 +609,19 @@ func (p *parser) parseParameterType(ellipsisOk bool) ast.Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (p *parser) parseParameterDecl(ellipsisOk bool) (*vector.Vector, ast.Expr) {
|
func (p *parser) parseVarList(isParam bool) (*vector.Vector, ast.Expr) {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, "ParameterDecl"))
|
defer un(trace(p, "VarList"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// a list of identifiers looks like a list of type names
|
// a list of identifiers looks like a list of type names
|
||||||
var list vector.Vector
|
var list vector.Vector
|
||||||
for {
|
for {
|
||||||
// TODO(gri): do not allow ()'s here
|
// parseVarType accepts any type (including parenthesized ones)
|
||||||
list.Push(p.parseParameterType(ellipsisOk))
|
// even though the syntax does not permit them here: we
|
||||||
|
// accept them all for more robust parsing and complain
|
||||||
|
// afterwards
|
||||||
|
list.Push(p.parseVarType(isParam))
|
||||||
if p.tok != token.COMMA {
|
if p.tok != token.COMMA {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -635,7 +629,7 @@ func (p *parser) parseParameterDecl(ellipsisOk bool) (*vector.Vector, ast.Expr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if we had a list of identifiers, it must be followed by a type
|
// if we had a list of identifiers, it must be followed by a type
|
||||||
typ := p.tryParameterType(ellipsisOk)
|
typ := p.tryVarType(isParam)
|
||||||
|
|
||||||
return &list, typ
|
return &list, typ
|
||||||
}
|
}
|
||||||
@ -646,7 +640,7 @@ func (p *parser) parseParameterList(ellipsisOk bool) []*ast.Field {
|
|||||||
defer un(trace(p, "ParameterList"))
|
defer un(trace(p, "ParameterList"))
|
||||||
}
|
}
|
||||||
|
|
||||||
list, typ := p.parseParameterDecl(ellipsisOk)
|
list, typ := p.parseVarList(ellipsisOk)
|
||||||
if typ != nil {
|
if typ != nil {
|
||||||
// IdentifierList Type
|
// IdentifierList Type
|
||||||
idents := p.makeIdentList(list)
|
idents := p.makeIdentList(list)
|
||||||
@ -658,7 +652,7 @@ func (p *parser) parseParameterList(ellipsisOk bool) []*ast.Field {
|
|||||||
|
|
||||||
for p.tok != token.RPAREN && p.tok != token.EOF {
|
for p.tok != token.RPAREN && p.tok != token.EOF {
|
||||||
idents := p.parseIdentList(ast.Var)
|
idents := p.parseIdentList(ast.Var)
|
||||||
typ := p.parseParameterType(ellipsisOk)
|
typ := p.parseVarType(ellipsisOk)
|
||||||
list.Push(&ast.Field{nil, idents, typ, nil, nil})
|
list.Push(&ast.Field{nil, idents, typ, nil, nil})
|
||||||
if p.tok != token.COMMA {
|
if p.tok != token.COMMA {
|
||||||
break
|
break
|
||||||
@ -1119,21 +1113,16 @@ func (p *parser) parseCompositeLit(typ ast.Expr) ast.Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO(gri): Consider different approach to checking syntax after parsing:
|
|
||||||
// Provide a arguments (set of flags) to parsing functions
|
|
||||||
// restricting what they are supposed to accept depending
|
|
||||||
// on context.
|
|
||||||
|
|
||||||
// checkExpr checks that x is an expression (and not a type).
|
// checkExpr checks that x is an expression (and not a type).
|
||||||
func (p *parser) checkExpr(x ast.Expr) ast.Expr {
|
func (p *parser) checkExpr(x ast.Expr) ast.Expr {
|
||||||
// TODO(gri): should provide predicate in AST nodes
|
switch t := unparen(x).(type) {
|
||||||
switch t := x.(type) {
|
|
||||||
case *ast.BadExpr:
|
case *ast.BadExpr:
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
case *ast.BasicLit:
|
case *ast.BasicLit:
|
||||||
case *ast.FuncLit:
|
case *ast.FuncLit:
|
||||||
case *ast.CompositeLit:
|
case *ast.CompositeLit:
|
||||||
case *ast.ParenExpr:
|
case *ast.ParenExpr:
|
||||||
|
panic("unreachable")
|
||||||
case *ast.SelectorExpr:
|
case *ast.SelectorExpr:
|
||||||
case *ast.IndexExpr:
|
case *ast.IndexExpr:
|
||||||
case *ast.SliceExpr:
|
case *ast.SliceExpr:
|
||||||
@ -1161,16 +1150,14 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// isTypeName returns true iff x is type name.
|
// isTypeName returns true iff x is a (qualified) TypeName.
|
||||||
func isTypeName(x ast.Expr) bool {
|
func isTypeName(x ast.Expr) bool {
|
||||||
// TODO(gri): should provide predicate in AST nodes
|
|
||||||
switch t := x.(type) {
|
switch t := x.(type) {
|
||||||
case *ast.BadExpr:
|
case *ast.BadExpr:
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
case *ast.ParenExpr:
|
|
||||||
return isTypeName(t.X) // TODO(gri): should (TypeName) be illegal?
|
|
||||||
case *ast.SelectorExpr:
|
case *ast.SelectorExpr:
|
||||||
return isTypeName(t.X)
|
_, isIdent := t.X.(*ast.Ident)
|
||||||
|
return isIdent
|
||||||
default:
|
default:
|
||||||
return false // all other nodes are not type names
|
return false // all other nodes are not type names
|
||||||
}
|
}
|
||||||
@ -1178,16 +1165,14 @@ func isTypeName(x ast.Expr) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// isCompositeLitType returns true iff x is a legal composite literal type.
|
// isLiteralType returns true iff x is a legal composite literal type.
|
||||||
func isCompositeLitType(x ast.Expr) bool {
|
func isLiteralType(x ast.Expr) bool {
|
||||||
// TODO(gri): should provide predicate in AST nodes
|
|
||||||
switch t := x.(type) {
|
switch t := x.(type) {
|
||||||
case *ast.BadExpr:
|
case *ast.BadExpr:
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
case *ast.ParenExpr:
|
|
||||||
return isCompositeLitType(t.X)
|
|
||||||
case *ast.SelectorExpr:
|
case *ast.SelectorExpr:
|
||||||
return isTypeName(t.X)
|
_, isIdent := t.X.(*ast.Ident)
|
||||||
|
return isIdent
|
||||||
case *ast.ArrayType:
|
case *ast.ArrayType:
|
||||||
case *ast.StructType:
|
case *ast.StructType:
|
||||||
case *ast.MapType:
|
case *ast.MapType:
|
||||||
@ -1198,12 +1183,31 @@ func isCompositeLitType(x ast.Expr) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If x is of the form *T, deref returns T, otherwise it returns x.
|
||||||
|
func deref(x ast.Expr) ast.Expr {
|
||||||
|
if p, isPtr := x.(*ast.StarExpr); isPtr {
|
||||||
|
x = p.X
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If x is of the form (T), unparen returns unparen(T), otherwise it returns x.
|
||||||
|
func unparen(x ast.Expr) ast.Expr {
|
||||||
|
if p, isParen := x.(*ast.ParenExpr); isParen {
|
||||||
|
x = unparen(p.X)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// checkExprOrType checks that x is an expression or a type
|
// checkExprOrType checks that x is an expression or a type
|
||||||
// (and not a raw type such as [...]T).
|
// (and not a raw type such as [...]T).
|
||||||
//
|
//
|
||||||
func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
|
func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
|
||||||
// TODO(gri): should provide predicate in AST nodes
|
switch t := unparen(x).(type) {
|
||||||
switch t := x.(type) {
|
case *ast.ParenExpr:
|
||||||
|
panic("unreachable")
|
||||||
case *ast.UnaryExpr:
|
case *ast.UnaryExpr:
|
||||||
if t.Op == token.RANGE {
|
if t.Op == token.RANGE {
|
||||||
// the range operator is only allowed at the top of a for statement
|
// the range operator is only allowed at the top of a for statement
|
||||||
@ -1238,7 +1242,7 @@ L:
|
|||||||
case token.LPAREN:
|
case token.LPAREN:
|
||||||
x = p.parseCallOrConversion(p.checkExprOrType(x))
|
x = p.parseCallOrConversion(p.checkExprOrType(x))
|
||||||
case token.LBRACE:
|
case token.LBRACE:
|
||||||
if isCompositeLitType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
|
if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
|
||||||
x = p.parseCompositeLit(x)
|
x = p.parseCompositeLit(x)
|
||||||
} else {
|
} else {
|
||||||
break L
|
break L
|
||||||
@ -1919,7 +1923,7 @@ func parseVarSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
|
|||||||
|
|
||||||
func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.GenDecl {
|
func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.GenDecl {
|
||||||
if p.trace {
|
if p.trace {
|
||||||
defer un(trace(p, keyword.String()+"Decl"))
|
defer un(trace(p, "GenDecl("+keyword.String()+")"))
|
||||||
}
|
}
|
||||||
|
|
||||||
doc := p.leadComment
|
doc := p.leadComment
|
||||||
@ -1960,17 +1964,15 @@ func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
|
|||||||
if par.NumFields() != 1 {
|
if par.NumFields() != 1 {
|
||||||
p.errorExpected(pos, "exactly one receiver")
|
p.errorExpected(pos, "exactly one receiver")
|
||||||
par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{noPos}}}
|
par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{noPos}}}
|
||||||
|
return par
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// recv type must be of the form ["*"] identifier
|
||||||
recv := par.List[0]
|
recv := par.List[0]
|
||||||
|
base := deref(recv.Type)
|
||||||
// recv type must be TypeName or *TypeName
|
if _, isIdent := base.(*ast.Ident); !isIdent {
|
||||||
base := recv.Type
|
p.errorExpected(base.Pos(), "(unqualified) identifier")
|
||||||
if ptr, isPtr := base.(*ast.StarExpr); isPtr {
|
par.List = []*ast.Field{&ast.Field{Type: &ast.BadExpr{recv.Pos()}}}
|
||||||
base = ptr.X
|
|
||||||
}
|
|
||||||
if !isTypeName(base) {
|
|
||||||
p.errorExpected(base.Pos(), "type name")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return par
|
return par
|
||||||
|
Loading…
Reference in New Issue
Block a user