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 <gri@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Bypass: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
// d.Name "[" pname ...
// d.Name "[" pname ptype ...
// d.Name "[" pname ptype "," ...
- d.TParamList = p.paramList(pname, ptype, _Rbrack, true) // ptype may be nil
+ d.TParamList = p.paramList(pname, ptype, _Rbrack, true, false) // ptype may be nil
d.Alias = p.gotAssign()
d.Type = p.typeOrNil()
} else {
var context string
if p.got(_Lparen) {
context = "method"
- rcvr := p.paramList(nil, nil, _Rparen, false)
+ rcvr := p.paramList(nil, nil, _Rparen, false, false)
switch len(rcvr) {
case 0:
p.error("method has no receiver")
p.syntaxError("empty type parameter list")
p.next()
} else {
- tparamList = p.paramList(nil, nil, _Rbrack, true)
+ tparamList = p.paramList(nil, nil, _Rbrack, true, false)
}
}
p.want(_Lparen)
- typ.ParamList = p.paramList(nil, nil, _Rparen, false)
+ typ.ParamList = p.paramList(nil, nil, _Rparen, false, true)
typ.ResultList = p.funcResult()
return tparamList, typ
}
if p.got(_Lparen) {
- return p.paramList(nil, nil, _Rparen, false)
+ return p.paramList(nil, nil, _Rparen, false, false)
}
pos := p.pos()
// A type argument list looks like a parameter list with only
// types. Parse a parameter list and decide afterwards.
- list := p.paramList(nil, nil, _Rbrack, false)
+ list := p.paramList(nil, nil, _Rbrack, false, false)
if len(list) == 0 {
// The type parameter list is not [] but we got nothing
// due to other errors (reported by paramList). Treat
p.next()
t.Elem = p.typeOrNil()
if t.Elem == nil {
- t.Elem = p.badExpr()
+ f.Type = p.badExpr()
p.syntaxError("... is missing type")
+ } else {
+ f.Type = t
}
- f.Type = t
return f
}
// If name != nil, it is the first name after "(" or "[".
// If typ != nil, name must be != nil, and (name, typ) is the first field in the list.
// In the result list, either all fields have a name, or no field has a name.
-func (p *parser) paramList(name *Name, typ Expr, close token, requireNames bool) (list []*Field) {
+func (p *parser) paramList(name *Name, typ Expr, close token, requireNames, dddok bool) (list []*Field) {
if trace {
defer p.trace("paramList")()
}
}
}
+ // check use of ...
+ first := true // only report first occurrence
+ for i, f := range list {
+ if t, _ := f.Type.(*DotsType); t != nil && (!dddok || i+1 < len(list)) {
+ if first {
+ first = false
+ if dddok {
+ p.errorAt(t.pos, "can only use ... with final parameter")
+ } else {
+ p.errorAt(t.pos, "invalid use of ...")
+ }
+ }
+ // use T instead of invalid ...T
+ f.Type = t.Elem
+ }
+ }
+
return
}
check.ident(x, e, nil, false)
case *syntax.DotsType:
- // dots are handled explicitly where they are legal
- // (array composite literals and parameter lists)
- check.error(e, BadDotDotDotSyntax, "invalid use of '...'")
+ // dots are handled explicitly where they are valid
+ check.error(e, InvalidSyntaxTree, "invalid use of ...")
goto Error
case *syntax.BasicLit:
if variadicOk && i == len(list)-1 {
variadic = true
} else {
- check.softErrorf(t, MisplacedDotDotDot, "can only use ... with final parameter in list")
+ check.error(t, InvalidSyntaxTree, "invalid use of ...")
// ignore ... and continue
}
}
return typ
case *syntax.DotsType:
- // dots are handled explicitly where they are legal
- // (array composite literals and parameter lists)
- check.error(e, InvalidDotDotDot, "invalid use of '...'")
- check.use(e.Elem)
+ // dots are handled explicitly where they are valid
+ check.error(e, InvalidSyntaxTree, "invalid use of ...")
case *syntax.StructType:
typ := new(Struct)
return
}
-func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing token.Token) (params []*ast.Field) {
+func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing token.Token, dddok bool) (params []*ast.Field) {
if p.trace {
defer un(trace(p, "ParameterList"))
}
}
}
+ // check use of ...
+ first := true // only report first occurrence
+ for i, _ := range list {
+ f := &list[i]
+ if t, _ := f.typ.(*ast.Ellipsis); t != nil && (!dddok || i+1 < len(list)) {
+ if first {
+ first = false
+ if dddok {
+ p.error(t.Ellipsis, "can only use ... with final parameter")
+ } else {
+ p.error(t.Ellipsis, "invalid use of ...")
+ }
+ }
+ // use T instead of invalid ...T
+ // TODO(gri) would like to use `f.typ = t.Elt` but that causes problems
+ // with the resolver in cases of reuse of the same identifier
+ f.typ = &ast.BadExpr{From: t.Pos(), To: t.End()}
+ }
+ }
+
// Convert list to []*ast.Field.
// If list contains types only, each type gets its own ast.Field.
if named == 0 {
lbrack := p.expect(token.LBRACK)
var list []*ast.Field
if p.tok != token.RBRACK {
- list = p.parseParameterList(nil, nil, token.RBRACK)
+ list = p.parseParameterList(nil, nil, token.RBRACK, false)
}
rbrack := p.expect(token.RBRACK)
return &ast.FieldList{Opening: lbrack, List: list, Closing: rbrack}
}
-func (p *parser) parseParameters() *ast.FieldList {
+func (p *parser) parseParameters(result bool) *ast.FieldList {
if p.trace {
defer un(trace(p, "Parameters"))
}
- lparen := p.expect(token.LPAREN)
- var list []*ast.Field
- if p.tok != token.RPAREN {
- list = p.parseParameterList(nil, nil, token.RPAREN)
- }
- rparen := p.expect(token.RPAREN)
-
- return &ast.FieldList{Opening: lparen, List: list, Closing: rparen}
-}
-
-func (p *parser) parseResult() *ast.FieldList {
- if p.trace {
- defer un(trace(p, "Result"))
- }
-
- if p.tok == token.LPAREN {
- return p.parseParameters()
+ if !result || p.tok == token.LPAREN {
+ lparen := p.expect(token.LPAREN)
+ var list []*ast.Field
+ if p.tok != token.RPAREN {
+ list = p.parseParameterList(nil, nil, token.RPAREN, !result)
+ }
+ rparen := p.expect(token.RPAREN)
+ return &ast.FieldList{Opening: lparen, List: list, Closing: rparen}
}
- typ := p.tryIdentOrType()
- if typ != nil {
+ if typ := p.tryIdentOrType(); typ != nil {
list := make([]*ast.Field, 1)
list[0] = &ast.Field{Type: typ}
return &ast.FieldList{List: list}
p.error(tparams.Opening, "function type must have no type parameters")
}
}
- params := p.parseParameters()
- results := p.parseResult()
+ params := p.parseParameters(false)
+ results := p.parseParameters(true)
return &ast.FuncType{Func: pos, Params: params, Results: results}
}
//
// Interface methods do not have type parameters. We parse them for a
// better error message and improved error recovery.
- _ = p.parseParameterList(name0, nil, token.RBRACK)
+ _ = p.parseParameterList(name0, nil, token.RBRACK, false)
_ = p.expect(token.RBRACK)
p.error(lbrack, "interface method must have no type parameters")
// TODO(rfindley) refactor to share code with parseFuncType.
- params := p.parseParameters()
- results := p.parseResult()
+ params := p.parseParameters(false)
+ results := p.parseParameters(true)
idents = []*ast.Ident{ident}
typ = &ast.FuncType{
Func: token.NoPos,
case p.tok == token.LPAREN:
// ordinary method
// TODO(rfindley) refactor to share code with parseFuncType.
- params := p.parseParameters()
- results := p.parseResult()
+ params := p.parseParameters(false)
+ results := p.parseParameters(true)
idents = []*ast.Ident{ident}
typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results}
default:
defer un(trace(p, "parseGenericType"))
}
- list := p.parseParameterList(name0, typ0, token.RBRACK)
+ list := p.parseParameterList(name0, typ0, token.RBRACK, false)
closePos := p.expect(token.RBRACK)
spec.TypeParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos}
if p.tok == token.ASSIGN {
var recv *ast.FieldList
if p.tok == token.LPAREN {
- recv = p.parseParameters()
+ recv = p.parseParameters(false)
}
ident := p.parseIdent()
tparams = nil
}
}
- params := p.parseParameters()
- results := p.parseResult()
+ params := p.parseParameters(false)
+ results := p.parseParameters(true)
var body *ast.BlockStmt
switch p.tok {
`package p; func f() { if true {} else ; /* ERROR "expected if statement or block" */ }`,
`package p; func f() { if true {} else defer /* ERROR "expected if statement or block" */ f() }`,
+ // variadic parameter lists
+ `package p; func f(a, b ... /* ERROR "can only use ... with final parameter" */ int)`,
+ `package p; func f(a ... /* ERROR "can only use ... with final parameter" */ int, b int)`,
+ `package p; func f(... /* ERROR "can only use ... with final parameter" */ int, int)`,
+ `package p; func f() (... /* ERROR "invalid use of ..." */ int)`,
+ `package p; func f() (a, b ... /* ERROR "invalid use of ..." */ int)`,
+ `package p; func f[T ... /* ERROR "invalid use of ..." */ C]()() {}`,
+
// generic code
`package p; type _[_ any] int; var _ = T[] /* ERROR "expected operand" */ {}`,
`package p; var _ func[ /* ERROR "must have no type parameters" */ T any](T)`,
check.ident(x, e, nil, false)
case *ast.Ellipsis:
- // ellipses are handled explicitly where they are legal
- // (array composite literals and parameter lists)
- check.error(e, BadDotDotDotSyntax, "invalid use of '...'")
+ // ellipses are handled explicitly where they are valid
+ check.error(e, InvalidSyntaxTree, "invalid use of ...")
goto Error
case *ast.BasicLit:
if variadicOk && i == len(list.List)-1 && len(field.Names) <= 1 {
variadic = true
} else {
- check.softErrorf(t, MisplacedDotDotDot, "can only use ... with final parameter in list")
+ check.softErrorf(t, InvalidSyntaxTree, "invalid use of ...")
// ignore ... and continue
}
}
// report error if we encountered [...]
case *ast.Ellipsis:
- // dots are handled explicitly where they are legal
- // (array composite literals and parameter lists)
- check.error(e, InvalidDotDotDot, "invalid use of '...'")
- check.use(e.Elt)
+ // dots are handled explicitly where they are valid
+ check.error(e, InvalidSyntaxTree, "invalid use of ...")
case *ast.StructType:
typ := new(Struct)
_ = x[MissingFieldOrMethod-76]
_ = x[BadDotDotDotSyntax-77]
_ = x[NonVariadicDotDotDot-78]
- _ = x[MisplacedDotDotDot-79]
_ = x[InvalidDotDotDot-81]
_ = x[UncalledBuiltin-82]
_ = x[InvalidAppend-83]
const (
_Code_name_0 = "InvalidSyntaxTree"
_Code_name_1 = "TestBlankPkgNameMismatchedPkgNameInvalidPkgUseBadImportPathBrokenImportImportCRenamedUnusedImportInvalidInitCycleDuplicateDeclInvalidDeclCycleInvalidTypeCycleInvalidConstInitInvalidConstValInvalidConstTypeUntypedNilUseWrongAssignCountUnassignableOperandNoNewVarMultiValAssignOpInvalidIfaceAssignInvalidChanAssignIncompatibleAssignUnaddressableFieldAssignNotATypeInvalidArrayLenBlankIfaceMethodIncomparableMapKey"
- _Code_name_2 = "InvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDotMisplacedDotDotDot"
+ _Code_name_2 = "InvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDot"
_Code_name_3 = "InvalidDotDotDotUncalledBuiltinInvalidAppendInvalidCapInvalidCloseInvalidCopyInvalidComplexInvalidDeleteInvalidImagInvalidLenSwappedMakeArgsInvalidMakeInvalidRealInvalidAssertImpossibleAssertInvalidConversionInvalidUntypedConversionBadOffsetofSyntaxInvalidOffsetofUnusedExprUnusedVarMissingReturnWrongResultCountOutOfScopeResultInvalidCondInvalidPostDecl"
_Code_name_4 = "InvalidIterVarInvalidRangeExprMisplacedBreakMisplacedContinueMisplacedFallthroughDuplicateCaseDuplicateDefaultBadTypeKeywordInvalidTypeSwitchInvalidExprSwitchInvalidSelectCaseUndeclaredLabelDuplicateLabelMisplacedLabelUnusedLabelJumpOverDeclJumpIntoBlockInvalidMethodExprWrongArgCountInvalidCallUnusedResultsInvalidDeferInvalidGoBadDeclRepeatedDeclInvalidUnsafeAddInvalidUnsafeSliceUnsupportedFeatureNotAGenericTypeWrongTypeArgCountCannotInferTypeArgsInvalidTypeArgInvalidInstanceCycleInvalidUnionMisplacedConstraintIfaceInvalidMethodTypeParamsMisplacedTypeParamInvalidUnsafeSliceDataInvalidUnsafeString"
_Code_name_5 = "InvalidClearTypeTooLargeInvalidMinMaxOperandTooNew"
var (
_Code_index_1 = [...]uint16{0, 4, 16, 33, 46, 59, 71, 85, 97, 113, 126, 142, 158, 174, 189, 205, 218, 234, 253, 261, 277, 295, 312, 330, 354, 362, 377, 393, 411}
- _Code_index_2 = [...]uint16{0, 15, 22, 33, 56, 71, 83, 94, 109, 123, 138, 153, 166, 175, 189, 204, 215, 230, 239, 255, 275, 293, 312, 324, 343, 362, 378, 395, 414, 428, 439, 454, 467, 482, 498, 512, 528, 543, 560, 578, 593, 603, 613, 630, 652, 666, 680, 700, 718, 738, 756}
+ _Code_index_2 = [...]uint16{0, 15, 22, 33, 56, 71, 83, 94, 109, 123, 138, 153, 166, 175, 189, 204, 215, 230, 239, 255, 275, 293, 312, 324, 343, 362, 378, 395, 414, 428, 439, 454, 467, 482, 498, 512, 528, 543, 560, 578, 593, 603, 613, 630, 652, 666, 680, 700, 718, 738}
_Code_index_3 = [...]uint16{0, 16, 31, 44, 54, 66, 77, 91, 104, 115, 125, 140, 151, 162, 175, 191, 208, 232, 249, 264, 274, 283, 296, 312, 328, 339, 354}
_Code_index_4 = [...]uint16{0, 14, 30, 44, 61, 81, 94, 110, 124, 141, 158, 175, 190, 204, 218, 229, 241, 254, 271, 284, 295, 308, 320, 329, 336, 348, 364, 382, 400, 415, 432, 451, 465, 485, 497, 521, 544, 562, 584, 603}
_Code_index_5 = [...]uint8{0, 12, 24, 44, 50}
case 1 <= i && i <= 28:
i -= 1
return _Code_name_1[_Code_index_1[i]:_Code_index_1[i+1]]
- case 30 <= i && i <= 79:
+ case 30 <= i && i <= 78:
i -= 30
return _Code_name_2[_Code_index_2[i]:_Code_index_2[i+1]]
case 81 <= i && i <= 106:
// MisplacedDotDotDot occurs when a "..." is used somewhere other than the
// final argument in a function declaration.
- //
- // Example:
- // func f(...int, int)
- MisplacedDotDotDot
+ _ // not used anymore (error reported by parser)
_ // InvalidDotDotDotOperand was removed.
func issue28281c(a, b, c ... /* ERROR "can only use ... with final parameter" */ int)
func issue28281d(... /* ERROR "can only use ... with final parameter" */ int, int)
func issue28281e(a, b, c ... /* ERROR "can only use ... with final parameter" */ int, d int)
-func issue28281f(... /* ERROR "can only use ... with final parameter" */ int, ... /* ERROR "can only use ... with final parameter" */ int, int)
-func (... /* ERROR "invalid use of '...'" */ TT) f()
-func issue28281g() (... /* ERROR "can only use ... with final parameter" */ TT)
+func issue28281f(... /* ERROR "can only use ... with final parameter" */ int, ... int, int)
+func (... /* ERROR "invalid use of ..." */ TT) f()
+func issue28281g() (... /* ERROR "invalid use of ..." */ TT)
// Issue #26234: Make various field/method lookup errors easier to read by matching cmd/compile's output
func issue26234a(f *syn.Prog) {
}
// There is no such thing as a variadic generic type.
-type _[T ... /* ERROR "invalid use of '...'" */ any] struct{}
+type _[T ... /* ERROR "invalid use of ..." */ any] struct{}
// Generic interfaces may be embedded as one would expect.
type I2 interface {