Skip to content

Commit

Permalink
Merge pull request #32 from gostaticanalysis/add-Under
Browse files Browse the repository at this point in the history
Add Under
  • Loading branch information
tenntenn authored Sep 29, 2020
2 parents 06bde33 + 1468452 commit 47295f6
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 57 deletions.
17 changes: 17 additions & 0 deletions helper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package analysisutil_test

import (
"testing"

"golang.org/x/tools/go/analysis/analysistest"
)

func WriteFiles(t *testing.T, filemap map[string]string) string {
t.Helper()
dir, clean, err := analysistest.WriteFiles(filemap)
if err != nil {
t.Fatal("unexpected error:", err)
}
t.Cleanup(clean)
return dir
}
15 changes: 0 additions & 15 deletions testdata/src/objectof/main.go

This file was deleted.

5 changes: 0 additions & 5 deletions testdata/src/objectof/vendor/vendored/vendored.go

This file was deleted.

10 changes: 10 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,13 @@ func mergeTypesInfo(i1, i2 *types.Info) {
// InitOrder
i1.InitOrder = append(i1.InitOrder, i2.InitOrder...)
}

// Under returns the most bottom underlying type.
func Under(t types.Type) types.Type {
switch t := t.(type) {
case *types.Named:
return Under(t.Underlying())
default:
return t
}
}
134 changes: 97 additions & 37 deletions types_test.go
Original file line number Diff line number Diff line change
@@ -1,57 +1,117 @@
package analysisutil_test

import (
"errors"
"fmt"
"go/token"
"go/types"
"path/filepath"
"testing"

"github.com/gostaticanalysis/analysisutil"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/passes/buildssa"
)

const pkg = "objectof"
func TestObjectOf(t *testing.T) {
t.Parallel()

var typesAnalyzer = &analysis.Analyzer{
Name: "test_types",
Run: run_test_types,
Requires: []*analysis.Analyzer{
buildssa.Analyzer,
},
}

func Test_Types(t *testing.T) {
testdata := analysistest.TestData()
analysistest.Run(t, testdata, typesAnalyzer, pkg)
}

func run_test_types(pass *analysis.Pass) (interface{}, error) {
tests := []struct {
path, name string
found bool
cases := map[string]struct {
src string
pkg string // blank means same as the map key
name string
found bool
}{
{"fmt", "Println", true},
{pkg, "A", true},
{pkg, "EOF", true},
{"io", "EOF", true},
{"reflect", "Kind", false},
{"a", "ok", false},
{"vendored", "EOF", true},
{"c", "EOF", false},
{"database/sql", "*DB", false},
"standard": {`import _ "fmt"`, "fmt", "Println", true},
"unimport": {"", "fmt", "Println", false},
"notexiststd": {`import _ "fmt"`, "fmt", "NOTEXIST", false},
"typename": {"type A int", "", "A", true},
"unexportvar": {"var n int", "", "n", true},
"exportvar": {"var N int", "", "N", true},
"notexist": {"", "", "NOTEXIST", false},
"vendored": {`import _ "fmt"`, "vendor/fmt", "Println", true},
"pointer": {"type A int", "", "*A", false},
}

for _, tt := range tests {
tt := tt
obj := analysisutil.ObjectOf(pass, tt.path, tt.name)
for name, tt := range cases {
name, tt := name, tt
t.Run(name, func(t *testing.T) {
t.Parallel()
a := &analysis.Analyzer{
Name: name + "Analyzer",
Run: func(pass *analysis.Pass) (interface{}, error) {
pkg := name
if tt.pkg != "" {
pkg = tt.pkg
}
obj := analysisutil.ObjectOf(pass, pkg, tt.name)
switch {
case tt.found && obj == nil:
return nil, errors.New("expect found but not found")
case !tt.found && obj != nil:
return nil, fmt.Errorf("unexpected return value: %v", obj)
}
return nil, nil
},
}
path := filepath.Join(name, name+".go")
dir := WriteFiles(t, map[string]string{
path: fmt.Sprintf("package %s\n%s", name, tt.src),
})
analysistest.Run(t, dir, a, name)
})
}

if obj == nil && tt.found {
pass.Reportf(token.NoPos, "objectof could not find %s.%s", tt.path, tt.name)
}
if obj != nil && !tt.found {
pass.Reportf(token.NoPos, "objectof found %s.%s, which does not exist", tt.path, tt.name)
}

func TestUnder(t *testing.T) {
t.Parallel()

lookup := func(pass *analysis.Pass, n string) (types.Type, error) {
_, obj := pass.Pkg.Scope().LookupParent(n, token.NoPos)
if obj == nil {
return nil, fmt.Errorf("does not find: %s", n)
}
return obj.Type(), nil
}

cases := map[string]struct {
src string
typ string
want string
}{
"nonamed": {"", "int", "int"},
"named": {"type A int", "A", "int"},
"twonamed": {"type A int; type B A", "B", "int"},
}

return nil, nil
for name, tt := range cases {
name, tt := name, tt
t.Run(name, func(t *testing.T) {
t.Parallel()
a := &analysis.Analyzer{
Name: name + "Analyzer",
Run: func(pass *analysis.Pass) (interface{}, error) {
typ, err := lookup(pass, tt.typ)
if err != nil {
return nil, err
}
want, err := lookup(pass, tt.want)
if err != nil {
return nil, err
}
got := analysisutil.Under(typ)
if !types.Identical(want, got) {
return nil, fmt.Errorf("want %v but got %v", want, got)
}
return nil, nil
},
}
path := filepath.Join(name, name+".go")
dir := WriteFiles(t, map[string]string{
path: fmt.Sprintf("package %s\n%s", name, tt.src),
})
analysistest.Run(t, dir, a, name)
})
}
}

0 comments on commit 47295f6

Please sign in to comment.