github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/unused/unused.go (about) 1 // Package unused contains code for finding unused code. 2 package unused 3 4 import ( 5 "fmt" 6 "go/ast" 7 "go/token" 8 "go/types" 9 "io" 10 "reflect" 11 "strings" 12 13 "github.com/amarpal/go-tools/analysis/facts/directives" 14 "github.com/amarpal/go-tools/analysis/facts/generated" 15 "github.com/amarpal/go-tools/analysis/lint" 16 "github.com/amarpal/go-tools/analysis/report" 17 "github.com/amarpal/go-tools/go/ast/astutil" 18 "github.com/amarpal/go-tools/go/types/typeutil" 19 20 "golang.org/x/tools/go/analysis" 21 "golang.org/x/tools/go/types/objectpath" 22 ) 23 24 // OPT(dh): don't track local variables that can't have any interesting outgoing edges. For example, using a local 25 // variable of type int is meaningless; we don't care if `int` is used or not. 26 // 27 // Note that we do have to track variables with for example array types, because the array type could have involved a 28 // named constant. 29 // 30 // We probably have different culling needs depending on the mode of operation, too. If we analyze multiple packages in 31 // one graph (unused's "whole program" mode), we could remove further useless edges (e.g. into nodes that themselves 32 // have no outgoing edges and aren't meaningful objects on their own) after having analyzed a package, to keep the 33 // in-memory representation small on average. If we only analyze a single package, that step would just waste cycles, as 34 // we're about to throw the entire graph away, anyway. 35 36 // TODO(dh): currently, types use methods that implement interfaces. However, this makes a method used even if the 37 // relevant interface is never used. What if instead interfaces used those methods? Right now we cannot do that, because 38 // methods use their receivers, so using a method uses the type. But do we need that edge? Is there a way to refer to a 39 // method without explicitly mentioning the type somewhere? If not, the edge from method to receiver is superfluous. 40 41 // XXX vet all code for proper use of core types 42 43 // TODO(dh): we cannot observe function calls in assembly files. 44 45 /* 46 47 This overview is true when using the default options. Different options may change individual behaviors. 48 49 - packages use: 50 - (1.1) exported named types 51 - (1.2) exported functions (but not methods!) 52 - (1.3) exported variables 53 - (1.4) exported constants 54 - (1.5) init functions 55 - (1.6) functions exported to cgo 56 - (1.7) the main function iff in the main package 57 - (1.8) symbols linked via go:linkname 58 - (1.9) objects in generated files 59 60 - named types use: 61 - (2.1) exported methods 62 - (2.2) the type they're based on 63 - (2.5) all their type parameters. Unused type parameters are probably useless, but they're a brand new feature and we 64 don't want to introduce false positives because we couldn't anticipate some novel use-case. 65 - (2.6) all their type arguments 66 67 - functions use: 68 - (4.1) all their arguments, return parameters and receivers 69 - (4.2) anonymous functions defined beneath them 70 - (4.3) closures and bound methods. 71 this implements a simplified model where a function is used merely by being referenced, even if it is never called. 72 that way we don't have to keep track of closures escaping functions. 73 - (4.4) functions they return. we assume that someone else will call the returned function 74 - (4.5) functions/interface methods they call 75 - (4.6) types they instantiate or convert to 76 - (4.7) fields they access 77 - (4.9) package-level variables they assign to iff in tests (sinks for benchmarks) 78 - (4.10) all their type parameters. See 2.5 for reasoning. 79 - (4.11) local variables 80 - Note that the majority of this is handled implicitly by seeing idents be used. In particular, unlike the old 81 IR-based implementation, the AST-based one doesn't care about closures, bound methods or anonymous functions. 82 They're all just additional nodes in the AST. 83 84 - conversions use: 85 - (5.1) when converting between two equivalent structs, the fields in 86 either struct use each other. the fields are relevant for the 87 conversion, but only if the fields are also accessed outside the 88 conversion. 89 - (5.2) when converting to or from unsafe.Pointer, mark all fields as used. 90 91 - structs use: 92 - (6.1) fields of type NoCopy sentinel 93 - (6.2) exported fields 94 - (6.3) embedded fields that help implement interfaces (either fully implements it, or contributes required methods) (recursively) 95 - (6.4) embedded fields that have exported methods (recursively) 96 - (6.5) embedded structs that have exported fields (recursively) 97 98 - (7.1) field accesses use fields 99 - (7.2) fields use their types 100 101 - (8.0) How we handle interfaces: 102 - (8.1) We do not technically care about interfaces that only consist of 103 exported methods. Exported methods on concrete types are always 104 marked as used. 105 - (8.2) Any concrete type implements all known interfaces. Even if it isn't 106 assigned to any interfaces in our code, the user may receive a value 107 of the type and expect to pass it back to us through an interface. 108 109 Concrete types use their methods that implement interfaces. If the 110 type is used, it uses those methods. Otherwise, it doesn't. This 111 way, types aren't incorrectly marked reachable through the edge 112 from method to type. 113 114 - (8.3) All interface methods are marked as used, even if they never get 115 called. This is to accommodate sum types (unexported interface 116 method that must exist but never gets called.) 117 118 - (8.4) All embedded interfaces are marked as used. This is an 119 extension of 8.3, but we have to explicitly track embedded 120 interfaces because in a chain C->B->A, B wouldn't be marked as 121 used by 8.3 just because it contributes A's methods to C. 122 123 - Inherent uses: 124 - (9.2) variables use their types 125 - (9.3) types use their underlying and element types 126 - (9.4) conversions use the type they convert to 127 - (9.7) variable _reads_ use variables, writes do not, except in tests 128 - (9.8) runtime functions that may be called from user code via the compiler 129 - (9.9) objects named the blank identifier are used. They cannot be referred to and are usually used explicitly to 130 use something that would otherwise be unused. 131 - The majority of idents get marked as read by virtue of being in the AST. 132 133 - const groups: 134 - (10.1) if one constant out of a block of constants is used, mark all 135 of them used. a lot of the time, unused constants exist for the sake 136 of completeness. See also 137 https://github.com/dominikh/go-tools/issues/365 138 139 Do not, however, include constants named _ in constant groups. 140 141 142 - (11.1) anonymous struct types use all their fields. we cannot 143 deduplicate struct types, as that leads to order-dependent 144 reports. we can't not deduplicate struct types while still 145 tracking fields, because then each instance of the unnamed type in 146 the data flow chain will get its own fields, causing false 147 positives. Thus, we only accurately track fields of named struct 148 types, and assume that unnamed struct types use all their fields. 149 150 - type parameters use: 151 - (12.1) their constraint type 152 153 */ 154 155 var Debug io.Writer 156 157 func assert(b bool) { 158 if !b { 159 panic("failed assertion") 160 } 161 } 162 163 // TODO(dh): should we return a map instead of two slices? 164 type Result struct { 165 Used []Object 166 Unused []Object 167 Quiet []Object 168 } 169 170 var Analyzer = &lint.Analyzer{ 171 Doc: &lint.Documentation{ 172 Title: "Unused code", 173 }, 174 Analyzer: &analysis.Analyzer{ 175 Name: "U1000", 176 Doc: "Unused code", 177 Run: run, 178 Requires: []*analysis.Analyzer{generated.Analyzer, directives.Analyzer}, 179 ResultType: reflect.TypeOf(Result{}), 180 }, 181 } 182 183 func newGraph( 184 fset *token.FileSet, 185 files []*ast.File, 186 pkg *types.Package, 187 info *types.Info, 188 directives []lint.Directive, 189 generated map[string]generated.Generator, 190 opts Options, 191 ) *graph { 192 g := graph{ 193 pkg: pkg, 194 info: info, 195 files: files, 196 directives: directives, 197 generated: generated, 198 fset: fset, 199 nodes: []Node{{}}, 200 edges: map[edge]struct{}{}, 201 objects: map[types.Object]NodeID{}, 202 opts: opts, 203 } 204 205 return &g 206 } 207 208 func run(pass *analysis.Pass) (interface{}, error) { 209 g := newGraph( 210 pass.Fset, 211 pass.Files, 212 pass.Pkg, 213 pass.TypesInfo, 214 pass.ResultOf[directives.Analyzer].([]lint.Directive), 215 pass.ResultOf[generated.Analyzer].(map[string]generated.Generator), 216 DefaultOptions, 217 ) 218 g.entry() 219 220 sg := &SerializedGraph{ 221 nodes: g.nodes, 222 } 223 224 if Debug != nil { 225 Debug.Write([]byte(sg.Dot())) 226 } 227 228 return sg.Results(), nil 229 } 230 231 type Options struct { 232 FieldWritesAreUses bool 233 PostStatementsAreReads bool 234 ExportedIsUsed bool 235 ExportedFieldsAreUsed bool 236 ParametersAreUsed bool 237 LocalVariablesAreUsed bool 238 GeneratedIsUsed bool 239 } 240 241 var DefaultOptions = Options{ 242 FieldWritesAreUses: true, 243 PostStatementsAreReads: false, 244 ExportedIsUsed: true, 245 ExportedFieldsAreUsed: true, 246 ParametersAreUsed: true, 247 LocalVariablesAreUsed: true, 248 GeneratedIsUsed: true, 249 } 250 251 type edgeKind uint8 252 253 const ( 254 edgeKindUse = iota + 1 255 edgeKindOwn 256 ) 257 258 type edge struct { 259 from, to NodeID 260 kind edgeKind 261 } 262 263 type graph struct { 264 pkg *types.Package 265 info *types.Info 266 files []*ast.File 267 fset *token.FileSet 268 directives []lint.Directive 269 generated map[string]generated.Generator 270 271 opts Options 272 273 // edges tracks all edges between nodes (uses and owns relationships). This data is also present in the Node struct, 274 // but there it can't be accessed in O(1) time. edges is used to deduplicate edges. 275 edges map[edge]struct{} 276 nodes []Node 277 objects map[types.Object]NodeID 278 279 // package-level named types 280 namedTypes []*types.TypeName 281 interfaceTypes []*types.Interface 282 } 283 284 type nodeState uint8 285 286 //gcassert:inline 287 func (ns nodeState) seen() bool { return ns&nodeStateSeen != 0 } 288 289 //gcassert:inline 290 func (ns nodeState) quiet() bool { return ns&nodeStateQuiet != 0 } 291 292 const ( 293 nodeStateSeen nodeState = 1 << iota 294 nodeStateQuiet 295 ) 296 297 // OPT(dh): 32 bits would be plenty, but the Node struct would end up with padding, anyway. 298 type NodeID uint64 299 300 type Node struct { 301 id NodeID 302 obj Object 303 304 // using slices instead of maps here helps make merging of graphs simpler and more efficient, because we can rewrite 305 // IDs in place instead of having to build new maps. 306 uses []NodeID 307 owns []NodeID 308 } 309 310 func (g *graph) objectToObject(obj types.Object) Object { 311 // OPT(dh): I think we only need object paths in whole-program mode. In other cases, position-based node merging 312 // should suffice. 313 314 // objectpath.For is an expensive function and we'd like to avoid calling it when we know that there cannot be a 315 // path, or when the path doesn't matter. 316 // 317 // Unexported global objects don't have paths. Local variables may have paths when they're parameters or return 318 // parameters, but we do not care about those, because they're not API that other packages can refer to directly. We 319 // do have to track fields, because they may be part of an anonymous type declared in a parameter or return 320 // parameter. We cannot categorically ignore unexported identifiers, because an exported field might have been 321 // embedded via an unexported field, which will be referred to. 322 323 var relevant bool 324 switch obj := obj.(type) { 325 case *types.Var: 326 // If it's a field or it's an exported top-level variable, we care about it. Otherwise, we don't. 327 // OPT(dh): same question as posed in the default branch 328 relevant = obj.IsField() || token.IsExported(obj.Name()) 329 default: 330 // OPT(dh): See if it's worth checking that the object is actually in package scope, and doesn't just have a 331 // capitalized name. 332 relevant = token.IsExported(obj.Name()) 333 } 334 335 var path ObjectPath 336 if relevant { 337 objPath, _ := objectpath.For(obj) 338 if objPath != "" { 339 path = ObjectPath{ 340 PkgPath: obj.Pkg().Path(), 341 ObjPath: objPath, 342 } 343 } 344 } 345 name := obj.Name() 346 if sig, ok := obj.Type().(*types.Signature); ok && sig.Recv() != nil { 347 switch sig.Recv().Type().(type) { 348 case *types.Named, *types.Pointer: 349 typ := types.TypeString(sig.Recv().Type(), func(*types.Package) string { return "" }) 350 if len(typ) > 0 && typ[0] == '*' { 351 name = fmt.Sprintf("(%s).%s", typ, obj.Name()) 352 } else if len(typ) > 0 { 353 name = fmt.Sprintf("%s.%s", typ, obj.Name()) 354 } 355 } 356 } 357 return Object{ 358 Name: name, 359 ShortName: obj.Name(), 360 Kind: typString(obj), 361 Path: path, 362 Position: g.fset.PositionFor(obj.Pos(), false), 363 DisplayPosition: report.DisplayPosition(g.fset, obj.Pos()), 364 } 365 } 366 367 func typString(obj types.Object) string { 368 switch obj := obj.(type) { 369 case *types.Func: 370 return "func" 371 case *types.Var: 372 if obj.IsField() { 373 return "field" 374 } 375 return "var" 376 case *types.Const: 377 return "const" 378 case *types.TypeName: 379 if _, ok := obj.Type().(*types.TypeParam); ok { 380 return "type param" 381 } else { 382 return "type" 383 } 384 default: 385 return "identifier" 386 } 387 } 388 389 func (g *graph) newNode(obj types.Object) NodeID { 390 id := NodeID(len(g.nodes)) 391 n := Node{ 392 id: id, 393 obj: g.objectToObject(obj), 394 } 395 g.nodes = append(g.nodes, n) 396 if _, ok := g.objects[obj]; ok { 397 panic(fmt.Sprintf("already had a node for %s", obj)) 398 } 399 g.objects[obj] = id 400 return id 401 } 402 403 func (g *graph) node(obj types.Object) NodeID { 404 if obj == nil { 405 return 0 406 } 407 obj = origin(obj) 408 if n, ok := g.objects[obj]; ok { 409 return n 410 } 411 n := g.newNode(obj) 412 return n 413 } 414 415 func origin(obj types.Object) types.Object { 416 switch obj := obj.(type) { 417 case *types.Var: 418 return obj.Origin() 419 case *types.Func: 420 return obj.Origin() 421 default: 422 return obj 423 } 424 } 425 426 func (g *graph) addEdge(e edge) bool { 427 if _, ok := g.edges[e]; ok { 428 return false 429 } 430 g.edges[e] = struct{}{} 431 return true 432 } 433 434 func (g *graph) addOwned(owner, owned NodeID) { 435 e := edge{owner, owned, edgeKindOwn} 436 if !g.addEdge(e) { 437 return 438 } 439 n := &g.nodes[owner] 440 n.owns = append(n.owns, owned) 441 } 442 443 func (g *graph) addUse(by, used NodeID) { 444 e := edge{by, used, edgeKindUse} 445 if !g.addEdge(e) { 446 return 447 } 448 nBy := &g.nodes[by] 449 nBy.uses = append(nBy.uses, used) 450 } 451 452 func (g *graph) see(obj, owner types.Object) { 453 if obj == nil { 454 panic("saw nil object") 455 } 456 457 if g.opts.ExportedIsUsed && obj.Pkg() != g.pkg || obj.Pkg() == nil { 458 return 459 } 460 461 nObj := g.node(obj) 462 if owner != nil { 463 nOwner := g.node(owner) 464 g.addOwned(nOwner, nObj) 465 } 466 } 467 468 func isIrrelevant(obj types.Object) bool { 469 switch obj.(type) { 470 case *types.PkgName: 471 return true 472 default: 473 return false 474 } 475 } 476 477 func (g *graph) use(used, by types.Object) { 478 if g.opts.ExportedIsUsed { 479 if used.Pkg() != g.pkg || used.Pkg() == nil { 480 return 481 } 482 if by != nil && by.Pkg() != g.pkg { 483 return 484 } 485 } 486 487 if isIrrelevant(used) { 488 return 489 } 490 491 nUsed := g.node(used) 492 nBy := g.node(by) 493 g.addUse(nBy, nUsed) 494 } 495 496 func (g *graph) entry() { 497 for _, f := range g.files { 498 for _, cg := range f.Comments { 499 for _, c := range cg.List { 500 if strings.HasPrefix(c.Text, "//go:linkname ") { 501 // FIXME(dh): we're looking at all comments. The 502 // compiler only looks at comments in the 503 // left-most column. The intention probably is to 504 // only look at top-level comments. 505 506 // (1.8) packages use symbols linked via go:linkname 507 fields := strings.Fields(c.Text) 508 if len(fields) == 3 { 509 obj := g.pkg.Scope().Lookup(fields[1]) 510 if obj == nil { 511 continue 512 } 513 g.use(obj, nil) 514 } 515 } 516 } 517 } 518 } 519 520 for _, f := range g.files { 521 for _, decl := range f.Decls { 522 g.decl(decl, nil) 523 } 524 } 525 526 if g.opts.GeneratedIsUsed { 527 // OPT(dh): depending on the options used, we do not need to track all objects. For example, if local variables 528 // are always used, then it is enough to use their surrounding function. 529 for obj := range g.objects { 530 path := g.fset.PositionFor(obj.Pos(), false).Filename 531 if _, ok := g.generated[path]; ok { 532 g.use(obj, nil) 533 } 534 } 535 } 536 537 processMethodSet := func(named *types.TypeName, ms *types.MethodSet) { 538 if g.opts.ExportedIsUsed { 539 for i := 0; i < ms.Len(); i++ { 540 m := ms.At(i) 541 if token.IsExported(m.Obj().Name()) { 542 // (2.1) named types use exported methods 543 // (6.4) structs use embedded fields that have exported methods 544 // 545 // By reading the selection, we read all embedded fields that are part of the path 546 g.readSelection(m, named) 547 } 548 } 549 } 550 551 if _, ok := named.Type().Underlying().(*types.Interface); !ok { 552 // (8.0) handle interfaces 553 // 554 // We don't care about interfaces implementing interfaces; all their methods are already used, anyway 555 for _, iface := range g.interfaceTypes { 556 if sels, ok := implements(named.Type(), iface, ms); ok { 557 for _, sel := range sels { 558 // (8.2) any concrete type implements all known interfaces 559 // (6.3) structs use embedded fields that help implement interfaces 560 g.readSelection(sel, named) 561 } 562 } 563 } 564 } 565 } 566 567 for _, named := range g.namedTypes { 568 // OPT(dh): do we already have the method set available? 569 processMethodSet(named, types.NewMethodSet(named.Type())) 570 processMethodSet(named, types.NewMethodSet(types.NewPointer(named.Type()))) 571 572 } 573 574 type ignoredKey struct { 575 file string 576 line int 577 } 578 ignores := map[ignoredKey]struct{}{} 579 for _, dir := range g.directives { 580 if dir.Command != "ignore" && dir.Command != "file-ignore" { 581 continue 582 } 583 if len(dir.Arguments) == 0 { 584 continue 585 } 586 for _, check := range strings.Split(dir.Arguments[0], ",") { 587 if check == "U1000" { 588 pos := g.fset.PositionFor(dir.Node.Pos(), false) 589 var key ignoredKey 590 switch dir.Command { 591 case "ignore": 592 key = ignoredKey{ 593 pos.Filename, 594 pos.Line, 595 } 596 case "file-ignore": 597 key = ignoredKey{ 598 pos.Filename, 599 -1, 600 } 601 } 602 603 ignores[key] = struct{}{} 604 break 605 } 606 } 607 } 608 609 if len(ignores) > 0 { 610 // all objects annotated with a //lint:ignore U1000 are considered used 611 for obj := range g.objects { 612 pos := g.fset.PositionFor(obj.Pos(), false) 613 key1 := ignoredKey{ 614 pos.Filename, 615 pos.Line, 616 } 617 key2 := ignoredKey{ 618 pos.Filename, 619 -1, 620 } 621 _, ok := ignores[key1] 622 if !ok { 623 _, ok = ignores[key2] 624 } 625 if ok { 626 g.use(obj, nil) 627 628 // use methods and fields of ignored types 629 if obj, ok := obj.(*types.TypeName); ok { 630 if obj.IsAlias() { 631 if typ, ok := obj.Type().(*types.Named); ok && (g.opts.ExportedIsUsed && typ.Obj().Pkg() != obj.Pkg() || typ.Obj().Pkg() == nil) { 632 // This is an alias of a named type in another package. 633 // Don't walk its fields or methods; we don't have to. 634 // 635 // For aliases to types in the same package, we do want to ignore the fields and methods, 636 // because ignoring the alias should ignore the aliased type. 637 continue 638 } 639 } 640 if typ, ok := obj.Type().(*types.Named); ok { 641 for i := 0; i < typ.NumMethods(); i++ { 642 g.use(typ.Method(i), nil) 643 } 644 } 645 if typ, ok := obj.Type().Underlying().(*types.Struct); ok { 646 for i := 0; i < typ.NumFields(); i++ { 647 g.use(typ.Field(i), nil) 648 } 649 } 650 } 651 } 652 } 653 } 654 } 655 656 func isOfType[T any](x any) bool { 657 _, ok := x.(T) 658 return ok 659 } 660 661 func (g *graph) read(node ast.Node, by types.Object) { 662 if node == nil { 663 return 664 } 665 666 switch node := node.(type) { 667 case *ast.Ident: 668 // Among many other things, this handles 669 // (7.1) field accesses use fields 670 671 obj := g.info.ObjectOf(node) 672 g.use(obj, by) 673 674 case *ast.BasicLit: 675 // Nothing to do 676 677 case *ast.SliceExpr: 678 g.read(node.X, by) 679 g.read(node.Low, by) 680 g.read(node.High, by) 681 g.read(node.Max, by) 682 683 case *ast.UnaryExpr: 684 g.read(node.X, by) 685 686 case *ast.ParenExpr: 687 g.read(node.X, by) 688 689 case *ast.ArrayType: 690 g.read(node.Len, by) 691 g.read(node.Elt, by) 692 693 case *ast.SelectorExpr: 694 g.readSelectorExpr(node, by) 695 696 case *ast.IndexExpr: 697 // Among many other things, this handles 698 // (2.6) named types use all their type arguments 699 g.read(node.X, by) 700 g.read(node.Index, by) 701 702 case *ast.IndexListExpr: 703 // Among many other things, this handles 704 // (2.6) named types use all their type arguments 705 g.read(node.X, by) 706 for _, index := range node.Indices { 707 g.read(index, by) 708 } 709 710 case *ast.BinaryExpr: 711 g.read(node.X, by) 712 g.read(node.Y, by) 713 714 case *ast.CompositeLit: 715 g.read(node.Type, by) 716 // We get the type of the node itself, not of node.Type, to handle nested composite literals of the kind 717 // T{{...}} 718 typ, isStruct := typeutil.CoreType(g.info.TypeOf(node)).(*types.Struct) 719 720 if isStruct { 721 unkeyed := len(node.Elts) != 0 && !isOfType[*ast.KeyValueExpr](node.Elts[0]) 722 if g.opts.FieldWritesAreUses && unkeyed { 723 // Untagged struct literal that specifies all fields. We have to manually use the fields in the type, 724 // because the unkeyd literal doesn't contain any nodes referring to the fields. 725 for i := 0; i < typ.NumFields(); i++ { 726 g.use(typ.Field(i), by) 727 } 728 } 729 if g.opts.FieldWritesAreUses || unkeyed { 730 for _, elt := range node.Elts { 731 g.read(elt, by) 732 } 733 } else { 734 for _, elt := range node.Elts { 735 kv := elt.(*ast.KeyValueExpr) 736 g.write(kv.Key, by) 737 g.read(kv.Value, by) 738 } 739 } 740 } else { 741 for _, elt := range node.Elts { 742 g.read(elt, by) 743 } 744 } 745 746 case *ast.KeyValueExpr: 747 g.read(node.Key, by) 748 g.read(node.Value, by) 749 750 case *ast.StarExpr: 751 g.read(node.X, by) 752 753 case *ast.MapType: 754 g.read(node.Key, by) 755 g.read(node.Value, by) 756 757 case *ast.FuncLit: 758 g.read(node.Type, by) 759 760 // See graph.decl's handling of ast.FuncDecl for why this bit of code is necessary. 761 fn := g.info.TypeOf(node).(*types.Signature) 762 for params, i := fn.Params(), 0; i < params.Len(); i++ { 763 g.see(params.At(i), by) 764 if params.At(i).Name() == "" { 765 g.use(params.At(i), by) 766 } 767 } 768 769 g.block(node.Body, by) 770 771 case *ast.FuncType: 772 m := map[*types.Var]struct{}{} 773 if !g.opts.ParametersAreUsed { 774 m = map[*types.Var]struct{}{} 775 // seeScope marks all local variables in the scope as used, but we don't want to unconditionally use 776 // parameters, as this is controlled by Options.ParametersAreUsed. Pass seeScope a list of variables it 777 // should skip. 778 for _, f := range node.Params.List { 779 for _, name := range f.Names { 780 m[g.info.ObjectOf(name).(*types.Var)] = struct{}{} 781 } 782 } 783 } 784 g.seeScope(node, by, m) 785 786 // (4.1) functions use all their arguments, return parameters and receivers 787 // (12.1) type parameters use their constraint type 788 g.read(node.TypeParams, by) 789 if g.opts.ParametersAreUsed { 790 g.read(node.Params, by) 791 } 792 g.read(node.Results, by) 793 794 case *ast.FieldList: 795 if node == nil { 796 return 797 } 798 799 // This branch is only hit for field lists enclosed by parentheses or square brackets, i.e. parameters. Fields 800 // (for structs) and method lists (for interfaces) are handled elsewhere. 801 802 for _, field := range node.List { 803 if len(field.Names) == 0 { 804 g.read(field.Type, by) 805 } else { 806 for _, name := range field.Names { 807 // OPT(dh): instead of by -> name -> type, we could just emit by -> type. We don't care about the 808 // (un)usedness of parameters of any kind. 809 obj := g.info.ObjectOf(name) 810 g.use(obj, by) 811 g.read(field.Type, obj) 812 } 813 } 814 } 815 816 case *ast.ChanType: 817 g.read(node.Value, by) 818 819 case *ast.StructType: 820 // This is only used for anonymous struct types, not named ones. 821 822 for _, field := range node.Fields.List { 823 if len(field.Names) == 0 { 824 // embedded field 825 826 f := g.embeddedField(field.Type, by) 827 g.use(f, by) 828 } else { 829 for _, name := range field.Names { 830 // (11.1) anonymous struct types use all their fields 831 // OPT(dh): instead of by -> name -> type, we could just emit by -> type. If the type is used, then the fields are used. 832 obj := g.info.ObjectOf(name) 833 g.see(obj, by) 834 g.use(obj, by) 835 g.read(field.Type, g.info.ObjectOf(name)) 836 } 837 } 838 } 839 840 case *ast.TypeAssertExpr: 841 g.read(node.X, by) 842 g.read(node.Type, by) 843 844 case *ast.InterfaceType: 845 if len(node.Methods.List) != 0 { 846 g.interfaceTypes = append(g.interfaceTypes, g.info.TypeOf(node).(*types.Interface)) 847 } 848 for _, meth := range node.Methods.List { 849 switch len(meth.Names) { 850 case 0: 851 // Embedded type or type union 852 // (8.4) all embedded interfaces are marked as used 853 // (this also covers type sets) 854 855 g.read(meth.Type, by) 856 case 1: 857 // Method 858 // (8.3) all interface methods are marked as used 859 obj := g.info.ObjectOf(meth.Names[0]) 860 g.see(obj, by) 861 g.use(obj, by) 862 g.read(meth.Type, obj) 863 default: 864 panic(fmt.Sprintf("unexpected number of names: %d", len(meth.Names))) 865 } 866 } 867 868 case *ast.Ellipsis: 869 g.read(node.Elt, by) 870 871 case *ast.CallExpr: 872 g.read(node.Fun, by) 873 for _, arg := range node.Args { 874 g.read(arg, by) 875 } 876 877 // Handle conversiosn 878 conv := node 879 if len(conv.Args) != 1 || conv.Ellipsis.IsValid() { 880 return 881 } 882 883 dst := g.info.TypeOf(conv.Fun) 884 src := g.info.TypeOf(conv.Args[0]) 885 886 // XXX use DereferenceR instead 887 // XXX guard against infinite recursion in DereferenceR 888 tSrc := typeutil.CoreType(typeutil.Dereference(src)) 889 tDst := typeutil.CoreType(typeutil.Dereference(dst)) 890 stSrc, okSrc := tSrc.(*types.Struct) 891 stDst, okDst := tDst.(*types.Struct) 892 if okDst && okSrc { 893 // Converting between two structs. The fields are 894 // relevant for the conversion, but only if the 895 // fields are also used outside of the conversion. 896 // Mark fields as used by each other. 897 898 assert(stDst.NumFields() == stSrc.NumFields()) 899 for i := 0; i < stDst.NumFields(); i++ { 900 // (5.1) when converting between two equivalent structs, the fields in 901 // either struct use each other. the fields are relevant for the 902 // conversion, but only if the fields are also accessed outside the 903 // conversion. 904 g.use(stDst.Field(i), stSrc.Field(i)) 905 g.use(stSrc.Field(i), stDst.Field(i)) 906 } 907 } else if okSrc && tDst == types.Typ[types.UnsafePointer] { 908 // (5.2) when converting to or from unsafe.Pointer, mark all fields as used. 909 g.useAllFieldsRecursively(stSrc, by) 910 } else if okDst && tSrc == types.Typ[types.UnsafePointer] { 911 // (5.2) when converting to or from unsafe.Pointer, mark all fields as used. 912 g.useAllFieldsRecursively(stDst, by) 913 } 914 915 default: 916 lint.ExhaustiveTypeSwitch(node) 917 } 918 } 919 920 func (g *graph) useAllFieldsRecursively(typ types.Type, by types.Object) { 921 switch typ := typ.Underlying().(type) { 922 case *types.Struct: 923 for i := 0; i < typ.NumFields(); i++ { 924 field := typ.Field(i) 925 g.use(field, by) 926 g.useAllFieldsRecursively(field.Type(), by) 927 } 928 case *types.Array: 929 g.useAllFieldsRecursively(typ.Elem(), by) 930 default: 931 return 932 } 933 } 934 935 func (g *graph) write(node ast.Node, by types.Object) { 936 if node == nil { 937 return 938 } 939 940 switch node := node.(type) { 941 case *ast.Ident: 942 obj := g.info.ObjectOf(node) 943 if obj == nil { 944 // This can happen for `switch x := v.(type)`, where that x doesn't have an object 945 return 946 } 947 948 // (4.9) functions use package-level variables they assign to iff in tests (sinks for benchmarks) 949 // (9.7) variable _reads_ use variables, writes do not, except in tests 950 path := g.fset.File(obj.Pos()).Name() 951 if strings.HasSuffix(path, "_test.go") { 952 if isGlobal(obj) { 953 g.use(obj, by) 954 } 955 } 956 957 case *ast.IndexExpr: 958 g.read(node.X, by) 959 g.read(node.Index, by) 960 961 case *ast.SelectorExpr: 962 if g.opts.FieldWritesAreUses { 963 // Writing to a field constitutes a use. See https://staticcheck.dev/issues/288 for some discussion on that. 964 // 965 // This code can also get triggered by qualified package variables, in which case it doesn't matter what we do, 966 // because the object is in another package. 967 // 968 // FIXME(dh): ^ isn't true if we track usedness of exported identifiers 969 g.readSelectorExpr(node, by) 970 } else { 971 g.read(node.X, by) 972 g.write(node.Sel, by) 973 } 974 975 case *ast.StarExpr: 976 g.read(node.X, by) 977 978 case *ast.ParenExpr: 979 g.write(node.X, by) 980 981 default: 982 lint.ExhaustiveTypeSwitch(node) 983 } 984 } 985 986 // readSelectorExpr reads all elements of a selector expression, including implicit fields. 987 func (g *graph) readSelectorExpr(sel *ast.SelectorExpr, by types.Object) { 988 // cover AST-based accesses 989 g.read(sel.X, by) 990 g.read(sel.Sel, by) 991 992 tsel, ok := g.info.Selections[sel] 993 if !ok { 994 return 995 } 996 g.readSelection(tsel, by) 997 } 998 999 func (g *graph) readSelection(sel *types.Selection, by types.Object) { 1000 indices := sel.Index() 1001 base := sel.Recv() 1002 for _, idx := range indices[:len(indices)-1] { 1003 // XXX do we need core types here? 1004 field := typeutil.Dereference(base.Underlying()).Underlying().(*types.Struct).Field(idx) 1005 g.use(field, by) 1006 base = field.Type() 1007 } 1008 1009 g.use(sel.Obj(), by) 1010 } 1011 1012 func (g *graph) block(block *ast.BlockStmt, by types.Object) { 1013 if block == nil { 1014 return 1015 } 1016 1017 g.seeScope(block, by, nil) 1018 for _, stmt := range block.List { 1019 g.stmt(stmt, by) 1020 } 1021 } 1022 1023 func isGlobal(obj types.Object) bool { 1024 return obj.Parent() == obj.Pkg().Scope() 1025 } 1026 1027 func (g *graph) decl(decl ast.Decl, by types.Object) { 1028 switch decl := decl.(type) { 1029 case *ast.GenDecl: 1030 switch decl.Tok { 1031 case token.IMPORT: 1032 // Nothing to do 1033 1034 case token.CONST: 1035 for _, spec := range decl.Specs { 1036 vspec := spec.(*ast.ValueSpec) 1037 assert(len(vspec.Values) == 0 || len(vspec.Values) == len(vspec.Names)) 1038 for i, name := range vspec.Names { 1039 obj := g.info.ObjectOf(name) 1040 g.see(obj, by) 1041 g.read(vspec.Type, obj) 1042 1043 if len(vspec.Values) != 0 { 1044 g.read(vspec.Values[i], obj) 1045 } 1046 1047 if name.Name == "_" { 1048 // (9.9) objects named the blank identifier are used 1049 g.use(obj, by) 1050 } else if token.IsExported(name.Name) && isGlobal(obj) && g.opts.ExportedIsUsed { 1051 g.use(obj, nil) 1052 } 1053 } 1054 } 1055 1056 groups := astutil.GroupSpecs(g.fset, decl.Specs) 1057 for _, group := range groups { 1058 // (10.1) if one constant out of a block of constants is used, mark all of them used 1059 // 1060 // We encode this as a ring. If we have a constant group 'const ( a; b; c )', then we'll produce the 1061 // following graph: a -> b -> c -> a. 1062 1063 var first, prev, last types.Object 1064 for _, spec := range group { 1065 for _, name := range spec.(*ast.ValueSpec).Names { 1066 if name.Name == "_" { 1067 // Having a blank constant in a group doesn't mark the whole group as used 1068 continue 1069 } 1070 1071 obj := g.info.ObjectOf(name) 1072 if first == nil { 1073 first = obj 1074 } else { 1075 g.use(obj, prev) 1076 } 1077 prev = obj 1078 last = obj 1079 } 1080 } 1081 if first != nil && first != last { 1082 g.use(first, last) 1083 } 1084 } 1085 1086 case token.TYPE: 1087 for _, spec := range decl.Specs { 1088 tspec := spec.(*ast.TypeSpec) 1089 obj := g.info.ObjectOf(tspec.Name).(*types.TypeName) 1090 g.see(obj, by) 1091 g.seeScope(tspec, obj, nil) 1092 if !tspec.Assign.IsValid() { 1093 g.namedTypes = append(g.namedTypes, obj) 1094 } 1095 if token.IsExported(tspec.Name.Name) && isGlobal(obj) && g.opts.ExportedIsUsed { 1096 // (1.1) packages use exported named types 1097 g.use(g.info.ObjectOf(tspec.Name), nil) 1098 } 1099 1100 // (2.5) named types use all their type parameters 1101 g.read(tspec.TypeParams, obj) 1102 1103 g.namedType(obj, tspec.Type) 1104 1105 if tspec.Name.Name == "_" { 1106 // (9.9) objects named the blank identifier are used 1107 g.use(obj, by) 1108 } 1109 } 1110 1111 case token.VAR: 1112 // We cannot rely on types.Initializer for package-level variables because 1113 // - initializers are only tracked for variables that are actually initialized 1114 // - we want to see the AST of the type, if specified, not just the rhs 1115 1116 for _, spec := range decl.Specs { 1117 vspec := spec.(*ast.ValueSpec) 1118 for i, name := range vspec.Names { 1119 obj := g.info.ObjectOf(name) 1120 g.see(obj, by) 1121 // variables and constants use their types 1122 g.read(vspec.Type, obj) 1123 1124 if len(vspec.Names) == len(vspec.Values) { 1125 // One value per variable 1126 g.read(vspec.Values[i], obj) 1127 } else if len(vspec.Values) != 0 { 1128 // Multiple variables initialized with a single rhs 1129 // assert(len(vspec.Values) == 1) 1130 if len(vspec.Values) != 1 { 1131 panic(g.fset.PositionFor(vspec.Pos(), false)) 1132 } 1133 g.read(vspec.Values[0], obj) 1134 } 1135 1136 if token.IsExported(name.Name) && isGlobal(obj) && g.opts.ExportedIsUsed { 1137 // (1.3) packages use exported variables 1138 g.use(obj, nil) 1139 } 1140 1141 if name.Name == "_" { 1142 // (9.9) objects named the blank identifier are used 1143 g.use(obj, by) 1144 } 1145 } 1146 } 1147 1148 default: 1149 panic(fmt.Sprintf("unexpected token %s", decl.Tok)) 1150 } 1151 1152 case *ast.FuncDecl: 1153 obj := g.info.ObjectOf(decl.Name).(*types.Func).Origin() 1154 g.see(obj, nil) 1155 1156 if token.IsExported(decl.Name.Name) && g.opts.ExportedIsUsed { 1157 if decl.Recv == nil { 1158 // (1.2) packages use exported functions 1159 g.use(obj, nil) 1160 } 1161 } else if decl.Name.Name == "init" { 1162 // (1.5) packages use init functions 1163 g.use(obj, nil) 1164 } else if decl.Name.Name == "main" && g.pkg.Name() == "main" { 1165 // (1.7) packages use the main function iff in the main package 1166 g.use(obj, nil) 1167 } else if g.pkg.Path() == "runtime" && runtimeFuncs[decl.Name.Name] { 1168 // (9.8) runtime functions that may be called from user code via the compiler 1169 g.use(obj, nil) 1170 } else if g.pkg.Path() == "runtime/coverage" && runtimeCoverageFuncs[decl.Name.Name] { 1171 // (9.8) runtime functions that may be called from user code via the compiler 1172 g.use(obj, nil) 1173 } 1174 1175 // (4.1) functions use their receivers 1176 g.read(decl.Recv, obj) 1177 g.read(decl.Type, obj) 1178 g.block(decl.Body, obj) 1179 1180 // g.read(decl.Type) will ultimately call g.seeScopes and see parameters that way. But because it relies 1181 // entirely on the AST, it cannot resolve unnamed parameters to types.Object. For that reason we explicitly 1182 // handle arguments here, as well as for FuncLits elsewhere. 1183 // 1184 // g.seeScopes can't get to the types.Signature for this function because there is no mapping from ast.FuncType to 1185 // types.Signature, only from ast.Ident to types.Signature. 1186 // 1187 // This code is only really relevant when Options.ParametersAreUsed is false. Otherwise, all parameters are 1188 // considered used, and if we never see a parameter then no harm done (we still see its type separately). 1189 fn := g.info.TypeOf(decl.Name).(*types.Signature) 1190 for params, i := fn.Params(), 0; i < params.Len(); i++ { 1191 g.see(params.At(i), obj) 1192 if params.At(i).Name() == "" { 1193 g.use(params.At(i), obj) 1194 } 1195 } 1196 1197 if decl.Name.Name == "_" { 1198 // (9.9) objects named the blank identifier are used 1199 g.use(obj, nil) 1200 } 1201 1202 if decl.Doc != nil { 1203 for _, cmt := range decl.Doc.List { 1204 if strings.HasPrefix(cmt.Text, "//go:cgo_export_") { 1205 // (1.6) packages use functions exported to cgo 1206 g.use(obj, nil) 1207 } 1208 } 1209 } 1210 1211 default: 1212 // We do not cover BadDecl, but we shouldn't ever see one of those 1213 lint.ExhaustiveTypeSwitch(decl) 1214 } 1215 } 1216 1217 // seeScope sees all objects in node's scope. If Options.LocalVariablesAreUsed is true, all objects that aren't fields 1218 // are marked as used. Variables set in skipLvars will not be marked as used. 1219 func (g *graph) seeScope(node ast.Node, by types.Object, skipLvars map[*types.Var]struct{}) { 1220 // A note on functions and scopes: for a function declaration, the body's BlockStmt can't be found in 1221 // types.Info.Scopes. Instead, the FuncType can, and that scope will contain receivers, parameters, return 1222 // parameters and immediate local variables. 1223 1224 scope := g.info.Scopes[node] 1225 if scope == nil { 1226 return 1227 } 1228 for _, name := range scope.Names() { 1229 obj := scope.Lookup(name) 1230 g.see(obj, by) 1231 1232 if g.opts.LocalVariablesAreUsed { 1233 if obj, ok := obj.(*types.Var); ok && !obj.IsField() { 1234 if _, ok := skipLvars[obj]; !ok { 1235 g.use(obj, by) 1236 } 1237 } 1238 } 1239 } 1240 } 1241 1242 func (g *graph) stmt(stmt ast.Stmt, by types.Object) { 1243 if stmt == nil { 1244 return 1245 } 1246 1247 for { 1248 // We don't care about labels, so unwrap LabeledStmts. Note that a label can itself be labeled. 1249 if labeled, ok := stmt.(*ast.LabeledStmt); ok { 1250 stmt = labeled.Stmt 1251 } else { 1252 break 1253 } 1254 } 1255 1256 switch stmt := stmt.(type) { 1257 case *ast.AssignStmt: 1258 for _, lhs := range stmt.Lhs { 1259 g.write(lhs, by) 1260 } 1261 for _, rhs := range stmt.Rhs { 1262 // Note: it would be more accurate to have the rhs used by the lhs, but it ultimately doesn't matter, 1263 // because local variables always end up used, anyway. 1264 // 1265 // TODO(dh): we'll have to change that once we allow tracking the usedness of parameters 1266 g.read(rhs, by) 1267 } 1268 1269 case *ast.BlockStmt: 1270 g.block(stmt, by) 1271 1272 case *ast.BranchStmt: 1273 // Nothing to do 1274 1275 case *ast.DeclStmt: 1276 g.decl(stmt.Decl, by) 1277 1278 case *ast.DeferStmt: 1279 g.read(stmt.Call, by) 1280 1281 case *ast.ExprStmt: 1282 g.read(stmt.X, by) 1283 1284 case *ast.ForStmt: 1285 g.seeScope(stmt, by, nil) 1286 g.stmt(stmt.Init, by) 1287 g.read(stmt.Cond, by) 1288 g.stmt(stmt.Post, by) 1289 g.block(stmt.Body, by) 1290 1291 case *ast.GoStmt: 1292 g.read(stmt.Call, by) 1293 1294 case *ast.IfStmt: 1295 g.seeScope(stmt, by, nil) 1296 g.stmt(stmt.Init, by) 1297 g.read(stmt.Cond, by) 1298 g.block(stmt.Body, by) 1299 g.stmt(stmt.Else, by) 1300 1301 case *ast.IncDecStmt: 1302 if g.opts.PostStatementsAreReads { 1303 g.read(stmt.X, by) 1304 g.write(stmt.X, by) 1305 } else { 1306 // We treat post-increment as a write only. This ends up using fields, and sinks in tests, but not other 1307 // variables. 1308 g.write(stmt.X, by) 1309 } 1310 1311 case *ast.RangeStmt: 1312 g.seeScope(stmt, by, nil) 1313 1314 g.write(stmt.Key, by) 1315 g.write(stmt.Value, by) 1316 g.read(stmt.X, by) 1317 g.block(stmt.Body, by) 1318 1319 case *ast.ReturnStmt: 1320 for _, ret := range stmt.Results { 1321 g.read(ret, by) 1322 } 1323 1324 case *ast.SelectStmt: 1325 for _, clause_ := range stmt.Body.List { 1326 clause := clause_.(*ast.CommClause) 1327 g.seeScope(clause, by, nil) 1328 switch comm := clause.Comm.(type) { 1329 case *ast.SendStmt: 1330 g.read(comm.Chan, by) 1331 g.read(comm.Value, by) 1332 case *ast.ExprStmt: 1333 g.read(astutil.Unparen(comm.X).(*ast.UnaryExpr).X, by) 1334 case *ast.AssignStmt: 1335 for _, lhs := range comm.Lhs { 1336 g.write(lhs, by) 1337 } 1338 for _, rhs := range comm.Rhs { 1339 g.read(rhs, by) 1340 } 1341 case nil: 1342 default: 1343 lint.ExhaustiveTypeSwitch(comm) 1344 } 1345 for _, body := range clause.Body { 1346 g.stmt(body, by) 1347 } 1348 } 1349 1350 case *ast.SendStmt: 1351 g.read(stmt.Chan, by) 1352 g.read(stmt.Value, by) 1353 1354 case *ast.SwitchStmt: 1355 g.seeScope(stmt, by, nil) 1356 g.stmt(stmt.Init, by) 1357 g.read(stmt.Tag, by) 1358 for _, clause_ := range stmt.Body.List { 1359 clause := clause_.(*ast.CaseClause) 1360 g.seeScope(clause, by, nil) 1361 for _, expr := range clause.List { 1362 g.read(expr, by) 1363 } 1364 for _, body := range clause.Body { 1365 g.stmt(body, by) 1366 } 1367 } 1368 1369 case *ast.TypeSwitchStmt: 1370 g.seeScope(stmt, by, nil) 1371 g.stmt(stmt.Init, by) 1372 g.stmt(stmt.Assign, by) 1373 for _, clause_ := range stmt.Body.List { 1374 clause := clause_.(*ast.CaseClause) 1375 g.seeScope(clause, by, nil) 1376 for _, expr := range clause.List { 1377 g.read(expr, by) 1378 } 1379 for _, body := range clause.Body { 1380 g.stmt(body, by) 1381 } 1382 } 1383 1384 case *ast.EmptyStmt: 1385 // Nothing to do 1386 1387 default: 1388 lint.ExhaustiveTypeSwitch(stmt) 1389 } 1390 } 1391 1392 // embeddedField sees the field declared by the embedded field node, and marks the type as used by the field. 1393 // 1394 // Embedded fields are special in two ways: they don't have names, so we don't have immediate access to an ast.Ident to 1395 // resolve to the field's types.Var and need to instead walk the AST, and we cannot use g.read on the type because 1396 // eventually we do get to an ast.Ident, and ObjectOf resolves embedded fields to the field they declare, not the type. 1397 // That's why we have code specially for handling embedded fields. 1398 func (g *graph) embeddedField(node ast.Node, by types.Object) *types.Var { 1399 // We need to traverse the tree to find the ast.Ident, but all the nodes we traverse should be used by the object we 1400 // get once we resolve the ident. Collect the nodes and process them once we've found the ident. 1401 nodes := make([]ast.Node, 0, 4) 1402 for { 1403 switch node_ := node.(type) { 1404 case *ast.Ident: 1405 // obj is the field 1406 obj := g.info.ObjectOf(node_).(*types.Var) 1407 // the field is declared by the enclosing type 1408 g.see(obj, by) 1409 for _, n := range nodes { 1410 g.read(n, obj) 1411 } 1412 1413 if tname, ok := g.info.Uses[node_].(*types.TypeName); ok && tname.IsAlias() { 1414 // When embedding an alias we want to use the alias, not what the alias points to. 1415 g.use(tname, obj) 1416 } else { 1417 switch typ := typeutil.Dereference(g.info.TypeOf(node_)).(type) { 1418 case *types.Named: 1419 // (7.2) fields use their types 1420 g.use(typ.Obj(), obj) 1421 case *types.Basic: 1422 // Nothing to do 1423 default: 1424 // Other types are only possible for aliases, which we've already handled 1425 lint.ExhaustiveTypeSwitch(typ) 1426 } 1427 } 1428 return obj 1429 case *ast.StarExpr: 1430 node = node_.X 1431 case *ast.SelectorExpr: 1432 node = node_.Sel 1433 nodes = append(nodes, node_.X) 1434 case *ast.IndexExpr: 1435 node = node_.X 1436 nodes = append(nodes, node_.Index) 1437 case *ast.IndexListExpr: 1438 node = node_.X 1439 default: 1440 lint.ExhaustiveTypeSwitch(node_) 1441 } 1442 } 1443 } 1444 1445 // isNoCopyType reports whether a type represents the NoCopy sentinel 1446 // type. The NoCopy type is a named struct with no fields and exactly 1447 // one method `func Lock()` that is empty. 1448 // 1449 // FIXME(dh): currently we're not checking that the function body is 1450 // empty. 1451 func isNoCopyType(typ types.Type) bool { 1452 st, ok := typ.Underlying().(*types.Struct) 1453 if !ok { 1454 return false 1455 } 1456 if st.NumFields() != 0 { 1457 return false 1458 } 1459 1460 named, ok := typ.(*types.Named) 1461 if !ok { 1462 return false 1463 } 1464 switch num := named.NumMethods(); num { 1465 case 1, 2: 1466 for i := 0; i < num; i++ { 1467 meth := named.Method(i) 1468 if meth.Name() != "Lock" && meth.Name() != "Unlock" { 1469 return false 1470 } 1471 sig := meth.Type().(*types.Signature) 1472 if sig.Params().Len() != 0 || sig.Results().Len() != 0 { 1473 return false 1474 } 1475 } 1476 default: 1477 return false 1478 } 1479 return true 1480 } 1481 1482 func (g *graph) namedType(typ *types.TypeName, spec ast.Expr) { 1483 // (2.2) named types use the type they're based on 1484 1485 if st, ok := spec.(*ast.StructType); ok { 1486 // Named structs are special in that its unexported fields are only used if they're being written to. That is, 1487 // the fields are not used by the named type itself, nor are the types of the fields. 1488 for _, field := range st.Fields.List { 1489 seen := map[*types.Struct]struct{}{} 1490 // For `type x struct { *x; F int }`, don't visit the embedded x 1491 seen[g.info.TypeOf(st).(*types.Struct)] = struct{}{} 1492 var hasExportedField func(t types.Type) bool 1493 hasExportedField = func(T types.Type) bool { 1494 t, ok := typeutil.Dereference(T).Underlying().(*types.Struct) 1495 if !ok { 1496 return false 1497 } 1498 if _, ok := seen[t]; ok { 1499 return false 1500 } 1501 seen[t] = struct{}{} 1502 for i := 0; i < t.NumFields(); i++ { 1503 field := t.Field(i) 1504 if field.Exported() { 1505 return true 1506 } 1507 if field.Embedded() && hasExportedField(field.Type()) { 1508 return true 1509 } 1510 } 1511 return false 1512 } 1513 1514 if len(field.Names) == 0 { 1515 fieldVar := g.embeddedField(field.Type, typ) 1516 if token.IsExported(fieldVar.Name()) && g.opts.ExportedIsUsed { 1517 // (6.2) structs use exported fields 1518 g.use(fieldVar, typ) 1519 } 1520 if g.opts.ExportedIsUsed && g.opts.ExportedFieldsAreUsed && hasExportedField(fieldVar.Type()) { 1521 // (6.5) structs use embedded structs that have exported fields (recursively) 1522 g.use(fieldVar, typ) 1523 } 1524 } else { 1525 for _, name := range field.Names { 1526 obj := g.info.ObjectOf(name) 1527 g.see(obj, typ) 1528 // (7.2) fields use their types 1529 // 1530 // This handles aliases correctly because ObjectOf(alias) returns the TypeName of the alias, not 1531 // what the alias points to. 1532 g.read(field.Type, obj) 1533 if name.Name == "_" { 1534 // (9.9) objects named the blank identifier are used 1535 g.use(obj, typ) 1536 } else if token.IsExported(name.Name) && g.opts.ExportedIsUsed { 1537 // (6.2) structs use exported fields 1538 g.use(obj, typ) 1539 } 1540 1541 if isNoCopyType(obj.Type()) { 1542 // (6.1) structs use fields of type NoCopy sentinel 1543 g.use(obj, typ) 1544 } 1545 } 1546 } 1547 1548 } 1549 } else { 1550 g.read(spec, typ) 1551 } 1552 } 1553 1554 func (g *SerializedGraph) color(rootID NodeID, states []nodeState) { 1555 root := g.nodes[rootID] 1556 if states[rootID].seen() { 1557 return 1558 } 1559 states[rootID] |= nodeStateSeen 1560 for _, n := range root.uses { 1561 g.color(n, states) 1562 } 1563 } 1564 1565 type Object struct { 1566 Name string 1567 ShortName string 1568 // OPT(dh): use an enum for the kind 1569 Kind string 1570 Path ObjectPath 1571 Position token.Position 1572 DisplayPosition token.Position 1573 } 1574 1575 func (g *SerializedGraph) Results() Result { 1576 // XXX objectpath does not return paths for unexported objects, which means that if we analyze the same code twice 1577 // (e.g. normal and test variant), then some objects will appear multiple times, but may not be used identically. we 1578 // have to deduplicate based on the token.Position. Actually we have to do that, anyway, because we may flag types 1579 // local to functions. Those are probably always both used or both unused, but we don't want to flag them twice, 1580 // either. 1581 // 1582 // Note, however, that we still need objectpaths to deduplicate exported identifiers when analyzing independent 1583 // packages in whole-program mode, because if package A uses an object from package B, B will have been imported 1584 // from export data, and we will not have column information. 1585 // 1586 // XXX ^ document that design requirement. 1587 1588 states := g.colorAndQuieten() 1589 1590 var res Result 1591 // OPT(dh): can we find meaningful initial capacities for the used and unused slices? 1592 for _, n := range g.nodes[1:] { 1593 state := states[n.id] 1594 if state.seen() { 1595 res.Used = append(res.Used, n.obj) 1596 } else if state.quiet() { 1597 res.Quiet = append(res.Quiet, n.obj) 1598 } else { 1599 res.Unused = append(res.Unused, n.obj) 1600 } 1601 } 1602 1603 return res 1604 } 1605 1606 func (g *SerializedGraph) colorAndQuieten() []nodeState { 1607 states := make([]nodeState, len(g.nodes)+1) 1608 g.color(0, states) 1609 1610 var quieten func(id NodeID) 1611 quieten = func(id NodeID) { 1612 states[id] |= nodeStateQuiet 1613 for _, owned := range g.nodes[id].owns { 1614 quieten(owned) 1615 } 1616 } 1617 1618 for _, n := range g.nodes { 1619 if states[n.id].seen() { 1620 continue 1621 } 1622 for _, owned := range n.owns { 1623 quieten(owned) 1624 } 1625 } 1626 1627 return states 1628 } 1629 1630 // Dot formats a graph in Graphviz dot format. 1631 func (g *SerializedGraph) Dot() string { 1632 b := &strings.Builder{} 1633 states := g.colorAndQuieten() 1634 // Note: We use addresses in our node names. This only works as long as Go's garbage collector doesn't move 1635 // memory around in the middle of our debug printing. 1636 debugNode := func(n Node) { 1637 if n.id == 0 { 1638 fmt.Fprintf(b, "n%d [label=\"Root\"];\n", n.id) 1639 } else { 1640 color := "red" 1641 if states[n.id].seen() { 1642 color = "green" 1643 } else if states[n.id].quiet() { 1644 color = "grey" 1645 } 1646 label := fmt.Sprintf("%s %s\n%s", n.obj.Kind, n.obj.Name, n.obj.Position) 1647 fmt.Fprintf(b, "n%d [label=%q, color=%q];\n", n.id, label, color) 1648 } 1649 for _, e := range n.uses { 1650 fmt.Fprintf(b, "n%d -> n%d;\n", n.id, e) 1651 } 1652 1653 for _, owned := range n.owns { 1654 fmt.Fprintf(b, "n%d -> n%d [style=dashed];\n", n.id, owned) 1655 } 1656 } 1657 1658 fmt.Fprintf(b, "digraph{\n") 1659 for _, v := range g.nodes { 1660 debugNode(v) 1661 } 1662 1663 fmt.Fprintf(b, "}\n") 1664 1665 return b.String() 1666 } 1667 1668 func Graph(fset *token.FileSet, 1669 files []*ast.File, 1670 pkg *types.Package, 1671 info *types.Info, 1672 directives []lint.Directive, 1673 generated map[string]generated.Generator, 1674 opts Options, 1675 ) []Node { 1676 g := newGraph(fset, files, pkg, info, directives, generated, opts) 1677 g.entry() 1678 return g.nodes 1679 }