blob: e8db64530e638b125e947c1f83525fe57256c85e [file] [log] [blame]
// Copyright 2019 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.
package cache
import (
"bytes"
"go/ast"
"go/format"
"go/parser"
"go/token"
"go/types"
"reflect"
"sort"
"testing"
"golang.org/x/tools/go/packages"
)
func TestArrayLength(t *testing.T) {
tests := []struct {
expr string
length int
}{
{`[...]int{0,1,2,3,4,5,6,7,8,9}`, 10},
{`[...]int{9:0}`, 10},
{`[...]int{19-10:0}`, 10},
{`[...]int{19-10:0, 17-10:0, 18-10:0}`, 10},
}
for _, tt := range tests {
expr, err := parser.ParseExpr(tt.expr)
if err != nil {
t.Fatal(err)
}
l, ok := arrayLength(expr.(*ast.CompositeLit))
if !ok {
t.Errorf("arrayLength did not recognize expression %#v", expr)
}
if l != tt.length {
t.Errorf("arrayLength(%#v) = %v, want %v", expr, l, tt.length)
}
}
}
func TestTrim(t *testing.T) {
tests := []struct {
name string
file string
kept []string
}{
{
name: "delete_unused",
file: `
type x struct{}
func y()
var z int
`,
kept: []string{},
},
{
// From the common type in testing.
name: "unexported_embedded",
file: `
type x struct {}
type Exported struct { x }
`,
kept: []string{"Exported", "x"},
},
{
// From the d type in unicode.
name: "exported_field_unexported_type",
file: `
type x struct {}
type Exported struct {
X x
}
`,
kept: []string{"Exported", "x"},
},
{
// From errNotExist in io/fs.
name: "exported_var_function_call",
file: `
func x() int { return 0 }
var Exported = x()
`,
kept: []string{"Exported", "x"},
},
{
// From DefaultServeMux in net/http.
name: "exported_pointer_to_unexported_var",
file: `
var Exported = &x
var x int
`,
kept: []string{"Exported", "x"},
},
{
// From DefaultWriter in goldmark/renderer/html.
name: "exported_pointer_to_composite_lit",
file: `
var Exported = &x{}
type x struct{}
`,
kept: []string{"Exported", "x"},
},
{
// From SelectDir in reflect.
name: "leave_constants",
file: `
type Enum int
const (
_ Enum = iota
EnumOne
)
`,
kept: []string{"Enum", "EnumOne"},
},
{
name: "constant_conversion",
file: `
type x int
const (
foo x = 0
)
`,
kept: []string{"x", "foo"},
},
{
name: "unexported_return",
file: `
type x int
func Exported() x {}
type y int
type Interface interface {
Exported() y
}
`,
kept: []string{"Exported", "Interface", "x", "y"},
},
{
name: "drop_composite_literals",
file: `
type x int
type Exported struct {
foo x
}
var Var = Exported{foo:1}
`,
kept: []string{"Exported", "Var", "x"},
},
{
name: "drop_function_literals",
file: `
type x int
var Exported = func() { return x(0) }
`,
kept: []string{"Exported"},
},
{
name: "missing_receiver_panic",
file: `
func() foo() {}
`,
kept: []string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "main.go", "package main\n\n"+tt.file, parser.AllErrors)
if err != nil {
t.Fatal(err)
}
filter := &unexportedFilter{uses: map[string]bool{}}
filter.Filter([]*ast.File{file})
pkg := types.NewPackage("main", "main")
checker := types.NewChecker(&types.Config{
DisableUnusedImportCheck: true,
}, fset, pkg, nil)
if err := checker.Files([]*ast.File{file}); err != nil {
t.Error(err)
}
names := pkg.Scope().Names()
sort.Strings(names)
sort.Strings(tt.kept)
if !reflect.DeepEqual(names, tt.kept) {
t.Errorf("package contains names %v, wanted %v", names, tt.kept)
}
})
}
}
func TestPkg(t *testing.T) {
t.Skip("for manual debugging")
fset := token.NewFileSet()
pkgs, err := packages.Load(&packages.Config{
Mode: packages.NeedSyntax | packages.NeedFiles,
Fset: fset,
}, "io")
if err != nil {
t.Fatal(err)
}
if len(pkgs[0].Errors) != 0 {
t.Fatal(pkgs[0].Errors)
}
filter := &unexportedFilter{uses: map[string]bool{}}
filter.Filter(pkgs[0].Syntax)
for _, file := range pkgs[0].Syntax {
buf := &bytes.Buffer{}
format.Node(buf, fset, file)
t.Log(buf.String())
}
}