Skip to content

Commit

Permalink
pattern: support type aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
dominikh committed Apr 24, 2024
1 parent 53a20a1 commit 4c40d00
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 6 deletions.
15 changes: 15 additions & 0 deletions debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"go/parser"
"go/token"
"go/types"
"sync"
)

// TypeCheck parses and type-checks a single-file Go package from a string.
Expand Down Expand Up @@ -45,3 +46,17 @@ func FormatNode(node ast.Node) string {
format.Node(&buf, fset, node)
return buf.String()
}

var aliasesDefaultOnce sync.Once
var gotypesaliasDefault bool

func AliasesEnabled() bool {
// Dynamically check if Aliases will be produced from go/types.
aliasesDefaultOnce.Do(func() {
fset := token.NewFileSet()
f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0)
pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
_, gotypesaliasDefault = pkg.Scope().Lookup("A").Type().(*types.Alias)
})
return gotypesaliasDefault
}
27 changes: 21 additions & 6 deletions pattern/match.go
Original file line number Diff line number Diff line change
Expand Up @@ -586,13 +586,28 @@ func (fn Symbol) Match(m *Matcher, node interface{}) (interface{}, bool) {
case *types.Builtin:
name = obj.Name()
case *types.TypeName:
if obj.Pkg() == nil {
return nil, false
}
if obj.Parent() != obj.Pkg().Scope() {
return nil, false
origObj := obj
for {
if obj.Parent() != obj.Pkg().Scope() {
return nil, false
}
name = types.TypeString(obj.Type(), nil)
_, ok = match(m, fn.Name, name)
if ok || !obj.IsAlias() {
return origObj, ok
} else {
// FIXME(dh): we should peel away one layer of alias at a time; this is blocked on
// github.com/golang/go/issues/66559
switch typ := types.Unalias(obj.Type()).(type) {
case interface{ Obj() *types.TypeName }:
obj = typ.Obj()
case *types.Basic:
return match(m, fn.Name, typ.Name())
default:
return nil, false
}
}
}
name = types.TypeString(obj.Type(), nil)
case *types.Const, *types.Var:
if obj.Pkg() == nil {
return nil, false
Expand Down
33 changes: 33 additions & 0 deletions pattern/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"runtime"
"strings"
"testing"

"honnef.co/go/tools/debug"
)

func TestParse(t *testing.T) {
Expand Down Expand Up @@ -112,3 +114,34 @@ func FuzzParse(f *testing.F) {
}
})
}

func TestMatchAlias(t *testing.T) {
p1 := MustParse(`(CallExpr (Symbol "foo.Alias") _)`)
p2 := MustParse(`(CallExpr (Symbol "int") _)`)

f, _, info, err := debug.TypeCheck(`
package pkg
type Alias = int
func _() { _ = Alias(0) }
`)
if err != nil {
t.Fatal(err)
}

m := &Matcher{
TypesInfo: info,
}
node := f.Decls[1].(*ast.FuncDecl).Body.List[0].(*ast.AssignStmt).Rhs[0]

if debug.AliasesEnabled() {
// Check that we can match on the name of the alias
if ok := m.Match(p1, node); !ok {
t.Errorf("%s did not match", p1.Root)
}
}

// Check that we can match on the name of the alias's target
if ok := m.Match(p2, node); !ok {
t.Errorf("%s did not match", p2.Root)
}
}

0 comments on commit 4c40d00

Please sign in to comment.