1// Copyright 2019 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package cache 6 7import ( 8 "bytes" 9 "go/ast" 10 "go/format" 11 "go/parser" 12 "go/token" 13 "go/types" 14 "reflect" 15 "sort" 16 "testing" 17 18 "golang.org/x/tools/go/packages" 19) 20 21func TestArrayLength(t *testing.T) { 22 tests := []struct { 23 expr string 24 length int 25 }{ 26 {`[...]int{0,1,2,3,4,5,6,7,8,9}`, 10}, 27 {`[...]int{9:0}`, 10}, 28 {`[...]int{19-10:0}`, 10}, 29 {`[...]int{19-10:0, 17-10:0, 18-10:0}`, 10}, 30 } 31 32 for _, tt := range tests { 33 expr, err := parser.ParseExpr(tt.expr) 34 if err != nil { 35 t.Fatal(err) 36 } 37 l, ok := arrayLength(expr.(*ast.CompositeLit)) 38 if !ok { 39 t.Errorf("arrayLength did not recognize expression %#v", expr) 40 } 41 if l != tt.length { 42 t.Errorf("arrayLength(%#v) = %v, want %v", expr, l, tt.length) 43 } 44 } 45} 46 47func TestTrim(t *testing.T) { 48 tests := []struct { 49 name string 50 file string 51 kept []string 52 }{ 53 { 54 name: "delete_unused", 55 file: ` 56type x struct{} 57func y() 58var z int 59`, 60 kept: []string{}, 61 }, 62 { 63 // From the common type in testing. 64 name: "unexported_embedded", 65 file: ` 66type x struct {} 67type Exported struct { x } 68`, 69 kept: []string{"Exported", "x"}, 70 }, 71 { 72 // From the d type in unicode. 73 name: "exported_field_unexported_type", 74 file: ` 75type x struct {} 76type Exported struct { 77 X x 78} 79`, 80 kept: []string{"Exported", "x"}, 81 }, 82 { 83 // From errNotExist in io/fs. 84 name: "exported_var_function_call", 85 file: ` 86func x() int { return 0 } 87var Exported = x() 88`, 89 kept: []string{"Exported", "x"}, 90 }, 91 { 92 // From DefaultServeMux in net/http. 93 name: "exported_pointer_to_unexported_var", 94 file: ` 95var Exported = &x 96var x int 97`, 98 kept: []string{"Exported", "x"}, 99 }, 100 { 101 // From DefaultWriter in goldmark/renderer/html. 102 name: "exported_pointer_to_composite_lit", 103 file: ` 104var Exported = &x{} 105type x struct{} 106`, 107 kept: []string{"Exported", "x"}, 108 }, 109 { 110 // From SelectDir in reflect. 111 name: "leave_constants", 112 file: ` 113type Enum int 114const ( 115 _ Enum = iota 116 EnumOne 117) 118`, 119 kept: []string{"Enum", "EnumOne"}, 120 }, 121 { 122 name: "constant_conversion", 123 file: ` 124type x int 125const ( 126 foo x = 0 127) 128`, 129 kept: []string{"x", "foo"}, 130 }, 131 { 132 name: "unexported_return", 133 file: ` 134type x int 135func Exported() x {} 136type y int 137type Interface interface { 138 Exported() y 139} 140`, 141 kept: []string{"Exported", "Interface", "x", "y"}, 142 }, 143 { 144 name: "drop_composite_literals", 145 file: ` 146type x int 147type Exported struct { 148 foo x 149} 150var Var = Exported{foo:1} 151`, 152 kept: []string{"Exported", "Var"}, 153 }, 154 { 155 name: "drop_function_literals", 156 file: ` 157type x int 158var Exported = func() { return x(0) } 159`, 160 kept: []string{"Exported"}, 161 }, 162 { 163 name: "missing_receiver_panic", 164 file: ` 165 func() foo() {} 166`, 167 kept: []string{}, 168 }, 169 } 170 171 for _, tt := range tests { 172 t.Run(tt.name, func(t *testing.T) { 173 fset := token.NewFileSet() 174 file, err := parser.ParseFile(fset, "main.go", "package main\n\n"+tt.file, parser.AllErrors) 175 if err != nil { 176 t.Fatal(err) 177 } 178 filter := &unexportedFilter{uses: map[string]bool{}} 179 filter.Filter([]*ast.File{file}) 180 pkg := types.NewPackage("main", "main") 181 checker := types.NewChecker(&types.Config{ 182 DisableUnusedImportCheck: true, 183 }, fset, pkg, nil) 184 if err := checker.Files([]*ast.File{file}); err != nil { 185 t.Error(err) 186 } 187 names := pkg.Scope().Names() 188 sort.Strings(names) 189 sort.Strings(tt.kept) 190 if !reflect.DeepEqual(names, tt.kept) { 191 t.Errorf("package contains names %v, wanted %v", names, tt.kept) 192 } 193 }) 194 } 195} 196 197func TestPkg(t *testing.T) { 198 t.Skip("for manual debugging") 199 fset := token.NewFileSet() 200 pkgs, err := packages.Load(&packages.Config{ 201 Mode: packages.NeedSyntax | packages.NeedFiles, 202 Fset: fset, 203 }, "io") 204 if err != nil { 205 t.Fatal(err) 206 } 207 if len(pkgs[0].Errors) != 0 { 208 t.Fatal(pkgs[0].Errors) 209 } 210 filter := &unexportedFilter{uses: map[string]bool{}} 211 filter.Filter(pkgs[0].Syntax) 212 for _, file := range pkgs[0].Syntax { 213 buf := &bytes.Buffer{} 214 format.Node(buf, fset, file) 215 t.Log(buf.String()) 216 } 217} 218