github.com/pankona/gometalinter@v2.0.11+incompatible/_linters/src/honnef.co/go/tools/unused/unused.go (about) 1 package unused // import "honnef.co/go/tools/unused" 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/token" 7 "go/types" 8 "io" 9 "path/filepath" 10 "strings" 11 12 "honnef.co/go/tools/lint" 13 14 "golang.org/x/tools/go/loader" 15 "golang.org/x/tools/go/types/typeutil" 16 ) 17 18 func NewLintChecker(c *Checker) *LintChecker { 19 l := &LintChecker{ 20 c: c, 21 } 22 return l 23 } 24 25 type LintChecker struct { 26 c *Checker 27 } 28 29 func (*LintChecker) Name() string { return "unused" } 30 func (*LintChecker) Prefix() string { return "U" } 31 32 func (l *LintChecker) Init(*lint.Program) {} 33 func (l *LintChecker) Funcs() map[string]lint.Func { 34 return map[string]lint.Func{ 35 "U1000": l.Lint, 36 } 37 } 38 39 func typString(obj types.Object) string { 40 switch obj := obj.(type) { 41 case *types.Func: 42 return "func" 43 case *types.Var: 44 if obj.IsField() { 45 return "field" 46 } 47 return "var" 48 case *types.Const: 49 return "const" 50 case *types.TypeName: 51 return "type" 52 default: 53 // log.Printf("%T", obj) 54 return "identifier" 55 } 56 } 57 58 func (l *LintChecker) Lint(j *lint.Job) { 59 unused := l.c.Check(j.Program.Prog) 60 for _, u := range unused { 61 name := u.Obj.Name() 62 if sig, ok := u.Obj.Type().(*types.Signature); ok && sig.Recv() != nil { 63 switch sig.Recv().Type().(type) { 64 case *types.Named, *types.Pointer: 65 typ := types.TypeString(sig.Recv().Type(), func(*types.Package) string { return "" }) 66 if len(typ) > 0 && typ[0] == '*' { 67 name = fmt.Sprintf("(%s).%s", typ, u.Obj.Name()) 68 } else if len(typ) > 0 { 69 name = fmt.Sprintf("%s.%s", typ, u.Obj.Name()) 70 } 71 } 72 } 73 j.Errorf(u.Obj, "%s %s is unused", typString(u.Obj), name) 74 } 75 } 76 77 type graph struct { 78 roots []*graphNode 79 nodes map[interface{}]*graphNode 80 } 81 82 func (g *graph) markUsedBy(obj, usedBy interface{}) { 83 objNode := g.getNode(obj) 84 usedByNode := g.getNode(usedBy) 85 if objNode.obj == usedByNode.obj { 86 return 87 } 88 usedByNode.uses[objNode] = struct{}{} 89 } 90 91 var labelCounter = 1 92 93 func (g *graph) getNode(obj interface{}) *graphNode { 94 for { 95 if pt, ok := obj.(*types.Pointer); ok { 96 obj = pt.Elem() 97 } else { 98 break 99 } 100 } 101 _, ok := g.nodes[obj] 102 if !ok { 103 g.addObj(obj) 104 } 105 106 return g.nodes[obj] 107 } 108 109 func (g *graph) addObj(obj interface{}) { 110 if pt, ok := obj.(*types.Pointer); ok { 111 obj = pt.Elem() 112 } 113 node := &graphNode{obj: obj, uses: make(map[*graphNode]struct{}), n: labelCounter} 114 g.nodes[obj] = node 115 labelCounter++ 116 117 if obj, ok := obj.(*types.Struct); ok { 118 n := obj.NumFields() 119 for i := 0; i < n; i++ { 120 field := obj.Field(i) 121 g.markUsedBy(obj, field) 122 } 123 } 124 } 125 126 type graphNode struct { 127 obj interface{} 128 uses map[*graphNode]struct{} 129 used bool 130 quiet bool 131 n int 132 } 133 134 type CheckMode int 135 136 const ( 137 CheckConstants CheckMode = 1 << iota 138 CheckFields 139 CheckFunctions 140 CheckTypes 141 CheckVariables 142 143 CheckAll = CheckConstants | CheckFields | CheckFunctions | CheckTypes | CheckVariables 144 ) 145 146 type Unused struct { 147 Obj types.Object 148 Position token.Position 149 } 150 151 type Checker struct { 152 Mode CheckMode 153 WholeProgram bool 154 ConsiderReflection bool 155 Debug io.Writer 156 157 graph *graph 158 159 msCache typeutil.MethodSetCache 160 lprog *loader.Program 161 topmostCache map[*types.Scope]*types.Scope 162 interfaces []*types.Interface 163 } 164 165 func NewChecker(mode CheckMode) *Checker { 166 return &Checker{ 167 Mode: mode, 168 graph: &graph{ 169 nodes: make(map[interface{}]*graphNode), 170 }, 171 topmostCache: make(map[*types.Scope]*types.Scope), 172 } 173 } 174 175 func (c *Checker) checkConstants() bool { return (c.Mode & CheckConstants) > 0 } 176 func (c *Checker) checkFields() bool { return (c.Mode & CheckFields) > 0 } 177 func (c *Checker) checkFunctions() bool { return (c.Mode & CheckFunctions) > 0 } 178 func (c *Checker) checkTypes() bool { return (c.Mode & CheckTypes) > 0 } 179 func (c *Checker) checkVariables() bool { return (c.Mode & CheckVariables) > 0 } 180 181 func (c *Checker) markFields(typ types.Type) { 182 structType, ok := typ.Underlying().(*types.Struct) 183 if !ok { 184 return 185 } 186 n := structType.NumFields() 187 for i := 0; i < n; i++ { 188 field := structType.Field(i) 189 c.graph.markUsedBy(field, typ) 190 } 191 } 192 193 type Error struct { 194 Errors map[string][]error 195 } 196 197 func (e Error) Error() string { 198 return fmt.Sprintf("errors in %d packages", len(e.Errors)) 199 } 200 201 func (c *Checker) Check(lprog *loader.Program) []Unused { 202 var unused []Unused 203 c.lprog = lprog 204 if c.WholeProgram { 205 c.findExportedInterfaces() 206 } 207 for _, pkg := range c.lprog.InitialPackages() { 208 c.processDefs(pkg) 209 c.processUses(pkg) 210 c.processTypes(pkg) 211 c.processSelections(pkg) 212 c.processAST(pkg) 213 } 214 215 for _, node := range c.graph.nodes { 216 obj, ok := node.obj.(types.Object) 217 if !ok { 218 continue 219 } 220 typNode, ok := c.graph.nodes[obj.Type()] 221 if !ok { 222 continue 223 } 224 node.uses[typNode] = struct{}{} 225 } 226 227 roots := map[*graphNode]struct{}{} 228 for _, root := range c.graph.roots { 229 roots[root] = struct{}{} 230 } 231 markNodesUsed(roots) 232 c.markNodesQuiet() 233 234 if c.Debug != nil { 235 c.printDebugGraph(c.Debug) 236 } 237 238 for _, node := range c.graph.nodes { 239 if node.used || node.quiet { 240 continue 241 } 242 obj, ok := node.obj.(types.Object) 243 if !ok { 244 continue 245 } 246 found := false 247 if !false { 248 for _, pkg := range c.lprog.InitialPackages() { 249 if pkg.Pkg == obj.Pkg() { 250 found = true 251 break 252 } 253 } 254 } 255 if !found { 256 continue 257 } 258 259 pos := c.lprog.Fset.Position(obj.Pos()) 260 if pos.Filename == "" || filepath.Base(pos.Filename) == "C" { 261 continue 262 } 263 generated := false 264 for _, file := range c.lprog.Package(obj.Pkg().Path()).Files { 265 if c.lprog.Fset.Position(file.Pos()).Filename != pos.Filename { 266 continue 267 } 268 if len(file.Comments) > 0 { 269 generated = isGenerated(file.Comments[0].Text()) 270 } 271 break 272 } 273 if generated { 274 continue 275 } 276 unused = append(unused, Unused{Obj: obj, Position: pos}) 277 } 278 return unused 279 } 280 281 // isNoCopyType reports whether a type represents the NoCopy sentinel 282 // type. The NoCopy type is a named struct with no fields and exactly 283 // one method `func Lock()` that is empty. 284 // 285 // FIXME(dh): currently we're not checking that the function body is 286 // empty. 287 func isNoCopyType(typ types.Type) bool { 288 st, ok := typ.Underlying().(*types.Struct) 289 if !ok { 290 return false 291 } 292 if st.NumFields() != 0 { 293 return false 294 } 295 296 named, ok := typ.(*types.Named) 297 if !ok { 298 return false 299 } 300 if named.NumMethods() != 1 { 301 return false 302 } 303 meth := named.Method(0) 304 if meth.Name() != "Lock" { 305 return false 306 } 307 sig := meth.Type().(*types.Signature) 308 if sig.Params().Len() != 0 || sig.Results().Len() != 0 { 309 return false 310 } 311 return true 312 } 313 314 func (c *Checker) useNoCopyFields(typ types.Type) { 315 if st, ok := typ.Underlying().(*types.Struct); ok { 316 n := st.NumFields() 317 for i := 0; i < n; i++ { 318 field := st.Field(i) 319 if isNoCopyType(field.Type()) { 320 c.graph.markUsedBy(field, typ) 321 } 322 } 323 } 324 } 325 326 func (c *Checker) useExportedFields(typ types.Type) { 327 if st, ok := typ.Underlying().(*types.Struct); ok { 328 n := st.NumFields() 329 for i := 0; i < n; i++ { 330 field := st.Field(i) 331 if field.Exported() { 332 c.graph.markUsedBy(field, typ) 333 } 334 } 335 } 336 } 337 338 func (c *Checker) useExportedMethods(typ types.Type) { 339 named, ok := typ.(*types.Named) 340 if !ok { 341 return 342 } 343 ms := typeutil.IntuitiveMethodSet(named, &c.msCache) 344 for i := 0; i < len(ms); i++ { 345 meth := ms[i].Obj() 346 if meth.Exported() { 347 c.graph.markUsedBy(meth, typ) 348 } 349 } 350 351 st, ok := named.Underlying().(*types.Struct) 352 if !ok { 353 return 354 } 355 n := st.NumFields() 356 for i := 0; i < n; i++ { 357 field := st.Field(i) 358 if !field.Anonymous() { 359 continue 360 } 361 ms := typeutil.IntuitiveMethodSet(field.Type(), &c.msCache) 362 for j := 0; j < len(ms); j++ { 363 if ms[j].Obj().Exported() { 364 c.graph.markUsedBy(field, typ) 365 break 366 } 367 } 368 } 369 } 370 371 func (c *Checker) processDefs(pkg *loader.PackageInfo) { 372 for _, obj := range pkg.Defs { 373 if obj == nil { 374 continue 375 } 376 c.graph.getNode(obj) 377 378 if obj, ok := obj.(*types.TypeName); ok { 379 c.graph.markUsedBy(obj.Type().Underlying(), obj.Type()) 380 c.graph.markUsedBy(obj.Type(), obj) // TODO is this needed? 381 c.graph.markUsedBy(obj, obj.Type()) 382 383 // We mark all exported fields as used. For normal 384 // operation, we have to. The user may use these fields 385 // without us knowing. 386 // 387 // TODO(dh): In whole-program mode, however, we mark them 388 // as used because of reflection (such as JSON 389 // marshaling). Strictly speaking, we would only need to 390 // mark them used if an instance of the type was 391 // accessible via an interface value. 392 if !c.WholeProgram || c.ConsiderReflection { 393 c.useExportedFields(obj.Type()) 394 } 395 396 // TODO(dh): Traditionally we have not marked all exported 397 // methods as exported, even though they're strictly 398 // speaking accessible through reflection. We've done that 399 // because using methods just via reflection is rare, and 400 // not worth the false negatives. With the new -reflect 401 // flag, however, we should reconsider that choice. 402 if !c.WholeProgram { 403 c.useExportedMethods(obj.Type()) 404 } 405 } 406 407 switch obj := obj.(type) { 408 case *types.Var, *types.Const, *types.Func, *types.TypeName: 409 if obj.Exported() { 410 // Exported variables and constants use their types, 411 // even if there's no expression using them in the 412 // checked program. 413 // 414 // Also operates on funcs and type names, but that's 415 // irrelevant/redundant. 416 c.graph.markUsedBy(obj.Type(), obj) 417 } 418 if obj.Name() == "_" { 419 node := c.graph.getNode(obj) 420 node.quiet = true 421 scope := c.topmostScope(pkg.Pkg.Scope().Innermost(obj.Pos()), pkg.Pkg) 422 if scope == pkg.Pkg.Scope() { 423 c.graph.roots = append(c.graph.roots, node) 424 } else { 425 c.graph.markUsedBy(obj, scope) 426 } 427 } else { 428 // Variables declared in functions are used. This is 429 // done so that arguments and return parameters are 430 // always marked as used. 431 if _, ok := obj.(*types.Var); ok { 432 if obj.Parent() != obj.Pkg().Scope() && obj.Parent() != nil { 433 c.graph.markUsedBy(obj, c.topmostScope(obj.Parent(), obj.Pkg())) 434 c.graph.markUsedBy(obj.Type(), obj) 435 } 436 } 437 } 438 } 439 440 if fn, ok := obj.(*types.Func); ok { 441 // A function uses its signature 442 c.graph.markUsedBy(fn, fn.Type()) 443 444 // A function uses its return types 445 sig := fn.Type().(*types.Signature) 446 res := sig.Results() 447 n := res.Len() 448 for i := 0; i < n; i++ { 449 c.graph.markUsedBy(res.At(i).Type(), fn) 450 } 451 } 452 453 if obj, ok := obj.(interface { 454 Scope() *types.Scope 455 Pkg() *types.Package 456 }); ok { 457 scope := obj.Scope() 458 c.graph.markUsedBy(c.topmostScope(scope, obj.Pkg()), obj) 459 } 460 461 if c.isRoot(obj) { 462 node := c.graph.getNode(obj) 463 c.graph.roots = append(c.graph.roots, node) 464 if obj, ok := obj.(*types.PkgName); ok { 465 scope := obj.Pkg().Scope() 466 c.graph.markUsedBy(scope, obj) 467 } 468 } 469 } 470 } 471 472 func (c *Checker) processUses(pkg *loader.PackageInfo) { 473 for ident, usedObj := range pkg.Uses { 474 if _, ok := usedObj.(*types.PkgName); ok { 475 continue 476 } 477 pos := ident.Pos() 478 scope := pkg.Pkg.Scope().Innermost(pos) 479 scope = c.topmostScope(scope, pkg.Pkg) 480 if scope != pkg.Pkg.Scope() { 481 c.graph.markUsedBy(usedObj, scope) 482 } 483 484 switch usedObj.(type) { 485 case *types.Var, *types.Const: 486 c.graph.markUsedBy(usedObj.Type(), usedObj) 487 } 488 } 489 } 490 491 func (c *Checker) findExportedInterfaces() { 492 c.interfaces = []*types.Interface{types.Universe.Lookup("error").Type().(*types.Named).Underlying().(*types.Interface)} 493 var pkgs []*loader.PackageInfo 494 if c.WholeProgram { 495 for _, pkg := range c.lprog.AllPackages { 496 pkgs = append(pkgs, pkg) 497 } 498 } else { 499 pkgs = c.lprog.InitialPackages() 500 } 501 502 for _, pkg := range pkgs { 503 for _, tv := range pkg.Types { 504 iface, ok := tv.Type.(*types.Interface) 505 if !ok { 506 continue 507 } 508 if iface.NumMethods() == 0 { 509 continue 510 } 511 c.interfaces = append(c.interfaces, iface) 512 } 513 } 514 } 515 516 func (c *Checker) processTypes(pkg *loader.PackageInfo) { 517 named := map[*types.Named]*types.Pointer{} 518 var interfaces []*types.Interface 519 for _, tv := range pkg.Types { 520 if typ, ok := tv.Type.(interface { 521 Elem() types.Type 522 }); ok { 523 c.graph.markUsedBy(typ.Elem(), typ) 524 } 525 526 switch obj := tv.Type.(type) { 527 case *types.Named: 528 named[obj] = types.NewPointer(obj) 529 c.graph.markUsedBy(obj, obj.Underlying()) 530 c.graph.markUsedBy(obj.Underlying(), obj) 531 case *types.Interface: 532 if obj.NumMethods() > 0 { 533 interfaces = append(interfaces, obj) 534 } 535 case *types.Struct: 536 c.useNoCopyFields(obj) 537 if pkg.Pkg.Name() != "main" && !c.WholeProgram { 538 c.useExportedFields(obj) 539 } 540 } 541 } 542 543 // Pretend that all types are meant to implement as many 544 // interfaces as possible. 545 // 546 // TODO(dh): For normal operations, that's the best we can do, as 547 // we have no idea what external users will do with our types. In 548 // whole-program mode, we could be more conservative, in two ways: 549 // 1) Only consider interfaces if a type has been assigned to one 550 // 2) Use SSA and flow analysis and determine the exact set of 551 // interfaces that is relevant. 552 fn := func(iface *types.Interface) { 553 for obj, objPtr := range named { 554 if !types.Implements(obj, iface) && !types.Implements(objPtr, iface) { 555 continue 556 } 557 ifaceMethods := make(map[string]struct{}, iface.NumMethods()) 558 n := iface.NumMethods() 559 for i := 0; i < n; i++ { 560 meth := iface.Method(i) 561 ifaceMethods[meth.Name()] = struct{}{} 562 } 563 for _, obj := range []types.Type{obj, objPtr} { 564 ms := c.msCache.MethodSet(obj) 565 n := ms.Len() 566 for i := 0; i < n; i++ { 567 sel := ms.At(i) 568 meth := sel.Obj().(*types.Func) 569 _, found := ifaceMethods[meth.Name()] 570 if !found { 571 continue 572 } 573 c.graph.markUsedBy(meth.Type().(*types.Signature).Recv().Type(), obj) // embedded receiver 574 if len(sel.Index()) > 1 { 575 f := getField(obj, sel.Index()[0]) 576 c.graph.markUsedBy(f, obj) // embedded receiver 577 } 578 c.graph.markUsedBy(meth, obj) 579 } 580 } 581 } 582 } 583 584 for _, iface := range interfaces { 585 fn(iface) 586 } 587 for _, iface := range c.interfaces { 588 fn(iface) 589 } 590 } 591 592 func (c *Checker) processSelections(pkg *loader.PackageInfo) { 593 fn := func(expr *ast.SelectorExpr, sel *types.Selection, offset int) { 594 scope := pkg.Pkg.Scope().Innermost(expr.Pos()) 595 c.graph.markUsedBy(expr.X, c.topmostScope(scope, pkg.Pkg)) 596 c.graph.markUsedBy(sel.Obj(), expr.X) 597 if len(sel.Index()) > 1 { 598 typ := sel.Recv() 599 indices := sel.Index() 600 for _, idx := range indices[:len(indices)-offset] { 601 obj := getField(typ, idx) 602 typ = obj.Type() 603 c.graph.markUsedBy(obj, expr.X) 604 } 605 } 606 } 607 608 for expr, sel := range pkg.Selections { 609 switch sel.Kind() { 610 case types.FieldVal: 611 fn(expr, sel, 0) 612 case types.MethodVal: 613 fn(expr, sel, 1) 614 } 615 } 616 } 617 618 func dereferenceType(typ types.Type) types.Type { 619 if typ, ok := typ.(*types.Pointer); ok { 620 return typ.Elem() 621 } 622 return typ 623 } 624 625 // processConversion marks fields as used if they're part of a type conversion. 626 func (c *Checker) processConversion(pkg *loader.PackageInfo, node ast.Node) { 627 if node, ok := node.(*ast.CallExpr); ok { 628 callTyp := pkg.TypeOf(node.Fun) 629 var typDst *types.Struct 630 var ok bool 631 switch typ := callTyp.(type) { 632 case *types.Named: 633 typDst, ok = typ.Underlying().(*types.Struct) 634 case *types.Pointer: 635 typDst, ok = typ.Elem().Underlying().(*types.Struct) 636 default: 637 return 638 } 639 if !ok { 640 return 641 } 642 643 if typ, ok := pkg.TypeOf(node.Args[0]).(*types.Basic); ok && typ.Kind() == types.UnsafePointer { 644 // This is an unsafe conversion. Assume that all the 645 // fields are relevant (they are, because of memory 646 // layout) 647 n := typDst.NumFields() 648 for i := 0; i < n; i++ { 649 c.graph.markUsedBy(typDst.Field(i), typDst) 650 } 651 return 652 } 653 654 typSrc, ok := dereferenceType(pkg.TypeOf(node.Args[0])).Underlying().(*types.Struct) 655 if !ok { 656 return 657 } 658 659 // When we convert from type t1 to t2, were t1 and t2 are 660 // structs, all fields are relevant, as otherwise the 661 // conversion would fail. 662 // 663 // We mark t2's fields as used by t1's fields, and vice 664 // versa. That way, if no code actually refers to a field 665 // in either type, it's still correctly marked as unused. 666 // If a field is used in either struct, it's implicitly 667 // relevant in the other one, too. 668 // 669 // It works in a similar way for conversions between types 670 // of two packages, only that the extra information in the 671 // graph is redundant unless we're in whole program mode. 672 n := typDst.NumFields() 673 for i := 0; i < n; i++ { 674 fDst := typDst.Field(i) 675 fSrc := typSrc.Field(i) 676 c.graph.markUsedBy(fDst, fSrc) 677 c.graph.markUsedBy(fSrc, fDst) 678 } 679 } 680 } 681 682 // processCompositeLiteral marks fields as used if the struct is used 683 // in a composite literal. 684 func (c *Checker) processCompositeLiteral(pkg *loader.PackageInfo, node ast.Node) { 685 // XXX how does this actually work? wouldn't it match t{}? 686 if node, ok := node.(*ast.CompositeLit); ok { 687 typ := pkg.TypeOf(node) 688 if _, ok := typ.(*types.Named); ok { 689 typ = typ.Underlying() 690 } 691 if _, ok := typ.(*types.Struct); !ok { 692 return 693 } 694 695 if isBasicStruct(node.Elts) { 696 c.markFields(typ) 697 } 698 } 699 } 700 701 // processCgoExported marks functions as used if they're being 702 // exported to cgo. 703 func (c *Checker) processCgoExported(pkg *loader.PackageInfo, node ast.Node) { 704 if node, ok := node.(*ast.FuncDecl); ok { 705 if node.Doc == nil { 706 return 707 } 708 for _, cmt := range node.Doc.List { 709 if !strings.HasPrefix(cmt.Text, "//go:cgo_export_") { 710 return 711 } 712 obj := pkg.ObjectOf(node.Name) 713 c.graph.roots = append(c.graph.roots, c.graph.getNode(obj)) 714 } 715 } 716 } 717 718 func (c *Checker) processVariableDeclaration(pkg *loader.PackageInfo, node ast.Node) { 719 if decl, ok := node.(*ast.GenDecl); ok { 720 for _, spec := range decl.Specs { 721 spec, ok := spec.(*ast.ValueSpec) 722 if !ok { 723 continue 724 } 725 for i, name := range spec.Names { 726 if i >= len(spec.Values) { 727 break 728 } 729 value := spec.Values[i] 730 fn := func(node ast.Node) bool { 731 if node3, ok := node.(*ast.Ident); ok { 732 obj := pkg.ObjectOf(node3) 733 if _, ok := obj.(*types.PkgName); ok { 734 return true 735 } 736 c.graph.markUsedBy(obj, pkg.ObjectOf(name)) 737 } 738 return true 739 } 740 ast.Inspect(value, fn) 741 } 742 } 743 } 744 } 745 746 func (c *Checker) processArrayConstants(pkg *loader.PackageInfo, node ast.Node) { 747 if decl, ok := node.(*ast.ArrayType); ok { 748 ident, ok := decl.Len.(*ast.Ident) 749 if !ok { 750 return 751 } 752 c.graph.markUsedBy(pkg.ObjectOf(ident), pkg.TypeOf(decl)) 753 } 754 } 755 756 func (c *Checker) processKnownReflectMethodCallers(pkg *loader.PackageInfo, node ast.Node) { 757 call, ok := node.(*ast.CallExpr) 758 if !ok { 759 return 760 } 761 sel, ok := call.Fun.(*ast.SelectorExpr) 762 if !ok { 763 return 764 } 765 if types.TypeString(pkg.TypeOf(sel.X), nil) != "*net/rpc.Server" { 766 x, ok := sel.X.(*ast.Ident) 767 if !ok { 768 return 769 } 770 pkgname, ok := pkg.ObjectOf(x).(*types.PkgName) 771 if !ok { 772 return 773 } 774 if pkgname.Imported().Path() != "net/rpc" { 775 return 776 } 777 } 778 779 var arg ast.Expr 780 switch sel.Sel.Name { 781 case "Register": 782 if len(call.Args) != 1 { 783 return 784 } 785 arg = call.Args[0] 786 case "RegisterName": 787 if len(call.Args) != 2 { 788 return 789 } 790 arg = call.Args[1] 791 } 792 typ := pkg.TypeOf(arg) 793 ms := types.NewMethodSet(typ) 794 for i := 0; i < ms.Len(); i++ { 795 c.graph.markUsedBy(ms.At(i).Obj(), typ) 796 } 797 } 798 799 func (c *Checker) processAST(pkg *loader.PackageInfo) { 800 fn := func(node ast.Node) bool { 801 c.processConversion(pkg, node) 802 c.processKnownReflectMethodCallers(pkg, node) 803 c.processCompositeLiteral(pkg, node) 804 c.processCgoExported(pkg, node) 805 c.processVariableDeclaration(pkg, node) 806 c.processArrayConstants(pkg, node) 807 return true 808 } 809 for _, file := range pkg.Files { 810 ast.Inspect(file, fn) 811 } 812 } 813 814 func isBasicStruct(elts []ast.Expr) bool { 815 for _, elt := range elts { 816 if _, ok := elt.(*ast.KeyValueExpr); !ok { 817 return true 818 } 819 } 820 return false 821 } 822 823 func isPkgScope(obj types.Object) bool { 824 return obj.Parent() == obj.Pkg().Scope() 825 } 826 827 func isMain(obj types.Object) bool { 828 if obj.Pkg().Name() != "main" { 829 return false 830 } 831 if obj.Name() != "main" { 832 return false 833 } 834 if !isPkgScope(obj) { 835 return false 836 } 837 if !isFunction(obj) { 838 return false 839 } 840 if isMethod(obj) { 841 return false 842 } 843 return true 844 } 845 846 func isFunction(obj types.Object) bool { 847 _, ok := obj.(*types.Func) 848 return ok 849 } 850 851 func isMethod(obj types.Object) bool { 852 if !isFunction(obj) { 853 return false 854 } 855 return obj.(*types.Func).Type().(*types.Signature).Recv() != nil 856 } 857 858 func isVariable(obj types.Object) bool { 859 _, ok := obj.(*types.Var) 860 return ok 861 } 862 863 func isConstant(obj types.Object) bool { 864 _, ok := obj.(*types.Const) 865 return ok 866 } 867 868 func isType(obj types.Object) bool { 869 _, ok := obj.(*types.TypeName) 870 return ok 871 } 872 873 func isField(obj types.Object) bool { 874 if obj, ok := obj.(*types.Var); ok && obj.IsField() { 875 return true 876 } 877 return false 878 } 879 880 func (c *Checker) checkFlags(v interface{}) bool { 881 obj, ok := v.(types.Object) 882 if !ok { 883 return false 884 } 885 if isFunction(obj) && !c.checkFunctions() { 886 return false 887 } 888 if isVariable(obj) && !c.checkVariables() { 889 return false 890 } 891 if isConstant(obj) && !c.checkConstants() { 892 return false 893 } 894 if isType(obj) && !c.checkTypes() { 895 return false 896 } 897 if isField(obj) && !c.checkFields() { 898 return false 899 } 900 return true 901 } 902 903 func (c *Checker) isRoot(obj types.Object) bool { 904 // - in local mode, main, init, tests, and non-test, non-main exported are roots 905 // - in global mode (not yet implemented), main, init and tests are roots 906 907 if _, ok := obj.(*types.PkgName); ok { 908 return true 909 } 910 911 if isMain(obj) || (isFunction(obj) && !isMethod(obj) && obj.Name() == "init") { 912 return true 913 } 914 if obj.Exported() { 915 f := c.lprog.Fset.Position(obj.Pos()).Filename 916 if strings.HasSuffix(f, "_test.go") { 917 return strings.HasPrefix(obj.Name(), "Test") || 918 strings.HasPrefix(obj.Name(), "Benchmark") || 919 strings.HasPrefix(obj.Name(), "Example") 920 } 921 922 // Package-level are used, except in package main 923 if isPkgScope(obj) && obj.Pkg().Name() != "main" && !c.WholeProgram { 924 return true 925 } 926 } 927 return false 928 } 929 930 func markNodesUsed(nodes map[*graphNode]struct{}) { 931 for node := range nodes { 932 wasUsed := node.used 933 node.used = true 934 if !wasUsed { 935 markNodesUsed(node.uses) 936 } 937 } 938 } 939 940 func (c *Checker) markNodesQuiet() { 941 for _, node := range c.graph.nodes { 942 if node.used { 943 continue 944 } 945 if obj, ok := node.obj.(types.Object); ok && !c.checkFlags(obj) { 946 node.quiet = true 947 continue 948 } 949 c.markObjQuiet(node.obj) 950 } 951 } 952 953 func (c *Checker) markObjQuiet(obj interface{}) { 954 switch obj := obj.(type) { 955 case *types.Named: 956 n := obj.NumMethods() 957 for i := 0; i < n; i++ { 958 meth := obj.Method(i) 959 node := c.graph.getNode(meth) 960 node.quiet = true 961 c.markObjQuiet(meth.Scope()) 962 } 963 case *types.Struct: 964 n := obj.NumFields() 965 for i := 0; i < n; i++ { 966 field := obj.Field(i) 967 c.graph.nodes[field].quiet = true 968 } 969 case *types.Func: 970 c.markObjQuiet(obj.Scope()) 971 case *types.Scope: 972 if obj == nil { 973 return 974 } 975 if obj.Parent() == types.Universe { 976 return 977 } 978 for _, name := range obj.Names() { 979 v := obj.Lookup(name) 980 if n, ok := c.graph.nodes[v]; ok { 981 n.quiet = true 982 } 983 } 984 n := obj.NumChildren() 985 for i := 0; i < n; i++ { 986 c.markObjQuiet(obj.Child(i)) 987 } 988 } 989 } 990 991 func getField(typ types.Type, idx int) *types.Var { 992 switch obj := typ.(type) { 993 case *types.Pointer: 994 return getField(obj.Elem(), idx) 995 case *types.Named: 996 switch v := obj.Underlying().(type) { 997 case *types.Struct: 998 return v.Field(idx) 999 case *types.Pointer: 1000 return getField(v.Elem(), idx) 1001 default: 1002 panic(fmt.Sprintf("unexpected type %s", typ)) 1003 } 1004 case *types.Struct: 1005 return obj.Field(idx) 1006 } 1007 return nil 1008 } 1009 1010 func (c *Checker) topmostScope(scope *types.Scope, pkg *types.Package) (ret *types.Scope) { 1011 if top, ok := c.topmostCache[scope]; ok { 1012 return top 1013 } 1014 defer func() { 1015 c.topmostCache[scope] = ret 1016 }() 1017 if scope == pkg.Scope() { 1018 return scope 1019 } 1020 if scope.Parent().Parent() == pkg.Scope() { 1021 return scope 1022 } 1023 return c.topmostScope(scope.Parent(), pkg) 1024 } 1025 1026 func (c *Checker) printDebugGraph(w io.Writer) { 1027 fmt.Fprintln(w, "digraph {") 1028 fmt.Fprintln(w, "n0 [label = roots]") 1029 for _, node := range c.graph.nodes { 1030 s := fmt.Sprintf("%s (%T)", node.obj, node.obj) 1031 s = strings.Replace(s, "\n", "", -1) 1032 s = strings.Replace(s, `"`, "", -1) 1033 fmt.Fprintf(w, `n%d [label = %q]`, node.n, s) 1034 color := "black" 1035 switch { 1036 case node.used: 1037 color = "green" 1038 case node.quiet: 1039 color = "orange" 1040 case !c.checkFlags(node.obj): 1041 color = "purple" 1042 default: 1043 color = "red" 1044 } 1045 fmt.Fprintf(w, "[color = %s]", color) 1046 fmt.Fprintln(w) 1047 } 1048 1049 for _, node1 := range c.graph.nodes { 1050 for node2 := range node1.uses { 1051 fmt.Fprintf(w, "n%d -> n%d\n", node1.n, node2.n) 1052 } 1053 } 1054 for _, root := range c.graph.roots { 1055 fmt.Fprintf(w, "n0 -> n%d\n", root.n) 1056 } 1057 fmt.Fprintln(w, "}") 1058 } 1059 1060 func isGenerated(comment string) bool { 1061 return strings.Contains(comment, "Code generated by") || 1062 strings.Contains(comment, "DO NOT EDIT") 1063 }