PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/go/gotour/src/code.google.com/p/go.tools/cmd/vet/main.go

https://github.com/zatkin/programming
Go | 562 lines | 430 code | 59 blank | 73 comment | 93 complexity | 46648222234714f5d1bf95e744882909 MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, Apache-2.0, MIT
  1. // Copyright 2010 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Vet is a simple checker for static errors in Go source code.
  5. // See doc.go for more information.
  6. package main
  7. import (
  8. "bytes"
  9. "flag"
  10. "fmt"
  11. "go/ast"
  12. "go/build"
  13. "go/parser"
  14. "go/printer"
  15. "go/token"
  16. "io/ioutil"
  17. "os"
  18. "path/filepath"
  19. "strconv"
  20. "strings"
  21. _ "code.google.com/p/go.tools/go/gcimporter"
  22. "code.google.com/p/go.tools/go/types"
  23. )
  24. // TODO: Need a flag to set build tags when parsing the package.
  25. var verbose = flag.Bool("v", false, "verbose")
  26. var strictShadowing = flag.Bool("shadowstrict", false, "whether to be strict about shadowing; can be noisy")
  27. var testFlag = flag.Bool("test", false, "for testing only: sets -all and -shadow")
  28. var exitCode = 0
  29. // "all" is here only for the appearance of backwards compatibility.
  30. // It has no effect; the triState flags do the work.
  31. var all = flag.Bool("all", true, "check everything; disabled if any explicit check is requested")
  32. // Flags to control which individual checks to perform.
  33. var report = map[string]*triState{
  34. "asmdecl": triStateFlag("asmdecl", unset, "check assembly against Go declarations"),
  35. "assign": triStateFlag("assign", unset, "check for useless assignments"),
  36. "atomic": triStateFlag("atomic", unset, "check for common mistaken usages of the sync/atomic package"),
  37. "buildtags": triStateFlag("buildtags", unset, "check that +build tags are valid"),
  38. "composites": triStateFlag("composites", unset, "check that composite literals used field-keyed elements"),
  39. "copylocks": triStateFlag("copylocks", unset, "check that locks are not passed by value"),
  40. "methods": triStateFlag("methods", unset, "check that canonically named methods are canonically defined"),
  41. "nilfunc": triStateFlag("nilfunc", unset, "check for comparisons between functions and nil"),
  42. "printf": triStateFlag("printf", unset, "check printf-like invocations"),
  43. "rangeloops": triStateFlag("rangeloops", unset, "check that range loop variables are used correctly"),
  44. "shadow": triStateFlag("shadow", unset, "check for shadowed variables (experimental; must be set explicitly)"),
  45. "structtags": triStateFlag("structtags", unset, "check that struct field tags have canonical format"),
  46. "unreachable": triStateFlag("unreachable", unset, "check for unreachable code"),
  47. }
  48. // experimental records the flags enabling experimental features. These must be
  49. // requested explicitly; they are not enabled by -all.
  50. var experimental = map[string]bool{
  51. "shadow": true,
  52. }
  53. // setTrueCount record how many flags are explicitly set to true.
  54. var setTrueCount int
  55. // A triState is a boolean that knows whether it has been set to either true or false.
  56. // It is used to identify if a flag appears; the standard boolean flag cannot
  57. // distinguish missing from unset. It also satisfies flag.Value.
  58. type triState int
  59. const (
  60. unset triState = iota
  61. setTrue
  62. setFalse
  63. )
  64. func triStateFlag(name string, value triState, usage string) *triState {
  65. flag.Var(&value, name, usage)
  66. return &value
  67. }
  68. // triState implements flag.Value, flag.Getter, and flag.boolFlag.
  69. // They work like boolean flags: we can say vet -printf as well as vet -printf=true
  70. func (ts *triState) Get() interface{} {
  71. return *ts == setTrue
  72. }
  73. func (ts triState) isTrue() bool {
  74. return ts == setTrue
  75. }
  76. func (ts *triState) Set(value string) error {
  77. b, err := strconv.ParseBool(value)
  78. if err != nil {
  79. return err
  80. }
  81. if b {
  82. *ts = setTrue
  83. setTrueCount++
  84. } else {
  85. *ts = setFalse
  86. }
  87. return nil
  88. }
  89. func (ts *triState) String() string {
  90. switch *ts {
  91. case unset:
  92. return "unset"
  93. case setTrue:
  94. return "true"
  95. case setFalse:
  96. return "false"
  97. }
  98. panic("not reached")
  99. }
  100. func (ts triState) IsBoolFlag() bool {
  101. return true
  102. }
  103. // vet tells whether to report errors for the named check, a flag name.
  104. func vet(name string) bool {
  105. if *testFlag {
  106. return true
  107. }
  108. return report[name].isTrue()
  109. }
  110. // setExit sets the value for os.Exit when it is called, later. It
  111. // remembers the highest value.
  112. func setExit(err int) {
  113. if err > exitCode {
  114. exitCode = err
  115. }
  116. }
  117. // Usage is a replacement usage function for the flags package.
  118. func Usage() {
  119. fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
  120. fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n")
  121. fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n")
  122. fmt.Fprintf(os.Stderr, "For more information run\n")
  123. fmt.Fprintf(os.Stderr, "\tgodoc code.google.com/p/go.tools/cmd/vet\n\n")
  124. fmt.Fprintf(os.Stderr, "Flags:\n")
  125. flag.PrintDefaults()
  126. os.Exit(2)
  127. }
  128. // File is a wrapper for the state of a file used in the parser.
  129. // The parse tree walkers are all methods of this type.
  130. type File struct {
  131. pkg *Package
  132. fset *token.FileSet
  133. name string
  134. content []byte
  135. file *ast.File
  136. b bytes.Buffer // for use by methods
  137. // The last "String() string" method receiver we saw while walking.
  138. // This is used by the recursiveStringer method in print.go.
  139. lastStringerReceiver *ast.Object
  140. }
  141. func main() {
  142. flag.Usage = Usage
  143. flag.Parse()
  144. // If any flag is set, we run only those checks requested.
  145. // If no flags are set true, set all the non-experimental ones not explicitly set (in effect, set the "-all" flag).
  146. if setTrueCount == 0 {
  147. for name, setting := range report {
  148. if *setting == unset && !experimental[name] {
  149. *setting = setTrue
  150. }
  151. }
  152. }
  153. if *printfuncs != "" {
  154. for _, name := range strings.Split(*printfuncs, ",") {
  155. if len(name) == 0 {
  156. flag.Usage()
  157. }
  158. skip := 0
  159. if colon := strings.LastIndex(name, ":"); colon > 0 {
  160. var err error
  161. skip, err = strconv.Atoi(name[colon+1:])
  162. if err != nil {
  163. errorf(`illegal format for "Func:N" argument %q; %s`, name, err)
  164. }
  165. name = name[:colon]
  166. }
  167. name = strings.ToLower(name)
  168. if name[len(name)-1] == 'f' {
  169. printfList[name] = skip
  170. } else {
  171. printList[name] = skip
  172. }
  173. }
  174. }
  175. if flag.NArg() == 0 {
  176. Usage()
  177. }
  178. dirs := false
  179. files := false
  180. for _, name := range flag.Args() {
  181. // Is it a directory?
  182. fi, err := os.Stat(name)
  183. if err != nil {
  184. warnf("error walking tree: %s", err)
  185. continue
  186. }
  187. if fi.IsDir() {
  188. dirs = true
  189. } else {
  190. files = true
  191. }
  192. }
  193. if dirs && files {
  194. Usage()
  195. }
  196. if dirs {
  197. for _, name := range flag.Args() {
  198. walkDir(name)
  199. }
  200. return
  201. }
  202. if !doPackage(".", flag.Args()) {
  203. warnf("no files checked")
  204. }
  205. os.Exit(exitCode)
  206. }
  207. // prefixDirectory places the directory name on the beginning of each name in the list.
  208. func prefixDirectory(directory string, names []string) {
  209. if directory != "." {
  210. for i, name := range names {
  211. names[i] = filepath.Join(directory, name)
  212. }
  213. }
  214. }
  215. // doPackageDir analyzes the single package found in the directory, if there is one,
  216. // plus a test package, if there is one.
  217. func doPackageDir(directory string) {
  218. pkg, err := build.Default.ImportDir(directory, 0)
  219. if err != nil {
  220. // If it's just that there are no go source files, that's fine.
  221. if _, nogo := err.(*build.NoGoError); nogo {
  222. return
  223. }
  224. // Non-fatal: we are doing a recursive walk and there may be other directories.
  225. warnf("cannot process directory %s: %s", directory, err)
  226. return
  227. }
  228. var names []string
  229. names = append(names, pkg.GoFiles...)
  230. names = append(names, pkg.CgoFiles...)
  231. names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
  232. names = append(names, pkg.SFiles...)
  233. prefixDirectory(directory, names)
  234. doPackage(directory, names)
  235. // Is there also a "foo_test" package? If so, do that one as well.
  236. if len(pkg.XTestGoFiles) > 0 {
  237. names = pkg.XTestGoFiles
  238. prefixDirectory(directory, names)
  239. doPackage(directory, names)
  240. }
  241. }
  242. type Package struct {
  243. path string
  244. defs map[*ast.Ident]types.Object
  245. uses map[*ast.Ident]types.Object
  246. types map[ast.Expr]types.TypeAndValue
  247. spans map[types.Object]Span
  248. files []*File
  249. typesPkg *types.Package
  250. }
  251. // doPackage analyzes the single package constructed from the named files.
  252. // It returns whether any files were checked.
  253. func doPackage(directory string, names []string) bool {
  254. var files []*File
  255. var astFiles []*ast.File
  256. fs := token.NewFileSet()
  257. for _, name := range names {
  258. f, err := os.Open(name)
  259. if err != nil {
  260. // Warn but continue to next package.
  261. warnf("%s: %s", name, err)
  262. return false
  263. }
  264. defer f.Close()
  265. data, err := ioutil.ReadAll(f)
  266. if err != nil {
  267. warnf("%s: %s", name, err)
  268. return false
  269. }
  270. checkBuildTag(name, data)
  271. var parsedFile *ast.File
  272. if strings.HasSuffix(name, ".go") {
  273. parsedFile, err = parser.ParseFile(fs, name, bytes.NewReader(data), 0)
  274. if err != nil {
  275. warnf("%s: %s", name, err)
  276. return false
  277. }
  278. astFiles = append(astFiles, parsedFile)
  279. }
  280. files = append(files, &File{fset: fs, content: data, name: name, file: parsedFile})
  281. }
  282. if len(astFiles) == 0 {
  283. return false
  284. }
  285. pkg := new(Package)
  286. pkg.path = astFiles[0].Name.Name
  287. pkg.files = files
  288. // Type check the package.
  289. err := pkg.check(fs, astFiles)
  290. if err != nil && *verbose {
  291. warnf("%s", err)
  292. }
  293. for _, file := range files {
  294. file.pkg = pkg
  295. if file.file != nil {
  296. file.walkFile(file.name, file.file)
  297. }
  298. }
  299. asmCheck(pkg)
  300. return true
  301. }
  302. func visit(path string, f os.FileInfo, err error) error {
  303. if err != nil {
  304. warnf("walk error: %s", err)
  305. return err
  306. }
  307. // One package per directory. Ignore the files themselves.
  308. if !f.IsDir() {
  309. return nil
  310. }
  311. doPackageDir(path)
  312. return nil
  313. }
  314. func (pkg *Package) hasFileWithSuffix(suffix string) bool {
  315. for _, f := range pkg.files {
  316. if strings.HasSuffix(f.name, suffix) {
  317. return true
  318. }
  319. }
  320. return false
  321. }
  322. // walkDir recursively walks the tree looking for Go packages.
  323. func walkDir(root string) {
  324. filepath.Walk(root, visit)
  325. }
  326. // errorf formats the error to standard error, adding program
  327. // identification and a newline, and exits.
  328. func errorf(format string, args ...interface{}) {
  329. fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
  330. os.Exit(2)
  331. }
  332. // warnf formats the error to standard error, adding program
  333. // identification and a newline, but does not exit.
  334. func warnf(format string, args ...interface{}) {
  335. fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
  336. setExit(1)
  337. }
  338. // Println is fmt.Println guarded by -v.
  339. func Println(args ...interface{}) {
  340. if !*verbose {
  341. return
  342. }
  343. fmt.Println(args...)
  344. }
  345. // Printf is fmt.Printf guarded by -v.
  346. func Printf(format string, args ...interface{}) {
  347. if !*verbose {
  348. return
  349. }
  350. fmt.Printf(format+"\n", args...)
  351. }
  352. // Bad reports an error and sets the exit code..
  353. func (f *File) Bad(pos token.Pos, args ...interface{}) {
  354. f.Warn(pos, args...)
  355. setExit(1)
  356. }
  357. // Badf reports a formatted error and sets the exit code.
  358. func (f *File) Badf(pos token.Pos, format string, args ...interface{}) {
  359. f.Warnf(pos, format, args...)
  360. setExit(1)
  361. }
  362. // loc returns a formatted representation of the position.
  363. func (f *File) loc(pos token.Pos) string {
  364. if pos == token.NoPos {
  365. return ""
  366. }
  367. // Do not print columns. Because the pos often points to the start of an
  368. // expression instead of the inner part with the actual error, the
  369. // precision can mislead.
  370. posn := f.fset.Position(pos)
  371. return fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
  372. }
  373. // Warn reports an error but does not set the exit code.
  374. func (f *File) Warn(pos token.Pos, args ...interface{}) {
  375. fmt.Fprint(os.Stderr, f.loc(pos)+": "+fmt.Sprintln(args...))
  376. }
  377. // Warnf reports a formatted error but does not set the exit code.
  378. func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) {
  379. fmt.Fprintf(os.Stderr, f.loc(pos)+": "+format+"\n", args...)
  380. }
  381. // walkFile walks the file's tree.
  382. func (f *File) walkFile(name string, file *ast.File) {
  383. Println("Checking file", name)
  384. ast.Walk(f, file)
  385. }
  386. // Visit implements the ast.Visitor interface.
  387. func (f *File) Visit(node ast.Node) ast.Visitor {
  388. switch n := node.(type) {
  389. case *ast.AssignStmt:
  390. f.walkAssignStmt(n)
  391. case *ast.BinaryExpr:
  392. f.walkBinaryExpr(n)
  393. case *ast.CallExpr:
  394. f.walkCallExpr(n)
  395. case *ast.CompositeLit:
  396. f.walkCompositeLit(n)
  397. case *ast.Field:
  398. f.walkFieldTag(n)
  399. case *ast.FuncDecl:
  400. f.walkFuncDecl(n)
  401. case *ast.FuncLit:
  402. f.walkFuncLit(n)
  403. case *ast.GenDecl:
  404. f.walkGenDecl(n)
  405. case *ast.InterfaceType:
  406. f.walkInterfaceType(n)
  407. case *ast.RangeStmt:
  408. f.walkRangeStmt(n)
  409. }
  410. return f
  411. }
  412. // walkAssignStmt walks an assignment statement
  413. func (f *File) walkAssignStmt(stmt *ast.AssignStmt) {
  414. f.checkAssignStmt(stmt)
  415. f.checkAtomicAssignment(stmt)
  416. f.checkShadowAssignment(stmt)
  417. }
  418. func (f *File) walkBinaryExpr(expr *ast.BinaryExpr) {
  419. f.checkNilFuncComparison(expr)
  420. }
  421. // walkCall walks a call expression.
  422. func (f *File) walkCall(call *ast.CallExpr, name string) {
  423. f.checkFmtPrintfCall(call, name)
  424. }
  425. // walkCallExpr walks a call expression.
  426. func (f *File) walkCallExpr(call *ast.CallExpr) {
  427. switch x := call.Fun.(type) {
  428. case *ast.Ident:
  429. f.walkCall(call, x.Name)
  430. case *ast.SelectorExpr:
  431. f.walkCall(call, x.Sel.Name)
  432. }
  433. }
  434. // walkCompositeLit walks a composite literal.
  435. func (f *File) walkCompositeLit(c *ast.CompositeLit) {
  436. f.checkUnkeyedLiteral(c)
  437. }
  438. // walkFieldTag walks a struct field tag.
  439. func (f *File) walkFieldTag(field *ast.Field) {
  440. if field.Tag == nil {
  441. return
  442. }
  443. f.checkCanonicalFieldTag(field)
  444. }
  445. // walkMethod walks the method's signature.
  446. func (f *File) walkMethod(id *ast.Ident, t *ast.FuncType) {
  447. f.checkCanonicalMethod(id, t)
  448. }
  449. // walkFuncDecl walks a function declaration.
  450. func (f *File) walkFuncDecl(d *ast.FuncDecl) {
  451. f.checkUnreachable(d.Body)
  452. if d.Recv != nil {
  453. f.walkMethod(d.Name, d.Type)
  454. }
  455. f.prepStringerReceiver(d)
  456. f.checkCopyLocks(d)
  457. }
  458. // prepStringerReceiver checks whether the given declaration is a fmt.Stringer
  459. // implementation, and if so sets the File's lastStringerReceiver field to the
  460. // declaration's receiver object.
  461. func (f *File) prepStringerReceiver(d *ast.FuncDecl) {
  462. if !f.isStringer(d) {
  463. return
  464. }
  465. if l := d.Recv.List; len(l) == 1 {
  466. if n := l[0].Names; len(n) == 1 {
  467. f.lastStringerReceiver = n[0].Obj
  468. }
  469. }
  470. }
  471. // isStringer returns true if the provided declaration is a "String() string"
  472. // method; an implementation of fmt.Stringer.
  473. func (f *File) isStringer(d *ast.FuncDecl) bool {
  474. return d.Recv != nil && d.Name.Name == "String" && d.Type.Results != nil &&
  475. len(d.Type.Params.List) == 0 && len(d.Type.Results.List) == 1 &&
  476. f.pkg.types[d.Type.Results.List[0].Type].Type == types.Typ[types.String]
  477. }
  478. // walkGenDecl walks a general declaration.
  479. func (f *File) walkGenDecl(d *ast.GenDecl) {
  480. f.checkShadowDecl(d)
  481. }
  482. // walkFuncLit walks a function literal.
  483. func (f *File) walkFuncLit(x *ast.FuncLit) {
  484. f.checkUnreachable(x.Body)
  485. }
  486. // walkInterfaceType walks the method signatures of an interface.
  487. func (f *File) walkInterfaceType(t *ast.InterfaceType) {
  488. for _, field := range t.Methods.List {
  489. for _, id := range field.Names {
  490. f.walkMethod(id, field.Type.(*ast.FuncType))
  491. }
  492. }
  493. }
  494. // walkRangeStmt walks a range statement.
  495. func (f *File) walkRangeStmt(n *ast.RangeStmt) {
  496. checkRangeLoop(f, n)
  497. }
  498. // gofmt returns a string representation of the expression.
  499. func (f *File) gofmt(x ast.Expr) string {
  500. f.b.Reset()
  501. printer.Fprint(&f.b, f.fset, x)
  502. return f.b.String()
  503. }