go/parser: refactor parameter parsing (cleanup)

Refactor parser.parseParameters to only parse
ordinary parameters. Introduce a variant to
parse type parameters.

In the two places where we need ordinary and type
parameters, call the function twice.

Also, use a range loop in two places which is a
bit easier to read.

Change-Id: I0a62e1c508d6ccd16b7cb6e1b852ab1d32224ec2
Reviewed-on: https://go-review.googlesource.com/c/go/+/630816
Auto-Submit: Robert Griesemer <gri@google.com>
TryBot-Bypass: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Commit-Queue: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer 2024-11-21 15:53:05 -08:00 committed by Robert Griesemer
parent 530c829270
commit 64eed8ef1d
1 changed files with 49 additions and 41 deletions

View File

@ -931,7 +931,7 @@ func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing tok
// distribute parameter types (len(list) > 0) // distribute parameter types (len(list) > 0)
if named == 0 { if named == 0 {
// all unnamed => found names are type names // all unnamed => found names are type names
for i := 0; i < len(list); i++ { for i := range list {
par := &list[i] par := &list[i]
if typ := par.name; typ != nil { if typ := par.name; typ != nil {
par.typ = typ par.typ = typ
@ -959,8 +959,8 @@ func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing tok
// some named or we're in a type parameter list => all must be named // some named or we're in a type parameter list => all must be named
var errPos token.Pos // left-most error position (or invalid) var errPos token.Pos // left-most error position (or invalid)
var typ ast.Expr // current type (from right to left) var typ ast.Expr // current type (from right to left)
for i := len(list) - 1; i >= 0; i-- { for i := range list {
if par := &list[i]; par.typ != nil { if par := &list[len(list)-i-1]; par.typ != nil {
typ = par.typ typ = par.typ
if par.name == nil { if par.name == nil {
errPos = typ.Pos() errPos = typ.Pos()
@ -1042,36 +1042,39 @@ func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing tok
return return
} }
func (p *parser) parseParameters(acceptTParams bool) (tparams, params *ast.FieldList) { func (p *parser) parseTypeParameters() *ast.FieldList {
if p.trace {
defer un(trace(p, "TypeParameters"))
}
lbrack := p.expect(token.LBRACK)
var list []*ast.Field
if p.tok != token.RBRACK {
list = p.parseParameterList(nil, nil, token.RBRACK)
}
rbrack := p.expect(token.RBRACK)
if len(list) == 0 {
p.error(rbrack, "empty type parameter list")
return nil // avoid follow-on errors
}
return &ast.FieldList{Opening: lbrack, List: list, Closing: rbrack}
}
func (p *parser) parseParameters() *ast.FieldList {
if p.trace { if p.trace {
defer un(trace(p, "Parameters")) defer un(trace(p, "Parameters"))
} }
if acceptTParams && p.tok == token.LBRACK { lparen := p.expect(token.LPAREN)
opening := p.pos var list []*ast.Field
p.next()
// [T any](params) syntax
list := p.parseParameterList(nil, nil, token.RBRACK)
rbrack := p.expect(token.RBRACK)
tparams = &ast.FieldList{Opening: opening, List: list, Closing: rbrack}
// Type parameter lists must not be empty.
if tparams.NumFields() == 0 {
p.error(tparams.Closing, "empty type parameter list")
tparams = nil // avoid follow-on errors
}
}
opening := p.expect(token.LPAREN)
var fields []*ast.Field
if p.tok != token.RPAREN { if p.tok != token.RPAREN {
fields = p.parseParameterList(nil, nil, token.RPAREN) list = p.parseParameterList(nil, nil, token.RPAREN)
} }
rparen := p.expect(token.RPAREN) rparen := p.expect(token.RPAREN)
params = &ast.FieldList{Opening: opening, List: fields, Closing: rparen}
return return &ast.FieldList{Opening: lparen, List: list, Closing: rparen}
} }
func (p *parser) parseResult() *ast.FieldList { func (p *parser) parseResult() *ast.FieldList {
@ -1080,8 +1083,7 @@ func (p *parser) parseResult() *ast.FieldList {
} }
if p.tok == token.LPAREN { if p.tok == token.LPAREN {
_, results := p.parseParameters(false) return p.parseParameters()
return results
} }
typ := p.tryIdentOrType() typ := p.tryIdentOrType()
@ -1100,10 +1102,14 @@ func (p *parser) parseFuncType() *ast.FuncType {
} }
pos := p.expect(token.FUNC) pos := p.expect(token.FUNC)
tparams, params := p.parseParameters(true) // accept type parameters for more tolerant parsing but complain
if tparams != nil { if p.tok == token.LBRACK {
p.error(tparams.Pos(), "function type must have no type parameters") tparams := p.parseTypeParameters()
if tparams != nil {
p.error(tparams.Opening, "function type must have no type parameters")
}
} }
params := p.parseParameters()
results := p.parseResult() results := p.parseResult()
return &ast.FuncType{Func: pos, Params: params, Results: results} return &ast.FuncType{Func: pos, Params: params, Results: results}
@ -1137,7 +1143,7 @@ func (p *parser) parseMethodSpec() *ast.Field {
p.error(lbrack, "interface method must have no type parameters") p.error(lbrack, "interface method must have no type parameters")
// TODO(rfindley) refactor to share code with parseFuncType. // TODO(rfindley) refactor to share code with parseFuncType.
_, params := p.parseParameters(false) params := p.parseParameters()
results := p.parseResult() results := p.parseResult()
idents = []*ast.Ident{ident} idents = []*ast.Ident{ident}
typ = &ast.FuncType{ typ = &ast.FuncType{
@ -1167,7 +1173,7 @@ func (p *parser) parseMethodSpec() *ast.Field {
case p.tok == token.LPAREN: case p.tok == token.LPAREN:
// ordinary method // ordinary method
// TODO(rfindley) refactor to share code with parseFuncType. // TODO(rfindley) refactor to share code with parseFuncType.
_, params := p.parseParameters(false) params := p.parseParameters()
results := p.parseResult() results := p.parseResult()
idents = []*ast.Ident{ident} idents = []*ast.Ident{ident}
typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results} typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results}
@ -2575,8 +2581,6 @@ func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *
list := p.parseParameterList(name0, typ0, token.RBRACK) list := p.parseParameterList(name0, typ0, token.RBRACK)
closePos := p.expect(token.RBRACK) closePos := p.expect(token.RBRACK)
spec.TypeParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos} spec.TypeParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos}
// Let the type checker decide whether to accept type parameters on aliases:
// see go.dev/issue/46477.
if p.tok == token.ASSIGN { if p.tok == token.ASSIGN {
// type alias // type alias
spec.Assign = p.pos spec.Assign = p.pos
@ -2771,18 +2775,22 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
var recv *ast.FieldList var recv *ast.FieldList
if p.tok == token.LPAREN { if p.tok == token.LPAREN {
_, recv = p.parseParameters(false) recv = p.parseParameters()
} }
ident := p.parseIdent() ident := p.parseIdent()
tparams, params := p.parseParameters(true) var tparams *ast.FieldList
if recv != nil && tparams != nil { if p.tok == token.LBRACK {
// Method declarations do not have type parameters. We parse them for a tparams = p.parseTypeParameters()
// better error message and improved error recovery. if recv != nil && tparams != nil {
p.error(tparams.Opening, "method must have no type parameters") // Method declarations do not have type parameters. We parse them for a
tparams = nil // better error message and improved error recovery.
p.error(tparams.Opening, "method must have no type parameters")
tparams = nil
}
} }
params := p.parseParameters()
results := p.parseResult() results := p.parseResult()
var body *ast.BlockStmt var body *ast.BlockStmt