Args []Expr // function arguments; or nil
Ellipsis token.Pos // position of "..." (token.NoPos if there is no "...")
Rparen token.Pos // position of ")"
- // TODO(rFindley) use a new ListExpr type rather than overloading CallExpr
- // via Brackets, as is done in the syntax package
- Brackets bool // if set, "[" and "]" are used instead of "(" and ")"
+ }
+
+ // A ListExpr node represents a list of expressions separated by commas.
+ // ListExpr nodes are used as index in IndexExpr nodes representing type
+ // or function instantiations with more than one type argument.
+ ListExpr struct {
+ ElemList []Expr
}
// A StarExpr node represents an expression of the form "*" Expression.
func (x *SliceExpr) Pos() token.Pos { return x.X.Pos() }
func (x *TypeAssertExpr) Pos() token.Pos { return x.X.Pos() }
func (x *CallExpr) Pos() token.Pos { return x.Fun.Pos() }
-func (x *StarExpr) Pos() token.Pos { return x.Star }
-func (x *UnaryExpr) Pos() token.Pos { return x.OpPos }
-func (x *BinaryExpr) Pos() token.Pos { return x.X.Pos() }
-func (x *KeyValueExpr) Pos() token.Pos { return x.Key.Pos() }
-func (x *ArrayType) Pos() token.Pos { return x.Lbrack }
-func (x *StructType) Pos() token.Pos { return x.Struct }
+func (x *ListExpr) Pos() token.Pos {
+ if len(x.ElemList) > 0 {
+ return x.ElemList[0].Pos()
+ }
+ return token.NoPos
+}
+func (x *StarExpr) Pos() token.Pos { return x.Star }
+func (x *UnaryExpr) Pos() token.Pos { return x.OpPos }
+func (x *BinaryExpr) Pos() token.Pos { return x.X.Pos() }
+func (x *KeyValueExpr) Pos() token.Pos { return x.Key.Pos() }
+func (x *ArrayType) Pos() token.Pos { return x.Lbrack }
+func (x *StructType) Pos() token.Pos { return x.Struct }
func (x *FuncType) Pos() token.Pos {
if x.Func.IsValid() || x.Params == nil { // see issue 3870
return x.Func
func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 }
func (x *TypeAssertExpr) End() token.Pos { return x.Rparen + 1 }
func (x *CallExpr) End() token.Pos { return x.Rparen + 1 }
-func (x *StarExpr) End() token.Pos { return x.X.End() }
-func (x *UnaryExpr) End() token.Pos { return x.X.End() }
-func (x *BinaryExpr) End() token.Pos { return x.Y.End() }
-func (x *KeyValueExpr) End() token.Pos { return x.Value.End() }
-func (x *ArrayType) End() token.Pos { return x.Elt.End() }
-func (x *StructType) End() token.Pos { return x.Fields.End() }
+func (x *ListExpr) End() token.Pos {
+ if len(x.ElemList) > 0 {
+ return x.ElemList[len(x.ElemList)-1].End()
+ }
+ return token.NoPos
+}
+func (x *StarExpr) End() token.Pos { return x.X.End() }
+func (x *UnaryExpr) End() token.Pos { return x.X.End() }
+func (x *BinaryExpr) End() token.Pos { return x.Y.End() }
+func (x *KeyValueExpr) End() token.Pos { return x.Value.End() }
+func (x *ArrayType) End() token.Pos { return x.Elt.End() }
+func (x *StructType) End() token.Pos { return x.Fields.End() }
func (x *FuncType) End() token.Pos {
if x.Results != nil {
return x.Results.End()
func (*SliceExpr) exprNode() {}
func (*TypeAssertExpr) exprNode() {}
func (*CallExpr) exprNode() {}
+func (*ListExpr) exprNode() {}
func (*StarExpr) exprNode() {}
func (*UnaryExpr) exprNode() {}
func (*BinaryExpr) exprNode() {}
// 40 . . . . . . . }
// 41 . . . . . . . Ellipsis: -
// 42 . . . . . . . Rparen: 4:25
- // 43 . . . . . . . Brackets: false
- // 44 . . . . . . }
- // 45 . . . . . }
- // 46 . . . . }
- // 47 . . . . Rbrace: 5:1
- // 48 . . . }
- // 49 . . }
- // 50 . }
- // 51 . Scope: *ast.Scope {
- // 52 . . Objects: map[string]*ast.Object (len = 1) {
- // 53 . . . "main": *(obj @ 11)
- // 54 . . }
- // 55 . }
- // 56 . Unresolved: []*ast.Ident (len = 1) {
- // 57 . . 0: *(obj @ 29)
- // 58 . }
- // 59 }
+ // 43 . . . . . . }
+ // 44 . . . . . }
+ // 45 . . . . }
+ // 46 . . . . Rbrace: 5:1
+ // 47 . . . }
+ // 48 . . }
+ // 49 . }
+ // 50 . Scope: *ast.Scope {
+ // 51 . . Objects: map[string]*ast.Object (len = 1) {
+ // 52 . . . "main": *(obj @ 11)
+ // 53 . . }
+ // 54 . }
+ // 55 . Unresolved: []*ast.Ident (len = 1) {
+ // 56 . . 0: *(obj @ 29)
+ // 57 . }
+ // 58 }
}
// This example illustrates how to remove a variable declaration
}
// x[P], x[P1, P2], ...
- return nil, &ast.CallExpr{Fun: x, Lparen: lbrack, Args: args, Rparen: rbrack, Brackets: true}
+ return nil, &ast.IndexExpr{X: x, Lbrack: lbrack, Index: &ast.ListExpr{ElemList: args}, Rbrack: rbrack}
}
func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
p.exprLev--
}
rbrack := p.expectClosing(token.RBRACK, "type argument list")
- typ = &ast.CallExpr{Fun: ident, Lparen: lbrack, Args: list, Rparen: rbrack, Brackets: true}
+ typ = &ast.IndexExpr{X: ident, Lbrack: lbrack, Index: &ast.ListExpr{ElemList: list}, Rbrack: rbrack}
}
case p.tok == token.LPAREN:
// ordinary method
closing := p.expectClosing(token.RBRACK, "type argument list")
- return &ast.CallExpr{Fun: typ, Lparen: opening, Args: list, Rparen: closing, Brackets: true}
+ return &ast.IndexExpr{X: typ, Lbrack: opening, Index: &ast.ListExpr{ElemList: list}, Rbrack: closing}
}
// If the result is an identifier, it is not resolved.
}
// instance expression
- return &ast.CallExpr{Fun: x, Lparen: lbrack, Args: args, Rparen: rbrack, Brackets: true}
+ return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: &ast.ListExpr{ElemList: args}, Rbrack: rbrack}
}
func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
// type; accept it but complain if we have a complit
t := unparen(x)
// determine if '{' belongs to a composite literal or a block statement
- switch t := t.(type) {
+ switch t.(type) {
case *ast.BadExpr, *ast.Ident, *ast.SelectorExpr:
if p.exprLev < 0 {
return
}
// x is possibly a composite literal type
- case *ast.CallExpr:
- if !t.Brackets || p.exprLev < 0 {
- return
- }
- // x is possibly a composite literal type
case *ast.IndexExpr:
if p.exprLev < 0 {
return
// TODO(gri): should treat[] like parentheses and undo one level of depth
p.expr1(x.X, token.HighestPrec, 1)
p.print(x.Lbrack, token.LBRACK)
- p.expr0(x.Index, depth+1)
+ if e, _ := x.Index.(*ast.ListExpr); e != nil {
+ p.exprList(x.Lbrack, e.ElemList, depth+1, commaTerm, x.Rbrack, false)
+ } else {
+ p.expr0(x.Index, depth+1)
+ }
p.print(x.Rbrack, token.RBRACK)
case *ast.SliceExpr:
depth++
}
var wasIndented bool
- if x.Brackets {
+ if _, ok := x.Fun.(*ast.FuncType); ok {
+ // conversions to literal function types require parentheses around the type
+ p.print(token.LPAREN)
wasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)
- p.print(x.Lparen, token.LBRACK)
- p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen, false)
- p.print(x.Rparen, token.RBRACK)
+ p.print(token.RPAREN)
} else {
- if _, ok := x.Fun.(*ast.FuncType); ok {
- // conversions to literal function types require parentheses around the type
- p.print(token.LPAREN)
- wasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)
- p.print(token.RPAREN)
- } else {
- wasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)
- }
- p.print(x.Lparen, token.LPAREN)
- if x.Ellipsis.IsValid() {
- p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis, false)
- p.print(x.Ellipsis, token.ELLIPSIS)
- if x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) {
- p.print(token.COMMA, formfeed)
- }
- } else {
- p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen, false)
+ wasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)
+ }
+ p.print(x.Lparen, token.LPAREN)
+ if x.Ellipsis.IsValid() {
+ p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis, false)
+ p.print(x.Ellipsis, token.ELLIPSIS)
+ if x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) {
+ p.print(token.COMMA, formfeed)
}
- p.print(x.Rparen, token.RPAREN)
+ } else {
+ p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen, false)
}
+ p.print(x.Rparen, token.RPAREN)
if wasIndented {
p.print(unindent)
}
{broken + `x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string][-1]int`},
// parameterized functions
- {genericPkg + `p0; func f[T any](T); var _ = f(int)`, `f`, `func[T₁ interface{}](T₁)`},
- {genericPkg + `p1; func f[T any](T); var _ = f(int)`, `f(int)`, `func(int)`},
+ {genericPkg + `p0; func f[T any](T); var _ = f[int]`, `f`, `func[T₁ interface{}](T₁)`},
+ {genericPkg + `p1; func f[T any](T); var _ = f[int]`, `f[int]`, `func(int)`},
{genericPkg + `p2; func f[T any](T); func _() { f(42) }`, `f`, `func[T₁ interface{}](T₁)`},
{genericPkg + `p3; func f[T any](T); func _() { f(42) }`, `f(42)`, `()`},
}
}
+// unpack unpacks an *ast.ListExpr into a list of ast.Expr.
+// TODO(gri) Should find a more efficient solution that doesn't
+// require introduction of a new slice for simple
+// expressions.
+func unpackExpr(x ast.Expr) []ast.Expr {
+ if x, _ := x.(*ast.ListExpr); x != nil {
+ return x.ElemList
+ }
+ if x != nil {
+ return []ast.Expr{x}
+ }
+ return nil
+}
+
func (check *Checker) shortVarDecl(pos positioner, lhs, rhs []ast.Expr) {
top := len(check.delayed)
scope := check.scope
"unicode"
)
-// TODO(rFindley) this has diverged a bit from types2. Bring it up to date.
-// If call == nil, the "call" was an index expression, and orig is of type *ast.IndexExpr.
-func (check *Checker) call(x *operand, call *ast.CallExpr, orig ast.Expr) exprKind {
- assert(orig != nil)
- if call != nil {
- assert(call == orig)
- check.exprOrType(x, call.Fun)
- } else {
- // We must have an index expression.
- // x has already been set up (evaluation of orig.X).
- // Set up fake call so we can use its fields below.
- expr := orig.(*ast.IndexExpr)
- call = &ast.CallExpr{Fun: expr.X, Lparen: expr.Lbrack, Args: []ast.Expr{expr.Index}, Rparen: expr.Rbrack, Brackets: true}
+// funcInst type-checks a function instantiaton inst and returns the result in x.
+// The operand x must be the evaluation of inst.X and its type must be a signature.
+func (check *Checker) funcInst(x *operand, inst *ast.IndexExpr) {
+ args, ok := check.exprOrTypeList(unpackExpr(inst.Index))
+ if !ok {
+ x.mode = invalid
+ x.expr = inst
+ return
+ }
+ if len(args) > 0 && args[0].mode != typexpr {
+ check.errorf(args[0], _NotAType, "%s is not a type", args[0])
+ ok = false
+ }
+
+ // check number of type arguments
+ n := len(args)
+ sig := x.typ.(*Signature)
+ if n > len(sig.tparams) {
+ check.errorf(args[n-1], _Todo, "got %d type arguments but want %d", n, len(sig.tparams))
+ x.mode = invalid
+ x.expr = inst
+ return
+ }
+
+ // collect types
+ targs := make([]Type, n)
+ // TODO(rFindley) use a positioner here? instantiate would need to be
+ // updated accordingly.
+ poslist := make([]token.Pos, n)
+ for i, a := range args {
+ if a.mode != typexpr {
+ // error was reported earlier
+ x.mode = invalid
+ x.expr = inst
+ return
+ }
+ targs[i] = a.typ
+ poslist[i] = a.Pos()
+ }
+
+ // if we don't have enough type arguments, use constraint type inference
+ var inferred bool
+ if n < len(sig.tparams) {
+ var failed int
+ targs, failed = check.inferB(sig.tparams, targs)
+ if targs == nil {
+ // error was already reported
+ x.mode = invalid
+ x.expr = inst
+ return
+ }
+ if failed >= 0 {
+ // at least one type argument couldn't be inferred
+ assert(targs[failed] == nil)
+ tpar := sig.tparams[failed]
+ check.errorf(inNode(inst, inst.Rbrack), 0, "cannot infer %s (%v) (%s)", tpar.name, tpar.pos, targs)
+ x.mode = invalid
+ x.expr = inst
+ return
+ }
+ // all type arguments were inferred sucessfully
+ if debug {
+ for _, targ := range targs {
+ assert(targ != nil)
+ }
+ }
+ n = len(targs)
+ inferred = true
}
+ assert(n == len(sig.tparams))
+
+ // instantiate function signature
+ for i, typ := range targs {
+ // some positions may be missing if types are inferred
+ var pos token.Pos
+ if i < len(poslist) {
+ pos = poslist[i]
+ }
+ check.ordinaryType(atPos(pos), typ)
+ }
+ res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
+ assert(res.tparams == nil) // signature is not generic anymore
+ if inferred {
+ check.recordInferred(inst, targs, res)
+ }
+ x.typ = res
+ x.mode = value
+ x.expr = inst
+}
+
+func (check *Checker) call(x *operand, call *ast.CallExpr) exprKind {
+ check.exprOrType(x, call.Fun)
switch x.mode {
case invalid:
check.use(call.Args...)
- x.expr = orig
+ x.expr = call
return statement
case typexpr:
check.use(call.Args...)
check.errorf(call.Args[n-1], _WrongArgCount, "too many arguments in conversion to %s", T)
}
- x.expr = orig
+ x.expr = call
return conversion
case builtin:
if !check.builtin(x, call, id) {
x.mode = invalid
}
- x.expr = orig
+ x.expr = call
// a non-constant result implies a function call
if x.mode != invalid && x.mode != constant_ {
check.hasCallOrRecv = true
if sig == nil {
check.invalidOp(x, _InvalidCall, "cannot call non-function %s", x)
x.mode = invalid
- x.expr = orig
+ x.expr = call
return statement
}
// evaluate arguments
args, ok := check.exprOrTypeList(call.Args)
- if ok && call.Brackets && len(args) > 0 && args[0].mode != typexpr {
- check.errorf(args[0], _NotAType, "%s is not a type", args[0])
- ok = false
- }
if !ok {
x.mode = invalid
- x.expr = orig
- return expression
- }
-
- // instantiate function if needed
- if n := len(args); n > 0 && len(sig.tparams) > 0 && args[0].mode == typexpr {
- // If the first argument is a type, assume we have explicit type arguments.
-
- // check number of type arguments
- if n > len(sig.tparams) {
- check.errorf(args[n-1], _Todo, "got %d type arguments but want %d", n, len(sig.tparams))
- x.mode = invalid
- x.expr = orig
- return expression
- }
-
- // collect types
- targs := make([]Type, n)
- // TODO(rFindley) use a positioner here? instantiate would need to be
- // updated accordingly.
- poslist := make([]token.Pos, n)
- for i, a := range args {
- if a.mode != typexpr {
- // error was reported earlier
- x.mode = invalid
- x.expr = orig
- return expression
- }
- targs[i] = a.typ
- poslist[i] = a.Pos()
- }
-
- // if we don't have enough type arguments, use constraint type inference
- var inferred bool
- if n < len(sig.tparams) {
- var failed int
- targs, failed = check.inferB(sig.tparams, targs)
- if targs == nil {
- // error was already reported
- x.mode = invalid
- x.expr = orig
- return expression
- }
- if failed >= 0 {
- // at least one type argument couldn't be inferred
- assert(targs[failed] == nil)
- tpar := sig.tparams[failed]
- ppos := check.fset.Position(tpar.pos).String()
- check.errorf(inNode(call, call.Rparen), 0, "cannot infer %s (%s) (%s)", tpar.name, ppos, targs)
- x.mode = invalid
- x.expr = orig
- return expression
- }
- // all type arguments were inferred sucessfully
- if debug {
- for _, targ := range targs {
- assert(targ != nil)
- }
- }
- n = len(targs)
- inferred = true
- }
- assert(n == len(sig.tparams))
-
- // instantiate function signature
- for i, typ := range targs {
- // some positions may be missing if types are inferred
- var pos token.Pos
- if i < len(poslist) {
- pos = poslist[i]
- }
- check.ordinaryType(atPos(pos), typ)
- }
- res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
- assert(res.tparams == nil) // signature is not generic anymore
- if inferred {
- check.recordInferred(orig, targs, res)
- }
- x.typ = res
- x.mode = value
- x.expr = orig
+ x.expr = call
return expression
}
- // If we reach here, orig must have been a regular call, not an index
- // expression.
- // TODO(rFindley) with a manually constructed AST it is possible to reach
- // this assertion. We should return an invalidAST error here
- // rather than panicking.
- assert(!call.Brackets)
-
sig = check.arguments(call, sig, args)
// determine result
// result type from the assignment to keep things simple and
// easy to understand.
var _ = new[int]()
-var _ *float64 = new(float64)() // the result type is indeed *float64
+var _ *float64 = new[float64]() // the result type is indeed *float64
// A function may have multiple type parameters, of course.
func foo[A, B, C any](a A, b []B, c *C) B {
}
// As before, we can pass type parameters explicitly.
-var s = foo[int, string, float64](1, []string{"first"}, new(float64)())
+var s = foo[int, string, float64](1, []string{"first"}, new[float64]())
// Or we can use type inference.
var _ float64 = foo(42, []float64{1.0}, &s)
if x.mode == value {
if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
- return check.call(x, nil, e)
+ check.funcInst(x, e)
+ return expression
}
}
x.typ = T
case *ast.CallExpr:
- return check.call(x, e, e)
+ return check.call(x, e)
case *ast.StarExpr:
check.exprOrType(x, e.X)
WriteExpr(buf, x.Index)
buf.WriteByte(']')
+ case *ast.ListExpr:
+ for i, e := range x.ElemList {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ WriteExpr(buf, e)
+ }
+
case *ast.SliceExpr:
WriteExpr(buf, x.X)
buf.WriteByte('[')
case *ast.CallExpr:
WriteExpr(buf, x.Fun)
- var l, r byte = '(', ')'
- if x.Brackets {
- l, r = '[', ']'
- }
- buf.WriteByte(l)
+ buf.WriteByte('(')
writeExprList(buf, x.Args)
if x.Ellipsis.IsValid() {
buf.WriteString("...")
}
- buf.WriteByte(r)
+ buf.WriteByte(')')
case *ast.StarExpr:
buf.WriteByte('*')
}
// unpack type parameters, if any
- switch ptyp := rtyp.(type) {
- case *ast.IndexExpr:
- panic("unimplemented")
- case *ast.CallExpr:
- rtyp = ptyp.Fun
+ if ptyp, _ := rtyp.(*ast.IndexExpr); ptyp != nil {
+ rtyp = ptyp.X
if unpackParams {
- for _, arg := range ptyp.Args {
+ for _, arg := range unpackExpr(ptyp.Index) {
var par *ast.Ident
switch arg := arg.(type) {
case *ast.Ident:
eql(x, x)
eql(y, y)
eql(y, nil)
- eql(io.Reader)(nil, nil)
+ eql[io.Reader](nil, nil)
}
// If we have a receiver of pointer type (below: *T) we must ignore
func (*T) m2()
func _() {
- f2(T /* ERROR wrong method signature */ )()
- f2(*T)()
+ f2[T /* ERROR wrong method signature */]()
+ f2[*T]()
}
// When a type parameter is used as an argument to instantiate a parameterized
var f func()
var cancelSlice []context.CancelFunc
-var _ = append(context.CancelFunc, []context.CancelFunc, context.CancelFunc)(cancelSlice, f)
+var _ = append[context.CancelFunc, []context.CancelFunc, context.CancelFunc](cancelSlice, f)
// A generic function must be instantiated with a type, not a value.
func swap[A, B any](a A, b B) (B, A) { return b, a }
var _ = swap /* ERROR single value is expected */ [int, float32](1, 2)
-var f32, i = swap[int, float32](swap(float32, int)(1, 2))
+var f32, i = swap[int, float32](swap[float32, int](1, 2))
var _ float32 = f32
var _ int = i
func _[T any](map[T /* ERROR incomparable map key type T \(missing comparable constraint\) */]int) // w/o constraint we don't know if T is comparable
func f1[T1 any](struct{T1}) int
-var _ = f1(int)(struct{T1}{})
+var _ = f1[int](struct{T1}{})
type T1 = int
func f2[t1 any](struct{t1; x float32}) int
-var _ = f2(t1)(struct{t1; x float32}{})
+var _ = f2[t1](struct{t1; x float32}{})
type t1 = int
var _ = f8(1, 2.3)
var _ = f8(1, 2.3, 3.4, 4.5)
var _ = f8(1, 2.3, 3.4, 4 /* ERROR does not match */ )
-var _ = f8(int, float64)(1, 2.3, 3.4, 4)
+var _ = f8[int, float64](1, 2.3, 3.4, 4)
-var _ = f8(int, float64)(0, 0, nil...) // test case for #18268
+var _ = f8[int, float64](0, 0, nil...) // test case for #18268
// init functions cannot have type parameters
func (a A[T]) m() A[T]
func _[T any]() {
- f12(A[T])()
+ f12[A[T]]()
}
// method expressions
new.X = X
return &new
}
- case *ast.CallExpr:
- var args []ast.Expr
- for i, arg := range n.Args {
- new := isubst(arg, smap)
- if new != arg {
- if args == nil {
- args = make([]ast.Expr, len(n.Args))
- copy(args, n.Args)
+ case *ast.IndexExpr:
+ index := isubst(n.Index, smap)
+ if index != n.Index {
+ new := *n
+ new.Index = index
+ return &new
+ }
+ case *ast.ListExpr:
+ var elems []ast.Expr
+ for i, elem := range n.ElemList {
+ new := isubst(elem, smap)
+ if new != elem {
+ if elems == nil {
+ elems = make([]ast.Expr, len(n.ElemList))
+ copy(elems, n.ElemList)
}
- args[i] = new
+ elems[i] = new
}
}
- if args != nil {
+ if elems != nil {
new := *n
- new.Args = args
+ new.ElemList = elems
return &new
}
case *ast.ParenExpr:
}
case *ast.IndexExpr:
- return check.instantiatedType(e.X, []ast.Expr{e.Index}, def)
-
- case *ast.CallExpr:
- if e.Brackets {
- return check.instantiatedType(e.Fun, e.Args, def)
- } else {
- check.errorf(e0, _NotAType, "%s is not a type", e0)
- }
+ return check.instantiatedType(e.X, unpackExpr(e.Index), def)
case *ast.ParenExpr:
// Generic types must be instantiated before they can be used in any form.
return e.Sel
case *ast.IndexExpr:
return embeddedFieldIdent(e.X)
- case *ast.CallExpr:
- if e.Brackets {
- return embeddedFieldIdent(e.Fun)
- }
}
return nil // invalid embedded field
}