Skip to content

Commit 460abab

Browse files
committed
pattern: support type aliases
1 parent 8856470 commit 460abab

File tree

3 files changed

+69
-6
lines changed

3 files changed

+69
-6
lines changed

debug/debug.go

+15
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"go/parser"
1010
"go/token"
1111
"go/types"
12+
"sync"
1213
)
1314

1415
// TypeCheck parses and type-checks a single-file Go package from a string.
@@ -45,3 +46,17 @@ func FormatNode(node ast.Node) string {
4546
format.Node(&buf, fset, node)
4647
return buf.String()
4748
}
49+
50+
var aliasesDefaultOnce sync.Once
51+
var gotypesaliasDefault bool
52+
53+
func AliasesEnabled() bool {
54+
// Dynamically check if Aliases will be produced from go/types.
55+
aliasesDefaultOnce.Do(func() {
56+
fset := token.NewFileSet()
57+
f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0)
58+
pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
59+
_, gotypesaliasDefault = pkg.Scope().Lookup("A").Type().(*types.Alias)
60+
})
61+
return gotypesaliasDefault
62+
}

pattern/match.go

+21-6
Original file line numberDiff line numberDiff line change
@@ -586,13 +586,28 @@ func (fn Symbol) Match(m *Matcher, node interface{}) (interface{}, bool) {
586586
case *types.Builtin:
587587
name = obj.Name()
588588
case *types.TypeName:
589-
if obj.Pkg() == nil {
590-
return nil, false
591-
}
592-
if obj.Parent() != obj.Pkg().Scope() {
593-
return nil, false
589+
origObj := obj
590+
for {
591+
if obj.Parent() != obj.Pkg().Scope() {
592+
return nil, false
593+
}
594+
name = types.TypeString(obj.Type(), nil)
595+
_, ok = match(m, fn.Name, name)
596+
if ok || !obj.IsAlias() {
597+
return origObj, ok
598+
} else {
599+
// FIXME(dh): we should peel away one layer of alias at a time; this is blocked on
600+
// github.com/golang/go/issues/66559
601+
switch typ := types.Unalias(obj.Type()).(type) {
602+
case interface{ Obj() *types.TypeName }:
603+
obj = typ.Obj()
604+
case *types.Basic:
605+
return match(m, fn.Name, typ.Name())
606+
default:
607+
return nil, false
608+
}
609+
}
594610
}
595-
name = types.TypeString(obj.Type(), nil)
596611
case *types.Const, *types.Var:
597612
if obj.Pkg() == nil {
598613
return nil, false

pattern/parser_test.go

+33
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"runtime"
1212
"strings"
1313
"testing"
14+
15+
"honnef.co/go/tools/debug"
1416
)
1517

1618
func TestParse(t *testing.T) {
@@ -112,3 +114,34 @@ func FuzzParse(f *testing.F) {
112114
}
113115
})
114116
}
117+
118+
func TestMatchAlias(t *testing.T) {
119+
p1 := MustParse(`(CallExpr (Symbol "foo.Alias") _)`)
120+
p2 := MustParse(`(CallExpr (Symbol "int") _)`)
121+
122+
f, _, info, err := debug.TypeCheck(`
123+
package pkg
124+
type Alias = int
125+
func _() { _ = Alias(0) }
126+
`)
127+
if err != nil {
128+
t.Fatal(err)
129+
}
130+
131+
m := &Matcher{
132+
TypesInfo: info,
133+
}
134+
node := f.Decls[1].(*ast.FuncDecl).Body.List[0].(*ast.AssignStmt).Rhs[0]
135+
136+
if debug.AliasesEnabled() {
137+
// Check that we can match on the name of the alias
138+
if ok := m.Match(p1, node); !ok {
139+
t.Errorf("%s did not match", p1.Root)
140+
}
141+
}
142+
143+
// Check that we can match on the name of the alias's target
144+
if ok := m.Match(p2, node); !ok {
145+
t.Errorf("%s did not match", p2.Root)
146+
}
147+
}

0 commit comments

Comments
 (0)