gitee.com/lonely0422/gometalinter.git@v3.0.1-0.20190307123442-32416ab75314+incompatible/_linters/src/honnef.co/go/tools/stylecheck/lint.go (about)

     1  package stylecheck // import "honnef.co/go/tools/stylecheck"
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/constant"
     7  	"go/token"
     8  	"go/types"
     9  	"strconv"
    10  	"strings"
    11  	"unicode"
    12  	"unicode/utf8"
    13  
    14  	"honnef.co/go/tools/lint"
    15  	. "honnef.co/go/tools/lint/lintdsl"
    16  	"honnef.co/go/tools/ssa"
    17  
    18  	"golang.org/x/tools/go/types/typeutil"
    19  )
    20  
    21  type Checker struct {
    22  	CheckGenerated bool
    23  }
    24  
    25  func NewChecker() *Checker {
    26  	return &Checker{}
    27  }
    28  
    29  func (*Checker) Name() string              { return "stylecheck" }
    30  func (*Checker) Prefix() string            { return "ST" }
    31  func (c *Checker) Init(prog *lint.Program) {}
    32  
    33  func (c *Checker) Checks() []lint.Check {
    34  	return []lint.Check{
    35  		{ID: "ST1000", FilterGenerated: false, Fn: c.CheckPackageComment},
    36  		{ID: "ST1001", FilterGenerated: true, Fn: c.CheckDotImports},
    37  		// {ID: "ST1002", FilterGenerated: true, Fn: c.CheckBlankImports},
    38  		{ID: "ST1003", FilterGenerated: true, Fn: c.CheckNames},
    39  		// {ID: "ST1004", FilterGenerated: false, Fn: nil, 			  },
    40  		{ID: "ST1005", FilterGenerated: false, Fn: c.CheckErrorStrings},
    41  		{ID: "ST1006", FilterGenerated: false, Fn: c.CheckReceiverNames},
    42  		// {ID: "ST1007", FilterGenerated: true, Fn: c.CheckIncDec},
    43  		{ID: "ST1008", FilterGenerated: false, Fn: c.CheckErrorReturn},
    44  		// {ID: "ST1009", FilterGenerated: false, Fn: c.CheckUnexportedReturn},
    45  		// {ID: "ST1010", FilterGenerated: false, Fn: c.CheckContextFirstArg},
    46  		{ID: "ST1011", FilterGenerated: false, Fn: c.CheckTimeNames},
    47  		{ID: "ST1012", FilterGenerated: false, Fn: c.CheckErrorVarNames},
    48  		{ID: "ST1013", FilterGenerated: true, Fn: c.CheckHTTPStatusCodes},
    49  		{ID: "ST1015", FilterGenerated: true, Fn: c.CheckDefaultCaseOrder},
    50  		{ID: "ST1016", FilterGenerated: false, Fn: c.CheckReceiverNamesIdentical},
    51  		{ID: "ST1017", FilterGenerated: true, Fn: c.CheckYodaConditions},
    52  	}
    53  }
    54  
    55  func (c *Checker) CheckPackageComment(j *lint.Job) {
    56  	// - At least one file in a non-main package should have a package comment
    57  	//
    58  	// - The comment should be of the form
    59  	// "Package x ...". This has a slight potential for false
    60  	// positives, as multiple files can have package comments, in
    61  	// which case they get appended. But that doesn't happen a lot in
    62  	// the real world.
    63  
    64  	for _, pkg := range j.Program.InitialPackages {
    65  		if pkg.Name == "main" {
    66  			continue
    67  		}
    68  		hasDocs := false
    69  		for _, f := range pkg.Syntax {
    70  			if IsInTest(j, f) {
    71  				continue
    72  			}
    73  			if f.Doc != nil && len(f.Doc.List) > 0 {
    74  				hasDocs = true
    75  				prefix := "Package " + f.Name.Name + " "
    76  				if !strings.HasPrefix(strings.TrimSpace(f.Doc.Text()), prefix) {
    77  					j.Errorf(f.Doc, `package comment should be of the form "%s..."`, prefix)
    78  				}
    79  				f.Doc.Text()
    80  			}
    81  		}
    82  
    83  		if !hasDocs {
    84  			for _, f := range pkg.Syntax {
    85  				if IsInTest(j, f) {
    86  					continue
    87  				}
    88  				j.Errorf(f, "at least one file in a package should have a package comment")
    89  			}
    90  		}
    91  	}
    92  }
    93  
    94  func (c *Checker) CheckDotImports(j *lint.Job) {
    95  	for _, pkg := range j.Program.InitialPackages {
    96  		for _, f := range pkg.Syntax {
    97  		imports:
    98  			for _, imp := range f.Imports {
    99  				path := imp.Path.Value
   100  				path = path[1 : len(path)-1]
   101  				for _, w := range pkg.Config.DotImportWhitelist {
   102  					if w == path {
   103  						continue imports
   104  					}
   105  				}
   106  
   107  				if imp.Name != nil && imp.Name.Name == "." && !IsInTest(j, f) {
   108  					j.Errorf(imp, "should not use dot imports")
   109  				}
   110  			}
   111  		}
   112  	}
   113  }
   114  
   115  func (c *Checker) CheckBlankImports(j *lint.Job) {
   116  	fset := j.Program.Fset()
   117  	for _, f := range j.Program.Files {
   118  		if IsInMain(j, f) || IsInTest(j, f) {
   119  			continue
   120  		}
   121  
   122  		// Collect imports of the form `import _ "foo"`, i.e. with no
   123  		// parentheses, as their comment will be associated with the
   124  		// (paren-free) GenDecl, not the import spec itself.
   125  		//
   126  		// We don't directly process the GenDecl so that we can
   127  		// correctly handle the following:
   128  		//
   129  		//  import _ "foo"
   130  		//  import _ "bar"
   131  		//
   132  		// where only the first import should get flagged.
   133  		skip := map[ast.Spec]bool{}
   134  		ast.Inspect(f, func(node ast.Node) bool {
   135  			switch node := node.(type) {
   136  			case *ast.File:
   137  				return true
   138  			case *ast.GenDecl:
   139  				if node.Tok != token.IMPORT {
   140  					return false
   141  				}
   142  				if node.Lparen == token.NoPos && node.Doc != nil {
   143  					skip[node.Specs[0]] = true
   144  				}
   145  				return false
   146  			}
   147  			return false
   148  		})
   149  		for i, imp := range f.Imports {
   150  			pos := fset.Position(imp.Pos())
   151  
   152  			if !IsBlank(imp.Name) {
   153  				continue
   154  			}
   155  			// Only flag the first blank import in a group of imports,
   156  			// or don't flag any of them, if the first one is
   157  			// commented
   158  			if i > 0 {
   159  				prev := f.Imports[i-1]
   160  				prevPos := fset.Position(prev.Pos())
   161  				if pos.Line-1 == prevPos.Line && IsBlank(prev.Name) {
   162  					continue
   163  				}
   164  			}
   165  
   166  			if imp.Doc == nil && imp.Comment == nil && !skip[imp] {
   167  				j.Errorf(imp, "a blank import should be only in a main or test package, or have a comment justifying it")
   168  			}
   169  		}
   170  	}
   171  }
   172  
   173  func (c *Checker) CheckIncDec(j *lint.Job) {
   174  	// TODO(dh): this can be noisy for function bodies that look like this:
   175  	// 	x += 3
   176  	// 	...
   177  	// 	x += 2
   178  	// 	...
   179  	// 	x += 1
   180  	fn := func(node ast.Node) bool {
   181  		assign, ok := node.(*ast.AssignStmt)
   182  		if !ok || (assign.Tok != token.ADD_ASSIGN && assign.Tok != token.SUB_ASSIGN) {
   183  			return true
   184  		}
   185  		if (len(assign.Lhs) != 1 || len(assign.Rhs) != 1) ||
   186  			!IsIntLiteral(assign.Rhs[0], "1") {
   187  			return true
   188  		}
   189  
   190  		suffix := ""
   191  		switch assign.Tok {
   192  		case token.ADD_ASSIGN:
   193  			suffix = "++"
   194  		case token.SUB_ASSIGN:
   195  			suffix = "--"
   196  		}
   197  
   198  		j.Errorf(assign, "should replace %s with %s%s", Render(j, assign), Render(j, assign.Lhs[0]), suffix)
   199  		return true
   200  	}
   201  	for _, f := range j.Program.Files {
   202  		ast.Inspect(f, fn)
   203  	}
   204  }
   205  
   206  func (c *Checker) CheckErrorReturn(j *lint.Job) {
   207  fnLoop:
   208  	for _, fn := range j.Program.InitialFunctions {
   209  		sig := fn.Type().(*types.Signature)
   210  		rets := sig.Results()
   211  		if rets == nil || rets.Len() < 2 {
   212  			continue
   213  		}
   214  
   215  		if rets.At(rets.Len()-1).Type() == types.Universe.Lookup("error").Type() {
   216  			// Last return type is error. If the function also returns
   217  			// errors in other positions, that's fine.
   218  			continue
   219  		}
   220  		for i := rets.Len() - 2; i >= 0; i-- {
   221  			if rets.At(i).Type() == types.Universe.Lookup("error").Type() {
   222  				j.Errorf(rets.At(i), "error should be returned as the last argument")
   223  				continue fnLoop
   224  			}
   225  		}
   226  	}
   227  }
   228  
   229  // CheckUnexportedReturn checks that exported functions on exported
   230  // types do not return unexported types.
   231  func (c *Checker) CheckUnexportedReturn(j *lint.Job) {
   232  	for _, fn := range j.Program.InitialFunctions {
   233  		if fn.Synthetic != "" || fn.Parent() != nil {
   234  			continue
   235  		}
   236  		if !ast.IsExported(fn.Name()) || IsInMain(j, fn) || IsInTest(j, fn) {
   237  			continue
   238  		}
   239  		sig := fn.Type().(*types.Signature)
   240  		if sig.Recv() != nil && !ast.IsExported(Dereference(sig.Recv().Type()).(*types.Named).Obj().Name()) {
   241  			continue
   242  		}
   243  		res := sig.Results()
   244  		for i := 0; i < res.Len(); i++ {
   245  			if named, ok := DereferenceR(res.At(i).Type()).(*types.Named); ok &&
   246  				!ast.IsExported(named.Obj().Name()) &&
   247  				named != types.Universe.Lookup("error").Type() {
   248  				j.Errorf(fn, "should not return unexported type")
   249  			}
   250  		}
   251  	}
   252  }
   253  
   254  func (c *Checker) CheckReceiverNames(j *lint.Job) {
   255  	for _, pkg := range j.Program.InitialPackages {
   256  		for _, m := range pkg.SSA.Members {
   257  			if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() {
   258  				ms := typeutil.IntuitiveMethodSet(T.Type(), nil)
   259  				for _, sel := range ms {
   260  					fn := sel.Obj().(*types.Func)
   261  					recv := fn.Type().(*types.Signature).Recv()
   262  					if Dereference(recv.Type()) != T.Type() {
   263  						// skip embedded methods
   264  						continue
   265  					}
   266  					if recv.Name() == "self" || recv.Name() == "this" {
   267  						j.Errorf(recv, `receiver name should be a reflection of its identity; don't use generic names such as "this" or "self"`)
   268  					}
   269  					if recv.Name() == "_" {
   270  						j.Errorf(recv, "receiver name should not be an underscore, omit the name if it is unused")
   271  					}
   272  				}
   273  			}
   274  		}
   275  	}
   276  }
   277  
   278  func (c *Checker) CheckReceiverNamesIdentical(j *lint.Job) {
   279  	for _, pkg := range j.Program.InitialPackages {
   280  		for _, m := range pkg.SSA.Members {
   281  			names := map[string]int{}
   282  
   283  			var firstFn *types.Func
   284  			if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() {
   285  				ms := typeutil.IntuitiveMethodSet(T.Type(), nil)
   286  				for _, sel := range ms {
   287  					fn := sel.Obj().(*types.Func)
   288  					recv := fn.Type().(*types.Signature).Recv()
   289  					if Dereference(recv.Type()) != T.Type() {
   290  						// skip embedded methods
   291  						continue
   292  					}
   293  					if firstFn == nil {
   294  						firstFn = fn
   295  					}
   296  					if recv.Name() != "" && recv.Name() != "_" {
   297  						names[recv.Name()]++
   298  					}
   299  				}
   300  			}
   301  
   302  			if len(names) > 1 {
   303  				var seen []string
   304  				for name, count := range names {
   305  					seen = append(seen, fmt.Sprintf("%dx %q", count, name))
   306  				}
   307  
   308  				j.Errorf(firstFn, "methods on the same type should have the same receiver name (seen %s)", strings.Join(seen, ", "))
   309  			}
   310  		}
   311  	}
   312  }
   313  
   314  func (c *Checker) CheckContextFirstArg(j *lint.Job) {
   315  	// TODO(dh): this check doesn't apply to test helpers. Example from the stdlib:
   316  	// 	func helperCommandContext(t *testing.T, ctx context.Context, s ...string) (cmd *exec.Cmd) {
   317  fnLoop:
   318  	for _, fn := range j.Program.InitialFunctions {
   319  		if fn.Synthetic != "" || fn.Parent() != nil {
   320  			continue
   321  		}
   322  		params := fn.Signature.Params()
   323  		if params.Len() < 2 {
   324  			continue
   325  		}
   326  		if types.TypeString(params.At(0).Type(), nil) == "context.Context" {
   327  			continue
   328  		}
   329  		for i := 1; i < params.Len(); i++ {
   330  			param := params.At(i)
   331  			if types.TypeString(param.Type(), nil) == "context.Context" {
   332  				j.Errorf(param, "context.Context should be the first argument of a function")
   333  				continue fnLoop
   334  			}
   335  		}
   336  	}
   337  }
   338  
   339  func (c *Checker) CheckErrorStrings(j *lint.Job) {
   340  	fnNames := map[*ssa.Package]map[string]bool{}
   341  	for _, fn := range j.Program.InitialFunctions {
   342  		m := fnNames[fn.Package()]
   343  		if m == nil {
   344  			m = map[string]bool{}
   345  			fnNames[fn.Package()] = m
   346  		}
   347  		m[fn.Name()] = true
   348  	}
   349  
   350  	for _, fn := range j.Program.InitialFunctions {
   351  		if IsInTest(j, fn) {
   352  			// We don't care about malformed error messages in tests;
   353  			// they're usually for direct human consumption, not part
   354  			// of an API
   355  			continue
   356  		}
   357  		for _, block := range fn.Blocks {
   358  		instrLoop:
   359  			for _, ins := range block.Instrs {
   360  				call, ok := ins.(*ssa.Call)
   361  				if !ok {
   362  					continue
   363  				}
   364  				if !IsCallTo(call.Common(), "errors.New") && !IsCallTo(call.Common(), "fmt.Errorf") {
   365  					continue
   366  				}
   367  
   368  				k, ok := call.Common().Args[0].(*ssa.Const)
   369  				if !ok {
   370  					continue
   371  				}
   372  
   373  				s := constant.StringVal(k.Value)
   374  				if len(s) == 0 {
   375  					continue
   376  				}
   377  				switch s[len(s)-1] {
   378  				case '.', ':', '!', '\n':
   379  					j.Errorf(call, "error strings should not end with punctuation or a newline")
   380  				}
   381  				idx := strings.IndexByte(s, ' ')
   382  				if idx == -1 {
   383  					// single word error message, probably not a real
   384  					// error but something used in tests or during
   385  					// debugging
   386  					continue
   387  				}
   388  				word := s[:idx]
   389  				first, n := utf8.DecodeRuneInString(word)
   390  				if !unicode.IsUpper(first) {
   391  					continue
   392  				}
   393  				for _, c := range word[n:] {
   394  					if unicode.IsUpper(c) {
   395  						// Word is probably an initialism or
   396  						// multi-word function name
   397  						continue instrLoop
   398  					}
   399  				}
   400  
   401  				word = strings.TrimRightFunc(word, func(r rune) bool { return unicode.IsPunct(r) })
   402  				if fnNames[fn.Package()][word] {
   403  					// Word is probably the name of a function in this package
   404  					continue
   405  				}
   406  				// First word in error starts with a capital
   407  				// letter, and the word doesn't contain any other
   408  				// capitals, making it unlikely to be an
   409  				// initialism or multi-word function name.
   410  				//
   411  				// It could still be a proper noun, though.
   412  
   413  				j.Errorf(call, "error strings should not be capitalized")
   414  			}
   415  		}
   416  	}
   417  }
   418  
   419  func (c *Checker) CheckTimeNames(j *lint.Job) {
   420  	suffixes := []string{
   421  		"Sec", "Secs", "Seconds",
   422  		"Msec", "Msecs",
   423  		"Milli", "Millis", "Milliseconds",
   424  		"Usec", "Usecs", "Microseconds",
   425  		"MS", "Ms",
   426  	}
   427  	fn := func(T types.Type, names []*ast.Ident) {
   428  		if !IsType(T, "time.Duration") && !IsType(T, "*time.Duration") {
   429  			return
   430  		}
   431  		for _, name := range names {
   432  			for _, suffix := range suffixes {
   433  				if strings.HasSuffix(name.Name, suffix) {
   434  					j.Errorf(name, "var %s is of type %v; don't use unit-specific suffix %q", name.Name, T, suffix)
   435  					break
   436  				}
   437  			}
   438  		}
   439  	}
   440  	for _, f := range j.Program.Files {
   441  		ast.Inspect(f, func(node ast.Node) bool {
   442  			switch node := node.(type) {
   443  			case *ast.ValueSpec:
   444  				T := TypeOf(j, node.Type)
   445  				fn(T, node.Names)
   446  			case *ast.FieldList:
   447  				for _, field := range node.List {
   448  					T := TypeOf(j, field.Type)
   449  					fn(T, field.Names)
   450  				}
   451  			}
   452  			return true
   453  		})
   454  	}
   455  }
   456  
   457  func (c *Checker) CheckErrorVarNames(j *lint.Job) {
   458  	for _, f := range j.Program.Files {
   459  		for _, decl := range f.Decls {
   460  			gen, ok := decl.(*ast.GenDecl)
   461  			if !ok || gen.Tok != token.VAR {
   462  				continue
   463  			}
   464  			for _, spec := range gen.Specs {
   465  				spec := spec.(*ast.ValueSpec)
   466  				if len(spec.Names) != len(spec.Values) {
   467  					continue
   468  				}
   469  
   470  				for i, name := range spec.Names {
   471  					val := spec.Values[i]
   472  					if !IsCallToAST(j, val, "errors.New") && !IsCallToAST(j, val, "fmt.Errorf") {
   473  						continue
   474  					}
   475  
   476  					prefix := "err"
   477  					if name.IsExported() {
   478  						prefix = "Err"
   479  					}
   480  					if !strings.HasPrefix(name.Name, prefix) {
   481  						j.Errorf(name, "error var %s should have name of the form %sFoo", name.Name, prefix)
   482  					}
   483  				}
   484  			}
   485  		}
   486  	}
   487  }
   488  
   489  var httpStatusCodes = map[int]string{
   490  	100: "StatusContinue",
   491  	101: "StatusSwitchingProtocols",
   492  	102: "StatusProcessing",
   493  	200: "StatusOK",
   494  	201: "StatusCreated",
   495  	202: "StatusAccepted",
   496  	203: "StatusNonAuthoritativeInfo",
   497  	204: "StatusNoContent",
   498  	205: "StatusResetContent",
   499  	206: "StatusPartialContent",
   500  	207: "StatusMultiStatus",
   501  	208: "StatusAlreadyReported",
   502  	226: "StatusIMUsed",
   503  	300: "StatusMultipleChoices",
   504  	301: "StatusMovedPermanently",
   505  	302: "StatusFound",
   506  	303: "StatusSeeOther",
   507  	304: "StatusNotModified",
   508  	305: "StatusUseProxy",
   509  	307: "StatusTemporaryRedirect",
   510  	308: "StatusPermanentRedirect",
   511  	400: "StatusBadRequest",
   512  	401: "StatusUnauthorized",
   513  	402: "StatusPaymentRequired",
   514  	403: "StatusForbidden",
   515  	404: "StatusNotFound",
   516  	405: "StatusMethodNotAllowed",
   517  	406: "StatusNotAcceptable",
   518  	407: "StatusProxyAuthRequired",
   519  	408: "StatusRequestTimeout",
   520  	409: "StatusConflict",
   521  	410: "StatusGone",
   522  	411: "StatusLengthRequired",
   523  	412: "StatusPreconditionFailed",
   524  	413: "StatusRequestEntityTooLarge",
   525  	414: "StatusRequestURITooLong",
   526  	415: "StatusUnsupportedMediaType",
   527  	416: "StatusRequestedRangeNotSatisfiable",
   528  	417: "StatusExpectationFailed",
   529  	418: "StatusTeapot",
   530  	422: "StatusUnprocessableEntity",
   531  	423: "StatusLocked",
   532  	424: "StatusFailedDependency",
   533  	426: "StatusUpgradeRequired",
   534  	428: "StatusPreconditionRequired",
   535  	429: "StatusTooManyRequests",
   536  	431: "StatusRequestHeaderFieldsTooLarge",
   537  	451: "StatusUnavailableForLegalReasons",
   538  	500: "StatusInternalServerError",
   539  	501: "StatusNotImplemented",
   540  	502: "StatusBadGateway",
   541  	503: "StatusServiceUnavailable",
   542  	504: "StatusGatewayTimeout",
   543  	505: "StatusHTTPVersionNotSupported",
   544  	506: "StatusVariantAlsoNegotiates",
   545  	507: "StatusInsufficientStorage",
   546  	508: "StatusLoopDetected",
   547  	510: "StatusNotExtended",
   548  	511: "StatusNetworkAuthenticationRequired",
   549  }
   550  
   551  func (c *Checker) CheckHTTPStatusCodes(j *lint.Job) {
   552  	for _, pkg := range j.Program.InitialPackages {
   553  		whitelist := map[string]bool{}
   554  		for _, code := range pkg.Config.HTTPStatusCodeWhitelist {
   555  			whitelist[code] = true
   556  		}
   557  		fn := func(node ast.Node) bool {
   558  			call, ok := node.(*ast.CallExpr)
   559  			if !ok {
   560  				return true
   561  			}
   562  
   563  			var arg int
   564  			switch CallNameAST(j, call) {
   565  			case "net/http.Error":
   566  				arg = 2
   567  			case "net/http.Redirect":
   568  				arg = 3
   569  			case "net/http.StatusText":
   570  				arg = 0
   571  			case "net/http.RedirectHandler":
   572  				arg = 1
   573  			default:
   574  				return true
   575  			}
   576  			lit, ok := call.Args[arg].(*ast.BasicLit)
   577  			if !ok {
   578  				return true
   579  			}
   580  			if whitelist[lit.Value] {
   581  				return true
   582  			}
   583  
   584  			n, err := strconv.Atoi(lit.Value)
   585  			if err != nil {
   586  				return true
   587  			}
   588  			s, ok := httpStatusCodes[n]
   589  			if !ok {
   590  				return true
   591  			}
   592  			j.Errorf(lit, "should use constant http.%s instead of numeric literal %d", s, n)
   593  			return true
   594  		}
   595  		for _, f := range pkg.Syntax {
   596  			ast.Inspect(f, fn)
   597  		}
   598  	}
   599  }
   600  
   601  func (c *Checker) CheckDefaultCaseOrder(j *lint.Job) {
   602  	fn := func(node ast.Node) bool {
   603  		stmt, ok := node.(*ast.SwitchStmt)
   604  		if !ok {
   605  			return true
   606  		}
   607  		list := stmt.Body.List
   608  		for i, c := range list {
   609  			if c.(*ast.CaseClause).List == nil && i != 0 && i != len(list)-1 {
   610  				j.Errorf(c, "default case should be first or last in switch statement")
   611  				break
   612  			}
   613  		}
   614  		return true
   615  	}
   616  	for _, f := range j.Program.Files {
   617  		ast.Inspect(f, fn)
   618  	}
   619  }
   620  
   621  func (c *Checker) CheckYodaConditions(j *lint.Job) {
   622  	fn := func(node ast.Node) bool {
   623  		cond, ok := node.(*ast.BinaryExpr)
   624  		if !ok {
   625  			return true
   626  		}
   627  		if cond.Op != token.EQL && cond.Op != token.NEQ {
   628  			return true
   629  		}
   630  		if _, ok := cond.X.(*ast.BasicLit); !ok {
   631  			return true
   632  		}
   633  		if _, ok := cond.Y.(*ast.BasicLit); ok {
   634  			// Don't flag lit == lit conditions, just in case
   635  			return true
   636  		}
   637  		j.Errorf(cond, "don't use Yoda conditions")
   638  		return true
   639  	}
   640  	for _, f := range j.Program.Files {
   641  		ast.Inspect(f, fn)
   642  	}
   643  }