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