Commit f5636523 authored by Robert Griesemer's avatar Robert Griesemer

go/types: accept recv base type that is alias to a pointer type

Per the spec clarification https://golang.org/cl/142757 (issue #27995).

Fixes #28251.
Updates #27995.

Change-Id: Idc142829955f9306a8698c5ed1c24baa8ee2b109
Reviewed-on: https://go-review.googlesource.com/c/143179Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent 0287d8ed
......@@ -94,6 +94,7 @@ var tests = [][]string{
{"testdata/issue26390.src"}, // stand-alone test to ensure case is triggered
{"testdata/issue23203a.src"},
{"testdata/issue23203b.src"},
{"testdata/issue28251.src"},
}
var fset = token.NewFileSet()
......
......@@ -460,65 +460,74 @@ func (check *Checker) collectObjects() {
for _, f := range methods {
fdecl := check.objMap[f].fdecl
if list := fdecl.Recv.List; len(list) > 0 {
// f is a method
// receiver may be of the form T or *T, possibly with parentheses
typ := unparen(list[0].Type)
if ptr, _ := typ.(*ast.StarExpr); ptr != nil {
typ = unparen(ptr.X)
// TODO(gri): This may not be sufficient. See issue #27995.
f.hasPtrRecv = true
}
if base, _ := typ.(*ast.Ident); base != nil {
// base is a potential base type name; determine
// "underlying" defined type and associate f with it
if tname := check.resolveBaseTypeName(base); tname != nil {
check.methods[tname] = append(check.methods[tname], f)
}
// f is a method.
// Determine the receiver base type and associate f with it.
ptr, base := check.resolveBaseTypeName(list[0].Type)
if base != nil {
f.hasPtrRecv = ptr
check.methods[base] = append(check.methods[base], f)
}
}
}
}
// resolveBaseTypeName returns the non-alias receiver base type name,
// explicitly declared in the package scope, for the given receiver
// type name; or nil.
func (check *Checker) resolveBaseTypeName(name *ast.Ident) *TypeName {
// resolveBaseTypeName returns the non-alias base type name for typ, and whether
// there was a pointer indirection to get to it. The base type name must be declared
// in package scope, and there can be at most one pointer indirection. If no such type
// name exists, the returned base is nil.
func (check *Checker) resolveBaseTypeName(typ ast.Expr) (ptr bool, base *TypeName) {
// Algorithm: Starting from a type expression, which may be a name,
// we follow that type through alias declarations until we reach a
// non-alias type name. If we encounter anything but pointer types or
// parentheses we're done. If we encounter more than one pointer type
// we're done.
var path []*TypeName
for {
typ = unparen(typ)
// check if we have a pointer type
if pexpr, _ := typ.(*ast.StarExpr); pexpr != nil {
// if we've already seen a pointer, we're done
if ptr {
return false, nil
}
ptr = true
typ = unparen(pexpr.X) // continue with pointer base type
}
// typ must be the name
name, _ := typ.(*ast.Ident)
if name == nil {
return false, nil
}
// name must denote an object found in the current package scope
// (note that dot-imported objects are not in the package scope!)
obj := check.pkg.scope.Lookup(name.Name)
if obj == nil {
return nil
return false, nil
}
// the object must be a type name...
tname, _ := obj.(*TypeName)
if tname == nil {
return nil
return false, nil
}
// ... which we have not seen before
if check.cycle(tname, path, false) {
return nil
return false, nil
}
// we're done if tdecl defined tname as a new type
// (rather than an alias)
tdecl := check.objMap[tname] // must exist for objects in package scope
if !tdecl.alias {
return tname
}
// Otherwise, if tdecl defined an alias for a (possibly parenthesized)
// type which is not an (unqualified) named type, we're done because
// receiver base types must be named types declared in this package.
typ := unparen(tdecl.typ) // a type may be parenthesized
name, _ = typ.(*ast.Ident)
if name == nil {
return nil
return ptr, tname
}
// continue resolving name
// otherwise, continue resolving
typ = tdecl.typ
path = append(path, tname)
}
}
......
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains test cases for various forms of
// method receiver declarations, per the spec clarification
// https://golang.org/cl/142757.
package issue28251
// test case from issue28251
type T struct{}
type T0 = *T
func (T0) m() {}
func _() { (&T{}).m() }
// various alternative forms
type (
T1 = (((T)))
)
func ((*(T1))) m1() {}
func _() { (T{}).m2() }
func _() { (&T{}).m2() }
type (
T2 = (((T3)))
T3 = T
)
func (T2) m2() {}
func _() { (T{}).m2() }
func _() { (&T{}).m2() }
type (
T4 = ((*(T5)))
T5 = T
)
func (T4) m4() {}
func _() { (T{}).m4 /* ERROR m4 is not in method set of T */ () }
func _() { (&T{}).m4() }
type (
T6 = (((T7)))
T7 = (*(T8))
T8 = T
)
func (T6) m6() {}
func _() { (T{}).m6 /* ERROR m6 is not in method set of T */ () }
func _() { (&T{}).m6() }
type (
T9 = *T10
T10 = *T11
T11 = T
)
func (T9 /* ERROR invalid receiver \*\*T */ ) m9() {}
func _() { (T{}).m9 /* ERROR has no field or method m9 */ () }
func _() { (&T{}).m9 /* ERROR has no field or method m9 */ () }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment