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