Skip to content

Commit ad7b46e

Browse files
griesemergopherbot
authored andcommitted
go/parser, go/types, syntax, types2: report invalid uses of ... by parsers
Check correct use of ...'s in parameter lists in parsers. This allows the type checkers to assume correct ASTs with respect to ... use. Adjust some error messages: if a ... is used in a result parameter list, the error is now more accurate. Eliminate a now unused error code. Change-Id: I66058e114e84805e24c59e570604b607ef5ff1fe Reviewed-on: https://go-review.googlesource.com/c/go/+/631135 Reviewed-by: Robert Griesemer <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> TryBot-Bypass: Robert Griesemer <[email protected]> Auto-Submit: Robert Griesemer <[email protected]>
1 parent d96fd2e commit ad7b46e

File tree

13 files changed

+96
-70
lines changed

13 files changed

+96
-70
lines changed

src/cmd/compile/internal/syntax/parser.go

+27-9
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ func (p *parser) typeDecl(group *Group) Decl {
650650
// d.Name "[" pname ...
651651
// d.Name "[" pname ptype ...
652652
// d.Name "[" pname ptype "," ...
653-
d.TParamList = p.paramList(pname, ptype, _Rbrack, true) // ptype may be nil
653+
d.TParamList = p.paramList(pname, ptype, _Rbrack, true, false) // ptype may be nil
654654
d.Alias = p.gotAssign()
655655
d.Type = p.typeOrNil()
656656
} else {
@@ -800,7 +800,7 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
800800
var context string
801801
if p.got(_Lparen) {
802802
context = "method"
803-
rcvr := p.paramList(nil, nil, _Rparen, false)
803+
rcvr := p.paramList(nil, nil, _Rparen, false, false)
804804
switch len(rcvr) {
805805
case 0:
806806
p.error("method has no receiver")
@@ -1469,12 +1469,12 @@ func (p *parser) funcType(context string) ([]*Field, *FuncType) {
14691469
p.syntaxError("empty type parameter list")
14701470
p.next()
14711471
} else {
1472-
tparamList = p.paramList(nil, nil, _Rbrack, true)
1472+
tparamList = p.paramList(nil, nil, _Rbrack, true, false)
14731473
}
14741474
}
14751475

14761476
p.want(_Lparen)
1477-
typ.ParamList = p.paramList(nil, nil, _Rparen, false)
1477+
typ.ParamList = p.paramList(nil, nil, _Rparen, false, true)
14781478
typ.ResultList = p.funcResult()
14791479

14801480
return tparamList, typ
@@ -1582,7 +1582,7 @@ func (p *parser) funcResult() []*Field {
15821582
}
15831583

15841584
if p.got(_Lparen) {
1585-
return p.paramList(nil, nil, _Rparen, false)
1585+
return p.paramList(nil, nil, _Rparen, false, false)
15861586
}
15871587

15881588
pos := p.pos()
@@ -1793,7 +1793,7 @@ func (p *parser) methodDecl() *Field {
17931793

17941794
// A type argument list looks like a parameter list with only
17951795
// types. Parse a parameter list and decide afterwards.
1796-
list := p.paramList(nil, nil, _Rbrack, false)
1796+
list := p.paramList(nil, nil, _Rbrack, false, false)
17971797
if len(list) == 0 {
17981798
// The type parameter list is not [] but we got nothing
17991799
// due to other errors (reported by paramList). Treat
@@ -1962,10 +1962,11 @@ func (p *parser) paramDeclOrNil(name *Name, follow token) *Field {
19621962
p.next()
19631963
t.Elem = p.typeOrNil()
19641964
if t.Elem == nil {
1965-
t.Elem = p.badExpr()
1965+
f.Type = p.badExpr()
19661966
p.syntaxError("... is missing type")
1967+
} else {
1968+
f.Type = t
19671969
}
1968-
f.Type = t
19691970
return f
19701971
}
19711972

@@ -1995,7 +1996,7 @@ func (p *parser) paramDeclOrNil(name *Name, follow token) *Field {
19951996
// If name != nil, it is the first name after "(" or "[".
19961997
// If typ != nil, name must be != nil, and (name, typ) is the first field in the list.
19971998
// In the result list, either all fields have a name, or no field has a name.
1998-
func (p *parser) paramList(name *Name, typ Expr, close token, requireNames bool) (list []*Field) {
1999+
func (p *parser) paramList(name *Name, typ Expr, close token, requireNames, dddok bool) (list []*Field) {
19992000
if trace {
20002001
defer p.trace("paramList")()
20012002
}
@@ -2109,6 +2110,23 @@ func (p *parser) paramList(name *Name, typ Expr, close token, requireNames bool)
21092110
}
21102111
}
21112112

2113+
// check use of ...
2114+
first := true // only report first occurrence
2115+
for i, f := range list {
2116+
if t, _ := f.Type.(*DotsType); t != nil && (!dddok || i+1 < len(list)) {
2117+
if first {
2118+
first = false
2119+
if dddok {
2120+
p.errorAt(t.pos, "can only use ... with final parameter")
2121+
} else {
2122+
p.errorAt(t.pos, "invalid use of ...")
2123+
}
2124+
}
2125+
// use T instead of invalid ...T
2126+
f.Type = t.Elem
2127+
}
2128+
}
2129+
21122130
return
21132131
}
21142132

src/cmd/compile/internal/types2/expr.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -1016,9 +1016,8 @@ func (check *Checker) exprInternal(T *target, x *operand, e syntax.Expr, hint Ty
10161016
check.ident(x, e, nil, false)
10171017

10181018
case *syntax.DotsType:
1019-
// dots are handled explicitly where they are legal
1020-
// (array composite literals and parameter lists)
1021-
check.error(e, BadDotDotDotSyntax, "invalid use of '...'")
1019+
// dots are handled explicitly where they are valid
1020+
check.error(e, InvalidSyntaxTree, "invalid use of ...")
10221021
goto Error
10231022

10241023
case *syntax.BasicLit:

src/cmd/compile/internal/types2/signature.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ func (check *Checker) collectParams(list []*syntax.Field, variadicOk bool) (name
344344
if variadicOk && i == len(list)-1 {
345345
variadic = true
346346
} else {
347-
check.softErrorf(t, MisplacedDotDotDot, "can only use ... with final parameter in list")
347+
check.error(t, InvalidSyntaxTree, "invalid use of ...")
348348
// ignore ... and continue
349349
}
350350
}

src/cmd/compile/internal/types2/typexpr.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -321,10 +321,8 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *TypeName) (T Type) {
321321
return typ
322322

323323
case *syntax.DotsType:
324-
// dots are handled explicitly where they are legal
325-
// (array composite literals and parameter lists)
326-
check.error(e, InvalidDotDotDot, "invalid use of '...'")
327-
check.use(e.Elem)
324+
// dots are handled explicitly where they are valid
325+
check.error(e, InvalidSyntaxTree, "invalid use of ...")
328326

329327
case *syntax.StructType:
330328
typ := new(Struct)

src/go/parser/parser.go

+43-33
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,7 @@ func (p *parser) parseParamDecl(name *ast.Ident, typeSetsOK bool) (f field) {
872872
return
873873
}
874874

875-
func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing token.Token) (params []*ast.Field) {
875+
func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing token.Token, dddok bool) (params []*ast.Field) {
876876
if p.trace {
877877
defer un(trace(p, "ParameterList"))
878878
}
@@ -1006,6 +1006,26 @@ func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing tok
10061006
}
10071007
}
10081008

1009+
// check use of ...
1010+
first := true // only report first occurrence
1011+
for i, _ := range list {
1012+
f := &list[i]
1013+
if t, _ := f.typ.(*ast.Ellipsis); t != nil && (!dddok || i+1 < len(list)) {
1014+
if first {
1015+
first = false
1016+
if dddok {
1017+
p.error(t.Ellipsis, "can only use ... with final parameter")
1018+
} else {
1019+
p.error(t.Ellipsis, "invalid use of ...")
1020+
}
1021+
}
1022+
// use T instead of invalid ...T
1023+
// TODO(gri) would like to use `f.typ = t.Elt` but that causes problems
1024+
// with the resolver in cases of reuse of the same identifier
1025+
f.typ = &ast.BadExpr{From: t.Pos(), To: t.End()}
1026+
}
1027+
}
1028+
10091029
// Convert list to []*ast.Field.
10101030
// If list contains types only, each type gets its own ast.Field.
10111031
if named == 0 {
@@ -1050,7 +1070,7 @@ func (p *parser) parseTypeParameters() *ast.FieldList {
10501070
lbrack := p.expect(token.LBRACK)
10511071
var list []*ast.Field
10521072
if p.tok != token.RBRACK {
1053-
list = p.parseParameterList(nil, nil, token.RBRACK)
1073+
list = p.parseParameterList(nil, nil, token.RBRACK, false)
10541074
}
10551075
rbrack := p.expect(token.RBRACK)
10561076

@@ -1062,32 +1082,22 @@ func (p *parser) parseTypeParameters() *ast.FieldList {
10621082
return &ast.FieldList{Opening: lbrack, List: list, Closing: rbrack}
10631083
}
10641084

1065-
func (p *parser) parseParameters() *ast.FieldList {
1085+
func (p *parser) parseParameters(result bool) *ast.FieldList {
10661086
if p.trace {
10671087
defer un(trace(p, "Parameters"))
10681088
}
10691089

1070-
lparen := p.expect(token.LPAREN)
1071-
var list []*ast.Field
1072-
if p.tok != token.RPAREN {
1073-
list = p.parseParameterList(nil, nil, token.RPAREN)
1074-
}
1075-
rparen := p.expect(token.RPAREN)
1076-
1077-
return &ast.FieldList{Opening: lparen, List: list, Closing: rparen}
1078-
}
1079-
1080-
func (p *parser) parseResult() *ast.FieldList {
1081-
if p.trace {
1082-
defer un(trace(p, "Result"))
1083-
}
1084-
1085-
if p.tok == token.LPAREN {
1086-
return p.parseParameters()
1090+
if !result || p.tok == token.LPAREN {
1091+
lparen := p.expect(token.LPAREN)
1092+
var list []*ast.Field
1093+
if p.tok != token.RPAREN {
1094+
list = p.parseParameterList(nil, nil, token.RPAREN, !result)
1095+
}
1096+
rparen := p.expect(token.RPAREN)
1097+
return &ast.FieldList{Opening: lparen, List: list, Closing: rparen}
10871098
}
10881099

1089-
typ := p.tryIdentOrType()
1090-
if typ != nil {
1100+
if typ := p.tryIdentOrType(); typ != nil {
10911101
list := make([]*ast.Field, 1)
10921102
list[0] = &ast.Field{Type: typ}
10931103
return &ast.FieldList{List: list}
@@ -1109,8 +1119,8 @@ func (p *parser) parseFuncType() *ast.FuncType {
11091119
p.error(tparams.Opening, "function type must have no type parameters")
11101120
}
11111121
}
1112-
params := p.parseParameters()
1113-
results := p.parseResult()
1122+
params := p.parseParameters(false)
1123+
results := p.parseParameters(true)
11141124

11151125
return &ast.FuncType{Func: pos, Params: params, Results: results}
11161126
}
@@ -1138,13 +1148,13 @@ func (p *parser) parseMethodSpec() *ast.Field {
11381148
//
11391149
// Interface methods do not have type parameters. We parse them for a
11401150
// better error message and improved error recovery.
1141-
_ = p.parseParameterList(name0, nil, token.RBRACK)
1151+
_ = p.parseParameterList(name0, nil, token.RBRACK, false)
11421152
_ = p.expect(token.RBRACK)
11431153
p.error(lbrack, "interface method must have no type parameters")
11441154

11451155
// TODO(rfindley) refactor to share code with parseFuncType.
1146-
params := p.parseParameters()
1147-
results := p.parseResult()
1156+
params := p.parseParameters(false)
1157+
results := p.parseParameters(true)
11481158
idents = []*ast.Ident{ident}
11491159
typ = &ast.FuncType{
11501160
Func: token.NoPos,
@@ -1173,8 +1183,8 @@ func (p *parser) parseMethodSpec() *ast.Field {
11731183
case p.tok == token.LPAREN:
11741184
// ordinary method
11751185
// TODO(rfindley) refactor to share code with parseFuncType.
1176-
params := p.parseParameters()
1177-
results := p.parseResult()
1186+
params := p.parseParameters(false)
1187+
results := p.parseParameters(true)
11781188
idents = []*ast.Ident{ident}
11791189
typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results}
11801190
default:
@@ -2578,7 +2588,7 @@ func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *
25782588
defer un(trace(p, "parseGenericType"))
25792589
}
25802590

2581-
list := p.parseParameterList(name0, typ0, token.RBRACK)
2591+
list := p.parseParameterList(name0, typ0, token.RBRACK, false)
25822592
closePos := p.expect(token.RBRACK)
25832593
spec.TypeParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos}
25842594
if p.tok == token.ASSIGN {
@@ -2775,7 +2785,7 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
27752785

27762786
var recv *ast.FieldList
27772787
if p.tok == token.LPAREN {
2778-
recv = p.parseParameters()
2788+
recv = p.parseParameters(false)
27792789
}
27802790

27812791
ident := p.parseIdent()
@@ -2790,8 +2800,8 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
27902800
tparams = nil
27912801
}
27922802
}
2793-
params := p.parseParameters()
2794-
results := p.parseResult()
2803+
params := p.parseParameters(false)
2804+
results := p.parseParameters(true)
27952805

27962806
var body *ast.BlockStmt
27972807
switch p.tok {

src/go/parser/short_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,14 @@ var invalids = []string{
190190
`package p; func f() { if true {} else ; /* ERROR "expected if statement or block" */ }`,
191191
`package p; func f() { if true {} else defer /* ERROR "expected if statement or block" */ f() }`,
192192

193+
// variadic parameter lists
194+
`package p; func f(a, b ... /* ERROR "can only use ... with final parameter" */ int)`,
195+
`package p; func f(a ... /* ERROR "can only use ... with final parameter" */ int, b int)`,
196+
`package p; func f(... /* ERROR "can only use ... with final parameter" */ int, int)`,
197+
`package p; func f() (... /* ERROR "invalid use of ..." */ int)`,
198+
`package p; func f() (a, b ... /* ERROR "invalid use of ..." */ int)`,
199+
`package p; func f[T ... /* ERROR "invalid use of ..." */ C]()() {}`,
200+
193201
// generic code
194202
`package p; type _[_ any] int; var _ = T[] /* ERROR "expected operand" */ {}`,
195203
`package p; var _ func[ /* ERROR "must have no type parameters" */ T any](T)`,

src/go/types/expr.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -1006,9 +1006,8 @@ func (check *Checker) exprInternal(T *target, x *operand, e ast.Expr, hint Type)
10061006
check.ident(x, e, nil, false)
10071007

10081008
case *ast.Ellipsis:
1009-
// ellipses are handled explicitly where they are legal
1010-
// (array composite literals and parameter lists)
1011-
check.error(e, BadDotDotDotSyntax, "invalid use of '...'")
1009+
// ellipses are handled explicitly where they are valid
1010+
check.error(e, InvalidSyntaxTree, "invalid use of ...")
10121011
goto Error
10131012

10141013
case *ast.BasicLit:

src/go/types/signature.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ func (check *Checker) collectParams(list *ast.FieldList, variadicOk bool) (names
364364
if variadicOk && i == len(list.List)-1 && len(field.Names) <= 1 {
365365
variadic = true
366366
} else {
367-
check.softErrorf(t, MisplacedDotDotDot, "can only use ... with final parameter in list")
367+
check.softErrorf(t, InvalidSyntaxTree, "invalid use of ...")
368368
// ignore ... and continue
369369
}
370370
}

src/go/types/typexpr.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,8 @@ func (check *Checker) typInternal(e0 ast.Expr, def *TypeName) (T Type) {
322322
// report error if we encountered [...]
323323

324324
case *ast.Ellipsis:
325-
// dots are handled explicitly where they are legal
326-
// (array composite literals and parameter lists)
327-
check.error(e, InvalidDotDotDot, "invalid use of '...'")
328-
check.use(e.Elt)
325+
// dots are handled explicitly where they are valid
326+
check.error(e, InvalidSyntaxTree, "invalid use of ...")
329327

330328
case *ast.StructType:
331329
typ := new(Struct)

src/internal/types/errors/code_string.go

+3-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/internal/types/errors/codes.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -719,10 +719,7 @@ const (
719719

720720
// MisplacedDotDotDot occurs when a "..." is used somewhere other than the
721721
// final argument in a function declaration.
722-
//
723-
// Example:
724-
// func f(...int, int)
725-
MisplacedDotDotDot
722+
_ // not used anymore (error reported by parser)
726723

727724
_ // InvalidDotDotDotOperand was removed.
728725

src/internal/types/testdata/check/issues0.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,9 @@ func issue28281b(a, b int, c ...int)
326326
func issue28281c(a, b, c ... /* ERROR "can only use ... with final parameter" */ int)
327327
func issue28281d(... /* ERROR "can only use ... with final parameter" */ int, int)
328328
func issue28281e(a, b, c ... /* ERROR "can only use ... with final parameter" */ int, d int)
329-
func issue28281f(... /* ERROR "can only use ... with final parameter" */ int, ... /* ERROR "can only use ... with final parameter" */ int, int)
330-
func (... /* ERROR "invalid use of '...'" */ TT) f()
331-
func issue28281g() (... /* ERROR "can only use ... with final parameter" */ TT)
329+
func issue28281f(... /* ERROR "can only use ... with final parameter" */ int, ... int, int)
330+
func (... /* ERROR "invalid use of ..." */ TT) f()
331+
func issue28281g() (... /* ERROR "invalid use of ..." */ TT)
332332

333333
// Issue #26234: Make various field/method lookup errors easier to read by matching cmd/compile's output
334334
func issue26234a(f *syn.Prog) {

0 commit comments

Comments
 (0)