}
typ := p.parseTypeName(ident)
- if p.tok == token.LBRACK {
+ if p.tok == token.LBRACK && p.mode&ParseTypeParams != 0 {
typ = p.parseTypeInstance(typ)
}
// list such as T[P,]? (We do in parseTypeInstance).
lbrack := p.expect(token.LBRACK)
var args []ast.Expr
+ var firstComma token.Pos
+ // TODO(rfindley): consider changing parseRhsOrType so that this function variable
+ // is not needed.
+ argparser := p.parseRhsOrType
+ if p.mode&ParseTypeParams == 0 {
+ argparser = p.parseRhs
+ }
if p.tok != token.RBRACK {
p.exprLev++
- args = append(args, p.parseRhsOrType())
+ args = append(args, argparser())
for p.tok == token.COMMA {
+ if !firstComma.IsValid() {
+ firstComma = p.pos
+ }
p.next()
- args = append(args, p.parseRhsOrType())
+ args = append(args, argparser())
}
p.exprLev--
}
// x [P]E
return x, &ast.ArrayType{Lbrack: lbrack, Len: args[0], Elt: elt}
}
+ if p.mode&ParseTypeParams == 0 {
+ p.error(rbrack, "missing element type in array type expression")
+ return nil, &ast.BadExpr{From: args[0].Pos(), To: args[0].End()}
+ }
+ }
+
+ if p.mode&ParseTypeParams == 0 {
+ p.error(firstComma, "expected ']', found ','")
+ return x, &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()}
}
// x[P], x[P1, P2], ...
defer un(trace(p, "Parameters"))
}
- if acceptTParams && p.tok == token.LBRACK {
+ if p.mode&ParseTypeParams != 0 && acceptTParams && p.tok == token.LBRACK {
opening := p.pos
p.next()
// [T any](params) syntax
var typ ast.Expr
x := p.parseTypeName(nil)
if ident, _ := x.(*ast.Ident); ident != nil {
- switch p.tok {
- case token.LBRACK:
+ switch {
+ case p.tok == token.LBRACK && p.mode&ParseTypeParams != 0:
// generic method or embedded instantiated type
lbrack := p.pos
p.next()
rbrack := p.expectClosing(token.RBRACK, "type argument list")
typ = &ast.CallExpr{Fun: ident, Lparen: lbrack, Args: list, Rparen: rbrack, Brackets: true}
}
- case token.LPAREN:
+ case p.tok == token.LPAREN:
// ordinary method
// TODO(rfindley) refactor to share code with parseFuncType.
scope := ast.NewScope(nil) // method scope
} else {
// embedded, possibly instantiated type
typ = x
- if p.tok == token.LBRACK {
+ if p.tok == token.LBRACK && p.mode&ParseTypeParams != 0 {
// embedded instantiated interface
typ = p.parseTypeInstance(typ)
}
lbrace := p.expect(token.LBRACE)
scope := ast.NewScope(nil) // interface scope
var list []*ast.Field
-L:
- for {
- switch p.tok {
- case token.IDENT, token.LPAREN:
+ for p.tok == token.IDENT || p.mode&ParseTypeParams != 0 && p.tok == token.TYPE {
+ if p.tok == token.IDENT {
list = append(list, p.parseMethodSpec(scope))
- case token.TYPE:
+ } else {
// all types in a type list share the same field name "type"
// (since type is a keyword, a Go program cannot have that field name)
name := []*ast.Ident{{NamePos: p.pos, Name: "type"}}
list = append(list, &ast.Field{Names: name, Type: typ})
}
p.expectSemi()
- default:
- break L
}
}
+ // TODO(rfindley): the error produced here could be improved, since we could
+ // accept a identifier, 'type', or a '}' at this point.
rbrace := p.expect(token.RBRACE)
return &ast.InterfaceType{
switch p.tok {
case token.IDENT:
typ := p.parseTypeName(nil)
- if p.tok == token.LBRACK {
+ if p.tok == token.LBRACK && p.mode&ParseTypeParams != 0 {
typ = p.parseTypeInstance(typ)
}
return typ
p.exprLev++
x := p.parseExpr(true) // we don't know yet if we're a lhs or rhs expr
p.exprLev--
- if name0, _ := x.(*ast.Ident); name0 != nil && p.tok != token.RBRACK {
+ if name0, _ := x.(*ast.Ident); p.mode&ParseTypeParams != 0 && name0 != nil && p.tok != token.RBRACK {
// generic type [T any];
p.parseGenericType(spec, lbrack, name0, token.RBRACK)
} else {
`package p; type T = int`,
`package p; type (T = p.T; _ = struct{}; x = *T)`,
`package p; type T (*int)`,
-
- // structs with parameterized embedded fields (for symmetry with interfaces)
`package p; type _ struct{ ((int)) }`,
`package p; type _ struct{ (*(int)) }`,
`package p; type _ struct{ ([]byte) }`, // disallowed by type-checker
-
- // type parameters
- `package p; type T[P any] struct { P }`,
- `package p; type T[P comparable] struct { P }`,
- `package p; type T[P comparable[P]] struct { P }`,
- `package p; type T[P1, P2 any] struct { P1; f []P2 }`,
- `package p; type _ []T[int]`,
-
`package p; var _ = func()T(nil)`,
- `package p; func _[T any]()`,
- `package p; func _[T any]()()`,
`package p; func _(T (P))`,
`package p; func _(T []E)`,
`package p; func _(T [P]E)`,
- `package p; func _(x T[P1, P2, P3])`,
- `package p; func _(x p.T[Q])`,
- `package p; func _(p.T[Q])`,
-
- `package p; var _ T[chan int]`,
- `package p; func f[A, B any](); func _() { _ = f[int, int] }`,
-
- `package p; type _[A interface{},] struct{}`,
- `package p; type _[A interface{}] struct{}`,
- `package p; type _[A, B any,] struct{}`,
- `package p; type _[A, B any] struct{}`,
- `package p; type _[A any,] struct{}`,
- `package p; type _ [A+B]struct{}`, // this is an array!
- `package p; type _[A any]struct{}`,
- `package p; type _[A any] struct{ A }`, // this is not an array!
-
- `package p; func _[T any]()`,
- `package p; func _[T any](x T)`,
- `package p; func _[T1, T2 any](x T)`,
-
+ `package p; type _ [A+B]struct{}`,
`package p; func (R) _()`,
- `package p; func (R[P]) _[T any]()`,
- `package p; func (_ R[P]) _[T any](x T)`,
- `package p; func (_ R[P, Q]) _[T1, T2 any](x T)`,
-
- `package p; var _ = []T[int]{}`,
- `package p; var _ = [10]T[int]{}`,
- `package p; var _ = func()T[int]{}`,
- `package p; var _ = map[T[int]]T[int]{}`,
- `package p; var _ = chan T[int](x)`,
- `package p; func _(T[P])`,
- `package p; func _(T[P1, P2, P3])`,
- `package p; func _(T[P]) T[P]`,
- `package p; func _(_ T[P], T P) T[P]`,
-
- `package p; func _[A, B any](a A) B`,
- `package p; func _[A, B C](a A) B`,
- `package p; func _[A, B C[A, B]](a A) B`,
-
- // method type parameters (if methodTypeParamsOk)
- `package p; func (T) _[A, B any](a A) B`,
- `package p; func (T) _[A, B C](a A) B`,
- `package p; func (T) _[A, B C[A, B]](a A) B`,
-
- // method type parameters are not permitted in interfaces.
- `package p; type _[A, B any] interface { _(a A) B }`,
- `package p; type _[A, B C[A, B]] interface { _(a A) B }`,
-
- // type bounds
- `package p; func _[T1, T2 interface{}](x T1) T2`,
- `package p; func _[T1 interface{ m() }, T2, T3 interface{}](x T1, y T3) T2`,
-
- // struct embedding
- `package p; type _ struct{ T[P] }`,
- `package p; type _ struct{ T[struct{a, b, c int}] }`,
`package p; type _ struct{ f [n]E }`,
`package p; type _ struct{ f [a+b+c+d]E }`,
+ `package p; type I1 interface{}; type I2 interface{ I1 }`,
+}
- // interfaces with type lists
- `package p; type _ interface{type int}`,
- `package p; type _ interface{type int, float32; type bool; m(); type string;}`,
+// validWithTParamsOnly holds source code examples that are valid if
+// ParseTypeParams is set, but invalid if not. When checking with the
+// ParseTypeParams set, errors are ignored.
+var validWithTParamsOnly = []string{
+ `package p; type _ []T[ /* ERROR "expected ';', found '\['" */ int]`,
+ `package p; type T[P any /* ERROR "expected ']', found any" */ ] struct { P }`,
+ `package p; type T[P comparable /* ERROR "expected ']', found comparable" */ ] struct { P }`,
+ `package p; type T[P comparable /* ERROR "expected ']', found comparable" */ [P]] struct { P }`,
+ `package p; type T[P1, /* ERROR "expected ']', found ','" */ P2 any] struct { P1; f []P2 }`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ T any]()()`,
+ `package p; func _(T (P))`,
+ `package p; func f[ /* ERROR "expected '\(', found '\['" */ A, B any](); func _() { _ = f[int, int] }`,
+ `package p; func _(x /* ERROR "mixed named and unnamed parameters" */ T[P1, P2, P3])`,
+ `package p; func _(x /* ERROR "mixed named and unnamed parameters" */ p.T[Q])`,
+ `package p; func _(p.T[ /* ERROR "missing ',' in parameter list" */ Q])`,
+ `package p; type _[A interface /* ERROR "expected ']', found 'interface'" */ {},] struct{}`,
+ `package p; type _[A interface /* ERROR "expected ']', found 'interface'" */ {}] struct{}`,
+ `package p; type _[A, /* ERROR "expected ']', found ','" */ B any,] struct{}`,
+ `package p; type _[A, /* ERROR "expected ']', found ','" */ B any] struct{}`,
+ `package p; type _[A any /* ERROR "expected ']', found any" */,] struct{}`,
+ `package p; type _[A any /* ERROR "expected ']', found any" */ ]struct{}`,
+ `package p; type _[A any /* ERROR "expected ']', found any" */ ] struct{ A }`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ T any]()`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ T any](x T)`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ T1, T2 any](x T)`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B any](a A) B`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B C](a A) B`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B C[A, B]](a A) B`,
+ `package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B any](a A) B`,
+ `package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C](a A) B`,
+ `package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C[A, B]](a A) B`,
+ `package p; type _[A, /* ERROR "expected ']', found ','" */ B any] interface { _(a A) B }`,
+ `package p; type _[A, /* ERROR "expected ']', found ','" */ B C[A, B]] interface { _(a A) B }`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ T1, T2 interface{}](x T1) T2`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ T1 interface{ m() }, T2, T3 interface{}](x T1, y T3) T2`,
+ `package p; var _ = [ /* ERROR "expected expression" */ ]T[int]{}`,
+ `package p; var _ = [ /* ERROR "expected expression" */ 10]T[int]{}`,
+ `package p; var _ = func /* ERROR "expected expression" */ ()T[int]{}`,
+ `package p; var _ = map /* ERROR "expected expression" */ [T[int]]T[int]{}`,
+ `package p; var _ = chan /* ERROR "expected expression" */ T[int](x)`,
+ `package p; func _(_ T[ /* ERROR "missing ',' in parameter list" */ P], T P) T[P]`,
+ `package p; var _ T[ /* ERROR "expected ';', found '\['" */ chan int]`,
- // interface embedding
- `package p; type I1 interface{}; type I2 interface{ I1 }`,
- `package p; type I1[T any] interface{}; type I2 interface{ I1[int] }`,
- `package p; type I1[T any] interface{}; type I2[T any] interface{ I1[T] }`,
+ // TODO(rfindley) this error message could be improved.
+ `package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[P]) _[T any](x T)`,
+ `package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[ P, Q]) _[T1, T2 any](x T)`,
+
+ `package p; func (R[P] /* ERROR "missing element type" */ ) _[T any]()`,
+ `package p; func _(T[P] /* ERROR "missing element type" */ )`,
+ `package p; func _(T[P1, /* ERROR "expected ']', found ','" */ P2, P3 ])`,
+ `package p; func _(T[P] /* ERROR "missing element type" */ ) T[P]`,
+ `package p; type _ struct{ T[P] /* ERROR "missing element type" */ }`,
+ `package p; type _ struct{ T[struct /* ERROR "expected expression" */ {a, b, c int}] }`,
+ `package p; type _ interface{type /* ERROR "expected '}', found 'type'" */ int}`,
+ `package p; type _ interface{type /* ERROR "expected '}', found 'type'" */ int, float32; type bool; m(); type string;}`,
+ `package p; type I1[T any /* ERROR "expected ']', found any" */ ] interface{}; type I2 interface{ I1[int] }`,
+ `package p; type I1[T any /* ERROR "expected ']', found any" */ ] interface{}; type I2[T any] interface{ I1[T] }`,
+ `package p; type _ interface { f[ /* ERROR "expected ';', found '\['" */ T any]() }`,
}
func TestValid(t *testing.T) {
- for _, src := range valids {
- checkErrors(t, src, src, DeclarationErrors|AllErrors)
- }
+ t.Run("no tparams", func(t *testing.T) {
+ for _, src := range valids {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors, false)
+ }
+ })
+ t.Run("tparams", func(t *testing.T) {
+ for _, src := range valids {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, false)
+ }
+ for _, src := range validWithTParamsOnly {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, false)
+ }
+ })
}
// TestSingle is useful to track down a problem with a single short test program.
func TestSingle(t *testing.T) {
const src = `package p; var _ = T[P]{}`
- checkErrors(t, src, src, DeclarationErrors|AllErrors)
+ checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, true)
}
var invalids = []string{
`package p; func f() { go f /* ERROR HERE "function must be invoked" */ }`,
`package p; func f() { defer func() {} /* ERROR HERE "function must be invoked" */ }`,
`package p; func f() { go func() { func() { f(x func /* ERROR "missing ','" */ (){}) } } }`,
- //`package p; func f(x func(), u v func /* ERROR "missing ','" */ ()){}`,
-
- // type parameters
- `package p; var _ func[ /* ERROR "cannot have type parameters" */ T any](T)`,
`package p; func _() (type /* ERROR "found 'type'" */ T)(T)`,
`package p; func (type /* ERROR "found 'type'" */ T)(T) _()`,
`package p; type _[A+B, /* ERROR "expected ']'" */ ] int`,
- `package p; type _[_ any] int; var _ = T[] /* ERROR "expected operand" */ {}`,
- `package p; type T[P any] = /* ERROR "cannot be alias" */ T0`,
- `package p; func _[]/* ERROR "empty type parameter list" */()`,
- // errors that could be improved
- `package p; var a = a[[]int:[ /* ERROR "expected expression" */ ]int];`, // TODO: should this be on the ':'?
- `package p; type _[A/* ERROR "all type parameters must be named" */,] struct{ A }`, // TODO: a better location would be after the ']'
- `package p; func _[type /* ERROR "all type parameters must be named" */P, *Q interface{}]()`, // TODO: this is confusing.
- `package p; type I1 interface{}; type I2 interface{ (/* ERROR "expected 'IDENT'" */I1) }`, // TODO: compiler error is 'syntax error: cannot parenthesize embedded type'
+ // TODO: this error should be positioned on the ':'
+ `package p; var a = a[[]int:[ /* ERROR "expected expression" */ ]int];`,
+ // TODO: the compiler error is better here: "cannot parenthesize embedded type"
+ `package p; type I1 interface{}; type I2 interface{ (/* ERROR "expected '}', found '\('" */ I1) }`,
// issue 8656
`package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`,
// issue 11611
`package p; type _ struct { int, } /* ERROR "expected 'IDENT', found '}'" */ ;`,
`package p; type _ struct { int, float } /* ERROR "expected type, found '}'" */ ;`,
- //`package p; type _ struct { ( /* ERROR "cannot parenthesize embedded type" */ int) };`,
- //`package p; func _()(x, y, z ... /* ERROR "expected '\)', found '...'" */ int){}`,
- //`package p; func _()(... /* ERROR "expected type, found '...'" */ int){}`,
// issue 13475
`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() }`,
}
+// invalidNoTParamErrs holds invalid source code examples annotated with the
+// error messages produced when ParseTypeParams is not set.
+var invalidNoTParamErrs = []string{
+ `package p; type _[_ any /* ERROR "expected ']', found any" */ ] int; var _ = T[]{}`,
+ `package p; type T[P any /* ERROR "expected ']', found any" */ ] = T0`,
+ `package p; var _ func[ /* ERROR "expected '\(', found '\['" */ T any](T)`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ ]()`,
+ `package p; type _[A, /* ERROR "expected ']', found ','" */] struct{ A }`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ type P, *Q interface{}]()`,
+}
+
+// invalidTParamErrs holds invalid source code examples annotated with the
+// error messages produced when ParseTypeParams is set.
+var invalidTParamErrs = []string{
+ `package p; type _[_ any] int; var _ = T[] /* ERROR "expected operand" */ {}`,
+ `package p; type T[P any] = /* ERROR "cannot be alias" */ T0`,
+ `package p; var _ func[ /* ERROR "cannot have type parameters" */ T any](T)`,
+ `package p; func _[]/* ERROR "empty type parameter list" */()`,
+
+ // TODO(rfindley) a better location would be after the ']'
+ `package p; type _[A/* ERROR "all type parameters must be named" */,] struct{ A }`,
+
+ // TODO(rfindley) this error is confusing.
+ `package p; func _[type /* ERROR "all type parameters must be named" */P, *Q interface{}]()`,
+}
+
func TestInvalid(t *testing.T) {
- for _, src := range invalids {
- checkErrors(t, src, src, DeclarationErrors|AllErrors)
- }
+ t.Run("no tparams", func(t *testing.T) {
+ for _, src := range invalids {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors, true)
+ }
+ for _, src := range validWithTParamsOnly {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors, true)
+ }
+ for _, src := range invalidNoTParamErrs {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors, true)
+ }
+ })
+ t.Run("tparams", func(t *testing.T) {
+ for _, src := range invalids {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, true)
+ }
+ for _, src := range invalidTParamErrs {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, true)
+ }
+ })
}