| // 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()) |
| } |
| } |