PageRenderTime 45ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/cmd/vet/main.go

https://bitbucket.org/nightsnaker/go
Go | 261 lines | 195 code | 31 blank | 35 comment | 41 complexity | a7b7c4131051ec47b5567ec5fd0c82ff MD5 | raw file
Possible License(s): BSD-3-Clause
  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/parser"
  13. "go/token"
  14. "io"
  15. "os"
  16. "path/filepath"
  17. "strconv"
  18. "strings"
  19. )
  20. var verbose = flag.Bool("v", false, "verbose")
  21. var exitCode = 0
  22. // Flags to control which checks to perform
  23. var (
  24. vetAll = flag.Bool("all", true, "check everything; disabled if any explicit check is requested")
  25. vetMethods = flag.Bool("methods", false, "check that canonically named methods are canonically defined")
  26. vetPrintf = flag.Bool("printf", false, "check printf-like invocations")
  27. vetStructTags = flag.Bool("structtags", false, "check that struct field tags have canonical format")
  28. vetUntaggedLiteral = flag.Bool("composites", false, "check that composite literals used type-tagged elements")
  29. vetRangeLoops = flag.Bool("rangeloops", false, "check that range loop variables are used correctly")
  30. )
  31. // setExit sets the value for os.Exit when it is called, later. It
  32. // remembers the highest value.
  33. func setExit(err int) {
  34. if err > exitCode {
  35. exitCode = err
  36. }
  37. }
  38. // Usage is a replacement usage function for the flags package.
  39. func Usage() {
  40. fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
  41. flag.PrintDefaults()
  42. os.Exit(2)
  43. }
  44. // File is a wrapper for the state of a file used in the parser.
  45. // The parse tree walkers are all methods of this type.
  46. type File struct {
  47. fset *token.FileSet
  48. file *ast.File
  49. b bytes.Buffer // for use by methods
  50. }
  51. func main() {
  52. flag.Usage = Usage
  53. flag.Parse()
  54. // If a check is named explicitly, turn off the 'all' flag.
  55. if *vetMethods || *vetPrintf || *vetStructTags || *vetUntaggedLiteral || *vetRangeLoops {
  56. *vetAll = false
  57. }
  58. if *printfuncs != "" {
  59. for _, name := range strings.Split(*printfuncs, ",") {
  60. if len(name) == 0 {
  61. flag.Usage()
  62. }
  63. skip := 0
  64. if colon := strings.LastIndex(name, ":"); colon > 0 {
  65. var err error
  66. skip, err = strconv.Atoi(name[colon+1:])
  67. if err != nil {
  68. errorf(`illegal format for "Func:N" argument %q; %s`, name, err)
  69. }
  70. name = name[:colon]
  71. }
  72. name = strings.ToLower(name)
  73. if name[len(name)-1] == 'f' {
  74. printfList[name] = skip
  75. } else {
  76. printList[name] = skip
  77. }
  78. }
  79. }
  80. if flag.NArg() == 0 {
  81. doFile("stdin", os.Stdin)
  82. } else {
  83. for _, name := range flag.Args() {
  84. // Is it a directory?
  85. if fi, err := os.Stat(name); err == nil && fi.IsDir() {
  86. walkDir(name)
  87. } else {
  88. doFile(name, nil)
  89. }
  90. }
  91. }
  92. os.Exit(exitCode)
  93. }
  94. // doFile analyzes one file. If the reader is nil, the source code is read from the
  95. // named file.
  96. func doFile(name string, reader io.Reader) {
  97. fs := token.NewFileSet()
  98. parsedFile, err := parser.ParseFile(fs, name, reader, 0)
  99. if err != nil {
  100. errorf("%s: %s", name, err)
  101. return
  102. }
  103. file := &File{fset: fs, file: parsedFile}
  104. file.walkFile(name, parsedFile)
  105. }
  106. func visit(path string, f os.FileInfo, err error) error {
  107. if err != nil {
  108. errorf("walk error: %s", err)
  109. return nil
  110. }
  111. if !f.IsDir() && strings.HasSuffix(path, ".go") {
  112. doFile(path, nil)
  113. }
  114. return nil
  115. }
  116. // walkDir recursively walks the tree looking for .go files.
  117. func walkDir(root string) {
  118. filepath.Walk(root, visit)
  119. }
  120. // error formats the error to standard error, adding program
  121. // identification and a newline
  122. func errorf(format string, args ...interface{}) {
  123. fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
  124. setExit(2)
  125. }
  126. // Println is fmt.Println guarded by -v.
  127. func Println(args ...interface{}) {
  128. if !*verbose {
  129. return
  130. }
  131. fmt.Println(args...)
  132. }
  133. // Printf is fmt.Printf guarded by -v.
  134. func Printf(format string, args ...interface{}) {
  135. if !*verbose {
  136. return
  137. }
  138. fmt.Printf(format+"\n", args...)
  139. }
  140. // Bad reports an error and sets the exit code..
  141. func (f *File) Bad(pos token.Pos, args ...interface{}) {
  142. f.Warn(pos, args...)
  143. setExit(1)
  144. }
  145. // Badf reports a formatted error and sets the exit code.
  146. func (f *File) Badf(pos token.Pos, format string, args ...interface{}) {
  147. f.Warnf(pos, format, args...)
  148. setExit(1)
  149. }
  150. // Warn reports an error but does not set the exit code.
  151. func (f *File) Warn(pos token.Pos, args ...interface{}) {
  152. loc := f.fset.Position(pos).String() + ": "
  153. fmt.Fprint(os.Stderr, loc+fmt.Sprintln(args...))
  154. }
  155. // Warnf reports a formatted error but does not set the exit code.
  156. func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) {
  157. loc := f.fset.Position(pos).String() + ": "
  158. fmt.Fprintf(os.Stderr, loc+format+"\n", args...)
  159. }
  160. // walkFile walks the file's tree.
  161. func (f *File) walkFile(name string, file *ast.File) {
  162. Println("Checking file", name)
  163. ast.Walk(f, file)
  164. }
  165. // Visit implements the ast.Visitor interface.
  166. func (f *File) Visit(node ast.Node) ast.Visitor {
  167. switch n := node.(type) {
  168. case *ast.CallExpr:
  169. f.walkCallExpr(n)
  170. case *ast.CompositeLit:
  171. f.walkCompositeLit(n)
  172. case *ast.Field:
  173. f.walkFieldTag(n)
  174. case *ast.FuncDecl:
  175. f.walkMethodDecl(n)
  176. case *ast.InterfaceType:
  177. f.walkInterfaceType(n)
  178. case *ast.RangeStmt:
  179. f.walkRangeStmt(n)
  180. }
  181. return f
  182. }
  183. // walkCall walks a call expression.
  184. func (f *File) walkCall(call *ast.CallExpr, name string) {
  185. f.checkFmtPrintfCall(call, name)
  186. }
  187. // walkCallExpr walks a call expression.
  188. func (f *File) walkCallExpr(call *ast.CallExpr) {
  189. switch x := call.Fun.(type) {
  190. case *ast.Ident:
  191. f.walkCall(call, x.Name)
  192. case *ast.SelectorExpr:
  193. f.walkCall(call, x.Sel.Name)
  194. }
  195. }
  196. // walkCompositeLit walks a composite literal.
  197. func (f *File) walkCompositeLit(c *ast.CompositeLit) {
  198. f.checkUntaggedLiteral(c)
  199. }
  200. // walkFieldTag walks a struct field tag.
  201. func (f *File) walkFieldTag(field *ast.Field) {
  202. if field.Tag == nil {
  203. return
  204. }
  205. f.checkCanonicalFieldTag(field)
  206. }
  207. // walkMethodDecl walks the method's signature.
  208. func (f *File) walkMethod(id *ast.Ident, t *ast.FuncType) {
  209. f.checkCanonicalMethod(id, t)
  210. }
  211. // walkMethodDecl walks the method signature in the declaration.
  212. func (f *File) walkMethodDecl(d *ast.FuncDecl) {
  213. if d.Recv == nil {
  214. // not a method
  215. return
  216. }
  217. f.walkMethod(d.Name, d.Type)
  218. }
  219. // walkInterfaceType walks the method signatures of an interface.
  220. func (f *File) walkInterfaceType(t *ast.InterfaceType) {
  221. for _, field := range t.Methods.List {
  222. for _, id := range field.Names {
  223. f.walkMethod(id, field.Type.(*ast.FuncType))
  224. }
  225. }
  226. }
  227. // walkRangeStmt walks a range statement.
  228. func (f *File) walkRangeStmt(n *ast.RangeStmt) {
  229. checkRangeLoop(f, n)
  230. }