github.com/golangci/go-tools@v0.0.0-20190318060251-af6baa5dc196/unused/unused.go (about) 1 package unused // import "github.com/golangci/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 "github.com/golangci/go-tools/lint" 13 . "github.com/golangci/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 269 return unused 270 } 271 272 // isNoCopyType reports whether a type represents the NoCopy sentinel 273 // type. The NoCopy type is a named struct with no fields and exactly 274 // one method `func Lock()` that is empty. 275 // 276 // FIXME(dh): currently we're not checking that the function body is 277 // empty. 278 func isNoCopyType(typ types.Type) bool { 279 st, ok := typ.Underlying().(*types.Struct) 280 if !ok { 281 return false 282 } 283 if st.NumFields() != 0 { 284 return false 285 } 286 287 named, ok := typ.(*types.Named) 288 if !ok { 289 return false 290 } 291 if named.NumMethods() != 1 { 292 return false 293 } 294 meth := named.Method(0) 295 if meth.Name() != "Lock" { 296 return false 297 } 298 sig := meth.Type().(*types.Signature) 299 if sig.Params().Len() != 0 || sig.Results().Len() != 0 { 300 return false 301 } 302 return true 303 } 304 305 func (c *Checker) useNoCopyFields(typ types.Type) { 306 if st, ok := typ.Underlying().(*types.Struct); ok { 307 n := st.NumFields() 308 for i := 0; i < n; i++ { 309 field := st.Field(i) 310 if isNoCopyType(field.Type()) { 311 c.graph.markUsedBy(field, typ) 312 c.graph.markUsedBy(field.Type().(*types.Named).Method(0), field.Type()) 313 } 314 } 315 } 316 } 317 318 func (c *Checker) useExportedFields(typ types.Type, by types.Type) bool { 319 any := false 320 if st, ok := typ.Underlying().(*types.Struct); ok { 321 n := st.NumFields() 322 for i := 0; i < n; i++ { 323 field := st.Field(i) 324 if field.Anonymous() { 325 if c.useExportedFields(field.Type(), typ) { 326 c.graph.markUsedBy(field, typ) 327 } 328 } 329 if field.Exported() { 330 c.graph.markUsedBy(field, by) 331 any = true 332 } 333 } 334 } 335 return any 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 *lint.Pkg) { 372 for _, obj := range pkg.TypesInfo.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(), 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.Types.Scope().Innermost(obj.Pos()), pkg.Types) 422 if scope == pkg.Types.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 *lint.Pkg) { 473 for ident, usedObj := range pkg.TypesInfo.Uses { 474 if _, ok := usedObj.(*types.PkgName); ok { 475 continue 476 } 477 pos := ident.Pos() 478 scope := pkg.Types.Scope().Innermost(pos) 479 scope = c.topmostScope(scope, pkg.Types) 480 if scope != pkg.Types.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 []*packages.Package 494 if c.WholeProgram { 495 pkgs = append(pkgs, c.prog.AllPackages...) 496 } else { 497 for _, pkg := range c.prog.InitialPackages { 498 pkgs = append(pkgs, pkg.Package) 499 } 500 } 501 502 for _, pkg := range pkgs { 503 for _, tv := range pkg.TypesInfo.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 *lint.Pkg) { 517 named := map[*types.Named]*types.Pointer{} 518 var interfaces []*types.Interface 519 for _, tv := range pkg.TypesInfo.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.Types.Name() != "main" && !c.WholeProgram { 538 c.useExportedFields(obj, 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 precise, 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 i := 0; i < iface.NumEmbeddeds(); i++ { 554 c.graph.markUsedBy(iface.Embedded(i), iface) 555 } 556 namedLoop: 557 for obj, objPtr := range named { 558 switch obj.Underlying().(type) { 559 case *types.Interface: 560 // pointers to interfaces have no methods, only checking non-pointer 561 if !c.implements(obj, iface) { 562 continue namedLoop 563 } 564 default: 565 // pointer receivers include the method set of non-pointer receivers, 566 // only checking pointer 567 if !c.implements(objPtr, iface) { 568 continue namedLoop 569 } 570 } 571 572 ifaceMethods := make(map[string]struct{}, iface.NumMethods()) 573 n := iface.NumMethods() 574 for i := 0; i < n; i++ { 575 meth := iface.Method(i) 576 ifaceMethods[meth.Name()] = struct{}{} 577 } 578 for _, obj := range []types.Type{obj, objPtr} { 579 ms := c.msCache.MethodSet(obj) 580 n := ms.Len() 581 for i := 0; i < n; i++ { 582 sel := ms.At(i) 583 meth := sel.Obj().(*types.Func) 584 _, found := ifaceMethods[meth.Name()] 585 if !found { 586 continue 587 } 588 c.graph.markUsedBy(meth.Type().(*types.Signature).Recv().Type(), obj) // embedded receiver 589 if len(sel.Index()) > 1 { 590 f := getField(obj, sel.Index()[0]) 591 c.graph.markUsedBy(f, obj) // embedded receiver 592 } 593 c.graph.markUsedBy(meth, obj) 594 } 595 } 596 } 597 } 598 599 for _, iface := range interfaces { 600 fn(iface) 601 } 602 for _, iface := range c.interfaces { 603 fn(iface) 604 } 605 } 606 607 func (c *Checker) processSelections(pkg *lint.Pkg) { 608 fn := func(expr *ast.SelectorExpr, sel *types.Selection, offset int) { 609 scope := pkg.Types.Scope().Innermost(expr.Pos()) 610 c.graph.markUsedBy(sel, c.topmostScope(scope, pkg.Types)) 611 c.graph.markUsedBy(sel.Obj(), sel) 612 if len(sel.Index()) > 1 { 613 typ := sel.Recv() 614 indices := sel.Index() 615 for _, idx := range indices[:len(indices)-offset] { 616 obj := getField(typ, idx) 617 typ = obj.Type() 618 c.graph.markUsedBy(obj, sel) 619 } 620 } 621 } 622 623 for expr, sel := range pkg.TypesInfo.Selections { 624 switch sel.Kind() { 625 case types.FieldVal: 626 fn(expr, sel, 0) 627 case types.MethodVal: 628 fn(expr, sel, 1) 629 } 630 } 631 } 632 633 func dereferenceType(typ types.Type) types.Type { 634 if typ, ok := typ.(*types.Pointer); ok { 635 return typ.Elem() 636 } 637 return typ 638 } 639 640 // processConversion marks fields as used if they're part of a type conversion. 641 func (c *Checker) processConversion(pkg *lint.Pkg, node ast.Node) { 642 if node, ok := node.(*ast.CallExpr); ok { 643 callTyp := pkg.TypesInfo.TypeOf(node.Fun) 644 var typDst *types.Struct 645 var ok bool 646 switch typ := callTyp.(type) { 647 case *types.Named: 648 typDst, ok = typ.Underlying().(*types.Struct) 649 case *types.Pointer: 650 typDst, ok = typ.Elem().Underlying().(*types.Struct) 651 default: 652 return 653 } 654 if !ok { 655 return 656 } 657 658 if typ, ok := pkg.TypesInfo.TypeOf(node.Args[0]).(*types.Basic); ok && typ.Kind() == types.UnsafePointer { 659 // This is an unsafe conversion. Assume that all the 660 // fields are relevant (they are, because of memory 661 // layout) 662 n := typDst.NumFields() 663 for i := 0; i < n; i++ { 664 c.graph.markUsedBy(typDst.Field(i), typDst) 665 } 666 return 667 } 668 669 typSrc, ok := dereferenceType(pkg.TypesInfo.TypeOf(node.Args[0])).Underlying().(*types.Struct) 670 if !ok { 671 return 672 } 673 674 // When we convert from type t1 to t2, were t1 and t2 are 675 // structs, all fields are relevant, as otherwise the 676 // conversion would fail. 677 // 678 // We mark t2's fields as used by t1's fields, and vice 679 // versa. That way, if no code actually refers to a field 680 // in either type, it's still correctly marked as unused. 681 // If a field is used in either struct, it's implicitly 682 // relevant in the other one, too. 683 // 684 // It works in a similar way for conversions between types 685 // of two packages, only that the extra information in the 686 // graph is redundant unless we're in whole program mode. 687 n := typDst.NumFields() 688 for i := 0; i < n; i++ { 689 fDst := typDst.Field(i) 690 fSrc := typSrc.Field(i) 691 c.graph.markUsedBy(fDst, fSrc) 692 c.graph.markUsedBy(fSrc, fDst) 693 } 694 } 695 } 696 697 // processCompositeLiteral marks fields as used if the struct is used 698 // in a composite literal. 699 func (c *Checker) processCompositeLiteral(pkg *lint.Pkg, node ast.Node) { 700 // XXX how does this actually work? wouldn't it match t{}? 701 if node, ok := node.(*ast.CompositeLit); ok { 702 typ := pkg.TypesInfo.TypeOf(node) 703 if _, ok := typ.(*types.Named); ok { 704 typ = typ.Underlying() 705 } 706 if _, ok := typ.(*types.Struct); !ok { 707 return 708 } 709 710 if isBasicStruct(node.Elts) { 711 c.markFields(typ) 712 } 713 } 714 } 715 716 // processCgoExported marks functions as used if they're being 717 // exported to cgo. 718 func (c *Checker) processCgoExported(pkg *lint.Pkg, node ast.Node) { 719 if node, ok := node.(*ast.FuncDecl); ok { 720 if node.Doc == nil { 721 return 722 } 723 for _, cmt := range node.Doc.List { 724 if !strings.HasPrefix(cmt.Text, "//go:cgo_export_") { 725 return 726 } 727 obj := pkg.TypesInfo.ObjectOf(node.Name) 728 c.graph.roots = append(c.graph.roots, c.graph.getNode(obj)) 729 } 730 } 731 } 732 733 func (c *Checker) processVariableDeclaration(pkg *lint.Pkg, node ast.Node) { 734 if decl, ok := node.(*ast.GenDecl); ok { 735 for _, spec := range decl.Specs { 736 spec, ok := spec.(*ast.ValueSpec) 737 if !ok { 738 continue 739 } 740 for i, name := range spec.Names { 741 if i >= len(spec.Values) { 742 break 743 } 744 value := spec.Values[i] 745 fn := func(node ast.Node) bool { 746 if node3, ok := node.(*ast.Ident); ok { 747 obj := pkg.TypesInfo.ObjectOf(node3) 748 if _, ok := obj.(*types.PkgName); ok { 749 return true 750 } 751 c.graph.markUsedBy(obj, pkg.TypesInfo.ObjectOf(name)) 752 } 753 return true 754 } 755 ast.Inspect(value, fn) 756 } 757 } 758 } 759 } 760 761 func (c *Checker) processArrayConstants(pkg *lint.Pkg, node ast.Node) { 762 if decl, ok := node.(*ast.ArrayType); ok { 763 ident, ok := decl.Len.(*ast.Ident) 764 if !ok { 765 return 766 } 767 c.graph.markUsedBy(pkg.TypesInfo.ObjectOf(ident), pkg.TypesInfo.TypeOf(decl)) 768 } 769 } 770 771 func (c *Checker) processKnownReflectMethodCallers(pkg *lint.Pkg, node ast.Node) { 772 call, ok := node.(*ast.CallExpr) 773 if !ok { 774 return 775 } 776 sel, ok := call.Fun.(*ast.SelectorExpr) 777 if !ok { 778 return 779 } 780 if !IsType(pkg.TypesInfo.TypeOf(sel.X), "*net/rpc.Server") { 781 x, ok := sel.X.(*ast.Ident) 782 if !ok { 783 return 784 } 785 pkgname, ok := pkg.TypesInfo.ObjectOf(x).(*types.PkgName) 786 if !ok { 787 return 788 } 789 if pkgname.Imported().Path() != "net/rpc" { 790 return 791 } 792 } 793 794 var arg ast.Expr 795 switch sel.Sel.Name { 796 case "Register": 797 if len(call.Args) != 1 { 798 return 799 } 800 arg = call.Args[0] 801 case "RegisterName": 802 if len(call.Args) != 2 { 803 return 804 } 805 arg = call.Args[1] 806 } 807 typ := pkg.TypesInfo.TypeOf(arg) 808 ms := types.NewMethodSet(typ) 809 for i := 0; i < ms.Len(); i++ { 810 c.graph.markUsedBy(ms.At(i).Obj(), typ) 811 } 812 } 813 814 func (c *Checker) processAST(pkg *lint.Pkg) { 815 fn := func(node ast.Node) bool { 816 c.processConversion(pkg, node) 817 c.processKnownReflectMethodCallers(pkg, node) 818 c.processCompositeLiteral(pkg, node) 819 c.processCgoExported(pkg, node) 820 c.processVariableDeclaration(pkg, node) 821 c.processArrayConstants(pkg, node) 822 return true 823 } 824 for _, file := range pkg.Syntax { 825 ast.Inspect(file, fn) 826 } 827 } 828 829 func isBasicStruct(elts []ast.Expr) bool { 830 for _, elt := range elts { 831 if _, ok := elt.(*ast.KeyValueExpr); !ok { 832 return true 833 } 834 } 835 return false 836 } 837 838 func isPkgScope(obj types.Object) bool { 839 return obj.Parent() == obj.Pkg().Scope() 840 } 841 842 func isMain(obj types.Object) bool { 843 if obj.Pkg().Name() != "main" { 844 return false 845 } 846 if obj.Name() != "main" { 847 return false 848 } 849 if !isPkgScope(obj) { 850 return false 851 } 852 if !isFunction(obj) { 853 return false 854 } 855 if isMethod(obj) { 856 return false 857 } 858 return true 859 } 860 861 func isFunction(obj types.Object) bool { 862 _, ok := obj.(*types.Func) 863 return ok 864 } 865 866 func isMethod(obj types.Object) bool { 867 if !isFunction(obj) { 868 return false 869 } 870 return obj.(*types.Func).Type().(*types.Signature).Recv() != nil 871 } 872 873 func isVariable(obj types.Object) bool { 874 _, ok := obj.(*types.Var) 875 return ok 876 } 877 878 func isConstant(obj types.Object) bool { 879 _, ok := obj.(*types.Const) 880 return ok 881 } 882 883 func isType(obj types.Object) bool { 884 _, ok := obj.(*types.TypeName) 885 return ok 886 } 887 888 func isField(obj types.Object) bool { 889 if obj, ok := obj.(*types.Var); ok && obj.IsField() { 890 return true 891 } 892 return false 893 } 894 895 func (c *Checker) checkFlags(v interface{}) bool { 896 obj, ok := v.(types.Object) 897 if !ok { 898 return false 899 } 900 if isFunction(obj) && !c.checkFunctions() { 901 return false 902 } 903 if isVariable(obj) && !c.checkVariables() { 904 return false 905 } 906 if isConstant(obj) && !c.checkConstants() { 907 return false 908 } 909 if isType(obj) && !c.checkTypes() { 910 return false 911 } 912 if isField(obj) && !c.checkFields() { 913 return false 914 } 915 return true 916 } 917 918 func (c *Checker) isRoot(obj types.Object) bool { 919 // - in local mode, main, init, tests, and non-test, non-main exported are roots 920 // - in global mode (not yet implemented), main, init and tests are roots 921 922 if _, ok := obj.(*types.PkgName); ok { 923 return true 924 } 925 926 if isMain(obj) || (isFunction(obj) && !isMethod(obj) && obj.Name() == "init") { 927 return true 928 } 929 if obj.Exported() { 930 f := c.prog.Fset().Position(obj.Pos()).Filename 931 if strings.HasSuffix(f, "_test.go") { 932 return strings.HasPrefix(obj.Name(), "Test") || 933 strings.HasPrefix(obj.Name(), "Benchmark") || 934 strings.HasPrefix(obj.Name(), "Example") 935 } 936 937 // Package-level are used, except in package main 938 if isPkgScope(obj) && obj.Pkg().Name() != "main" && !c.WholeProgram { 939 return true 940 } 941 } 942 return false 943 } 944 945 func markNodesUsed(nodes map[*graphNode]struct{}) { 946 for node := range nodes { 947 wasUsed := node.used 948 node.used = true 949 if !wasUsed { 950 markNodesUsed(node.uses) 951 } 952 } 953 } 954 955 // deduplicate merges objects based on their positions. This is done 956 // to work around packages existing multiple times in go/packages. 957 func (c *Checker) deduplicate() { 958 m := map[token.Position]struct{ used, quiet bool }{} 959 for _, node := range c.graph.nodes { 960 obj, ok := node.obj.(types.Object) 961 if !ok { 962 continue 963 } 964 pos := c.prog.Fset().Position(obj.Pos()) 965 m[pos] = struct{ used, quiet bool }{ 966 m[pos].used || node.used, 967 m[pos].quiet || node.quiet, 968 } 969 } 970 971 for _, node := range c.graph.nodes { 972 obj, ok := node.obj.(types.Object) 973 if !ok { 974 continue 975 } 976 pos := c.prog.Fset().Position(obj.Pos()) 977 node.used = m[pos].used 978 node.quiet = m[pos].quiet 979 } 980 } 981 982 func (c *Checker) markNodesQuiet() { 983 for _, node := range c.graph.nodes { 984 if node.used { 985 continue 986 } 987 if obj, ok := node.obj.(types.Object); ok && !c.checkFlags(obj) { 988 node.quiet = true 989 continue 990 } 991 c.markObjQuiet(node.obj) 992 } 993 } 994 995 func (c *Checker) markObjQuiet(obj interface{}) { 996 switch obj := obj.(type) { 997 case *types.Named: 998 n := obj.NumMethods() 999 for i := 0; i < n; i++ { 1000 meth := obj.Method(i) 1001 node := c.graph.getNode(meth) 1002 node.quiet = true 1003 c.markObjQuiet(meth.Scope()) 1004 } 1005 case *types.Struct: 1006 n := obj.NumFields() 1007 for i := 0; i < n; i++ { 1008 field := obj.Field(i) 1009 c.graph.nodes[field].quiet = true 1010 } 1011 case *types.Func: 1012 c.markObjQuiet(obj.Scope()) 1013 case *types.Scope: 1014 if obj == nil { 1015 return 1016 } 1017 if obj.Parent() == types.Universe { 1018 return 1019 } 1020 for _, name := range obj.Names() { 1021 v := obj.Lookup(name) 1022 if n, ok := c.graph.nodes[v]; ok { 1023 n.quiet = true 1024 } 1025 } 1026 n := obj.NumChildren() 1027 for i := 0; i < n; i++ { 1028 c.markObjQuiet(obj.Child(i)) 1029 } 1030 } 1031 } 1032 1033 func getField(typ types.Type, idx int) *types.Var { 1034 switch obj := typ.(type) { 1035 case *types.Pointer: 1036 return getField(obj.Elem(), idx) 1037 case *types.Named: 1038 switch v := obj.Underlying().(type) { 1039 case *types.Struct: 1040 return v.Field(idx) 1041 case *types.Pointer: 1042 return getField(v.Elem(), idx) 1043 default: 1044 panic(fmt.Sprintf("unexpected type %s", typ)) 1045 } 1046 case *types.Struct: 1047 return obj.Field(idx) 1048 } 1049 return nil 1050 } 1051 1052 func (c *Checker) topmostScope(scope *types.Scope, pkg *types.Package) (ret *types.Scope) { 1053 if top, ok := c.topmostCache[scope]; ok { 1054 return top 1055 } 1056 defer func() { 1057 c.topmostCache[scope] = ret 1058 }() 1059 if scope == pkg.Scope() { 1060 return scope 1061 } 1062 if scope.Parent().Parent() == pkg.Scope() { 1063 return scope 1064 } 1065 return c.topmostScope(scope.Parent(), pkg) 1066 } 1067 1068 func (c *Checker) printDebugGraph(w io.Writer) { 1069 fmt.Fprintln(w, "digraph {") 1070 fmt.Fprintln(w, "n0 [label = roots]") 1071 for _, node := range c.graph.nodes { 1072 s := fmt.Sprintf("%s (%T)", node.obj, node.obj) 1073 s = strings.Replace(s, "\n", "", -1) 1074 s = strings.Replace(s, `"`, "", -1) 1075 fmt.Fprintf(w, `n%d [label = %q]`, node.n, s) 1076 color := "black" 1077 switch { 1078 case node.used: 1079 color = "green" 1080 case node.quiet: 1081 color = "orange" 1082 case !c.checkFlags(node.obj): 1083 color = "purple" 1084 default: 1085 color = "red" 1086 } 1087 fmt.Fprintf(w, "[color = %s]", color) 1088 fmt.Fprintln(w) 1089 } 1090 1091 for _, node1 := range c.graph.nodes { 1092 for node2 := range node1.uses { 1093 fmt.Fprintf(w, "n%d -> n%d\n", node1.n, node2.n) 1094 } 1095 } 1096 for _, root := range c.graph.roots { 1097 fmt.Fprintf(w, "n0 -> n%d\n", root.n) 1098 } 1099 fmt.Fprintln(w, "}") 1100 }