@@ -24,12 +24,9 @@ import (
2424 "golang.org/x/tools/go/callgraph"
2525 "golang.org/x/tools/go/callgraph/cha"
2626 "golang.org/x/tools/go/callgraph/rta"
27- "golang.org/x/tools/go/loader "
27+ "golang.org/x/tools/go/packages "
2828 "golang.org/x/tools/go/ssa"
2929 "golang.org/x/tools/go/ssa/ssautil"
30-
31- "github.com/kisielk/gotool"
32- "mvdan.cc/lint"
3330)
3431
3532// UnusedParams returns a list of human-readable issues that point out unused
@@ -55,7 +52,7 @@ func UnusedParams(tests bool, algo string, exported, debug bool, args ...string)
5552// UnusedParams instead, unless you want to use a *loader.Program and
5653// *ssa.Program directly.
5754type Checker struct {
58- lprog * loader. Program
55+ pkgs [] * packages. Package
5956 prog * ssa.Program
6057 graph * callgraph.Graph
6158
@@ -66,48 +63,53 @@ type Checker struct {
6663 exported bool
6764 debugLog io.Writer
6865
69- issues []lint. Issue
66+ issues []Issue
7067
7168 cachedDeclCounts map [string ]map [string ]int
7269
7370 callByPos map [token.Pos ]* ast.CallExpr
7471}
7572
76- var (
77- _ lint.Checker = (* Checker )(nil )
78- _ lint.WithSSA = (* Checker )(nil )
79-
80- errorType = types .Universe .Lookup ("error" ).Type ()
81- )
73+ var errorType = types .Universe .Lookup ("error" ).Type ()
8274
8375// lines runs the checker and returns the list of readable issues.
8476func (c * Checker ) lines (args ... string ) ([]string , error ) {
85- paths := gotool . ImportPaths ( args )
86- conf := loader. Config {
87- ParserMode : parser . ParseComments ,
77+ cfg := & packages. Config {
78+ Mode : packages . LoadSyntax ,
79+ Tests : c . tests ,
8880 }
89- if _ , err := conf .FromArgs (paths , c .tests ); err != nil {
90- return nil , err
91- }
92- lprog , err := conf .Load ()
81+ pkgs , err := packages .Load (cfg , args ... )
9382 if err != nil {
9483 return nil , err
9584 }
96- prog := ssautil .CreateProgram (lprog , 0 )
85+ if packages .PrintErrors (pkgs ) > 0 {
86+ return nil , fmt .Errorf ("encountered errors" )
87+ }
88+
89+ prog , _ := ssautil .Packages (pkgs , 0 )
9790 prog .Build ()
98- c .Program ( lprog )
91+ c .Packages ( pkgs )
9992 c .ProgramSSA (prog )
10093 issues , err := c .Check ()
10194 if err != nil {
10295 return nil , err
10396 }
104- lines := make ([]string , len (issues ))
105- for i , issue := range issues {
97+ lines := make ([]string , 0 , len (issues ))
98+ prevLine := ""
99+ for _ , issue := range issues {
106100 fpos := prog .Fset .Position (issue .Pos ()).String ()
107101 if strings .HasPrefix (fpos , c .wd ) {
108102 fpos = fpos [len (c .wd )+ 1 :]
109103 }
110- lines [i ] = fmt .Sprintf ("%s: %s" , fpos , issue .Message ())
104+ line := fmt .Sprintf ("%s: %s" , fpos , issue .Message ())
105+ if line == prevLine {
106+ // Deduplicate lines, since we may look at the same
107+ // package multiple times if tests are involved.
108+ // TODO: is there a better way to handle this?
109+ continue
110+ }
111+ prevLine = line
112+ lines = append (lines , fmt .Sprintf ("%s: %s" , fpos , issue .Message ()))
111113 }
112114 return lines , nil
113115}
@@ -123,8 +125,8 @@ func (i Issue) Pos() token.Pos { return i.pos }
123125func (i Issue ) Message () string { return i .fname + " - " + i .msg }
124126
125127// Program supplies Checker with the needed *loader.Program.
126- func (c * Checker ) Program ( lprog * loader. Program ) {
127- c .lprog = lprog
128+ func (c * Checker ) Packages ( pkgs [] * packages. Package ) {
129+ c .pkgs = pkgs
128130}
129131
130132// ProgramSSA supplies Checker with the needed *ssa.Program.
@@ -173,14 +175,15 @@ var stdSizes = types.SizesFor("gc", "amd64")
173175
174176// Check runs the unused parameter check and returns the list of found issues,
175177// and any error encountered.
176- func (c * Checker ) Check () ([]lint. Issue , error ) {
178+ func (c * Checker ) Check () ([]Issue , error ) {
177179 c .cachedDeclCounts = make (map [string ]map [string ]int )
178180 c .callByPos = make (map [token.Pos ]* ast.CallExpr )
179- wantPkg := make (map [* types.Package ]* loader. PackageInfo )
181+ wantPkg := make (map [* types.Package ]* packages. Package )
180182 genFiles := make (map [string ]bool )
181- for _ , info := range c .lprog .InitialPackages () {
182- wantPkg [info .Pkg ] = info
183- for _ , f := range info .Files {
183+ for _ , pkg := range c .pkgs {
184+ fmt .Println (pkg )
185+ wantPkg [pkg .Types ] = pkg
186+ for _ , f := range pkg .Syntax {
184187 if len (f .Comments ) > 0 && generatedDoc (f .Comments [0 ].Text ()) {
185188 fname := c .prog .Fset .Position (f .Pos ()).Filename
186189 genFiles [fname ] = true
@@ -221,8 +224,8 @@ func (c *Checker) Check() ([]lint.Issue, error) {
221224 case len (fn .Blocks ) == 0 : // stub
222225 continue
223226 }
224- pkgInfo := wantPkg [fn .Pkg .Pkg ]
225- if pkgInfo == nil { // not part of given pkgs
227+ pkg := wantPkg [fn .Pkg .Pkg ]
228+ if pkg == nil { // not part of given pkgs
226229 continue
227230 }
228231 if c .exported || fn .Pkg .Pkg .Name () == "main" {
@@ -238,7 +241,7 @@ func (c *Checker) Check() ([]lint.Issue, error) {
238241 continue // generated file
239242 }
240243
241- c .checkFunc (fn , pkgInfo )
244+ c .checkFunc (fn , pkg )
242245 }
243246 sort .Slice (c .issues , func (i , j int ) bool {
244247 p1 := c .prog .Fset .Position (c .issues [i ].Pos ())
@@ -269,7 +272,7 @@ func constValueString(cnst *ssa.Const) string {
269272}
270273
271274// checkFunc checks a single function for unused parameters.
272- func (c * Checker ) checkFunc (fn * ssa.Function , pkgInfo * loader. PackageInfo ) {
275+ func (c * Checker ) checkFunc (fn * ssa.Function , pkg * packages. Package ) {
273276 c .debug ("func %s\n " , fn .RelString (fn .Package ().Pkg ))
274277 if dummyImpl (fn .Blocks [0 ]) { // panic implementation
275278 c .debug (" skip - dummy implementation\n " )
@@ -283,7 +286,7 @@ func (c *Checker) checkFunc(fn *ssa.Function, pkgInfo *loader.PackageInfo) {
283286 c .debug (" skip - type is required via call\n " )
284287 return
285288 }
286- if c .multipleImpls (pkgInfo , fn ) {
289+ if c .multipleImpls (pkg , fn ) {
287290 c .debug (" skip - multiple implementations via build tags\n " )
288291 return
289292 }
@@ -395,7 +398,7 @@ resLoop:
395398}
396399
397400// mainPackages returns the subset of main packages within pkgSet.
398- func mainPackages (prog * ssa.Program , pkgSet map [* types.Package ]* loader. PackageInfo ) ([]* ssa.Package , error ) {
401+ func mainPackages (prog * ssa.Program , pkgSet map [* types.Package ]* packages. Package ) ([]* ssa.Package , error ) {
399402 mains := make ([]* ssa.Package , 0 , len (pkgSet ))
400403 for tpkg := range pkgSet {
401404 pkg := prog .Package (tpkg )
@@ -641,6 +644,11 @@ func (c *Checker) declCounts(pkgDir string, pkgName string) map[string]int {
641644 c .cachedDeclCounts [pkgDir ] = nil
642645 return nil
643646 }
647+ if len (pkgs ) == 0 {
648+ // TODO: investigate why this started happening after switching
649+ // to go/packages
650+ return nil
651+ }
644652 pkg := pkgs [pkgName ]
645653 count := make (map [string ]int )
646654 for _ , file := range pkg .Files {
@@ -682,12 +690,12 @@ func recvPrefix(recv *ast.FieldList) string {
682690// source code. For example, if there are different function bodies depending on
683691// the operating system or architecture. That tends to mean that an unused
684692// parameter in one implementation may not be unused in another.
685- func (c * Checker ) multipleImpls (info * loader. PackageInfo , fn * ssa.Function ) bool {
693+ func (c * Checker ) multipleImpls (pkg * packages. Package , fn * ssa.Function ) bool {
686694 if fn .Parent () != nil { // nested func
687695 return false
688696 }
689697 path := c .prog .Fset .Position (fn .Pos ()).Filename
690- count := c .declCounts (filepath .Dir (path ), info . Pkg .Name ())
698+ count := c .declCounts (filepath .Dir (path ), pkg . Types .Name ())
691699 name := fn .Name ()
692700 if fn .Signature .Recv () != nil {
693701 tp := fn .Params [0 ].Type ()
0 commit comments