github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/src/go/types/gotype.go (about) 1 // Copyright 2011 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 5 // +build ignore 6 7 // Build this command explicitly: go build gotype.go 8 9 /* 10 The gotype command does syntactic and semantic analysis of Go files 11 and packages like the front-end of a Go compiler. Errors are reported 12 if the analysis fails; otherwise gotype is quiet (unless -v is set). 13 14 Without a list of paths, gotype reads from standard input, which 15 must provide a single Go source file defining a complete package. 16 17 If a single path is specified that is a directory, gotype checks 18 the Go files in that directory; they must all belong to the same 19 package. 20 21 Otherwise, each path must be the filename of Go file belonging to 22 the same package. 23 24 Usage: 25 gotype [flags] [path...] 26 27 The flags are: 28 -a 29 use all (incl. _test.go) files when processing a directory 30 -e 31 report all errors (not just the first 10) 32 -v 33 verbose mode 34 -c 35 compiler used to compile packages (gc or gccgo); default: gc 36 (gotype based on Go1.5 and up only) 37 -gccgo 38 use gccimporter instead of gcimporter 39 (gotype based on Go1.4 and before only) 40 41 Debugging flags: 42 -seq 43 parse sequentially, rather than in parallel 44 -ast 45 print AST (forces -seq) 46 -trace 47 print parse trace (forces -seq) 48 -comments 49 parse comments (ignored unless -ast or -trace is provided) 50 51 Examples: 52 53 To check the files a.go, b.go, and c.go: 54 55 gotype a.go b.go c.go 56 57 To check an entire package in the directory dir and print the processed files: 58 59 gotype -v dir 60 61 To check an entire package including tests in the local directory: 62 63 gotype -a . 64 65 To verify the output of a pipe: 66 67 echo "package foo" | gotype 68 69 */ 70 package main 71 72 import ( 73 "flag" 74 "fmt" 75 "go/ast" 76 "go/build" 77 "go/importer" 78 "go/parser" 79 "go/scanner" 80 "go/token" 81 "go/types" 82 "io/ioutil" 83 "os" 84 "path/filepath" 85 "time" 86 ) 87 88 var ( 89 // main operation modes 90 allFiles = flag.Bool("a", false, "use all (incl. _test.go) files when processing a directory") 91 allErrors = flag.Bool("e", false, "report all errors (not just the first 10)") 92 verbose = flag.Bool("v", false, "verbose mode") 93 gccgo = flag.Bool("gccgo", false, "use gccgoimporter instead of gcimporter") 94 95 // debugging support 96 sequential = flag.Bool("seq", false, "parse sequentially, rather than in parallel") 97 printAST = flag.Bool("ast", false, "print AST (forces -seq)") 98 printTrace = flag.Bool("trace", false, "print parse trace (forces -seq)") 99 parseComments = flag.Bool("comments", false, "parse comments (ignored unless -ast or -trace is provided)") 100 ) 101 102 var ( 103 fset = token.NewFileSet() 104 errorCount = 0 105 parserMode parser.Mode 106 sizes types.Sizes 107 ) 108 109 func initParserMode() { 110 if *allErrors { 111 parserMode |= parser.AllErrors 112 } 113 if *printTrace { 114 parserMode |= parser.Trace 115 } 116 if *parseComments && (*printAST || *printTrace) { 117 parserMode |= parser.ParseComments 118 } 119 } 120 121 func initSizes() { 122 wordSize := 8 123 maxAlign := 8 124 switch build.Default.GOARCH { 125 case "386", "arm": 126 wordSize = 4 127 maxAlign = 4 128 // add more cases as needed 129 } 130 sizes = &types.StdSizes{WordSize: int64(wordSize), MaxAlign: int64(maxAlign)} 131 } 132 133 func usage() { 134 fmt.Fprintln(os.Stderr, "usage: gotype [flags] [path ...]") 135 flag.PrintDefaults() 136 os.Exit(2) 137 } 138 139 func report(err error) { 140 scanner.PrintError(os.Stderr, err) 141 if list, ok := err.(scanner.ErrorList); ok { 142 errorCount += len(list) 143 return 144 } 145 errorCount++ 146 } 147 148 // parse may be called concurrently 149 func parse(filename string, src interface{}) (*ast.File, error) { 150 if *verbose { 151 fmt.Println(filename) 152 } 153 file, err := parser.ParseFile(fset, filename, src, parserMode) // ok to access fset concurrently 154 if *printAST { 155 ast.Print(fset, file) 156 } 157 return file, err 158 } 159 160 func parseStdin() (*ast.File, error) { 161 src, err := ioutil.ReadAll(os.Stdin) 162 if err != nil { 163 return nil, err 164 } 165 return parse("<standard input>", src) 166 } 167 168 func parseFiles(filenames []string) ([]*ast.File, error) { 169 files := make([]*ast.File, len(filenames)) 170 171 if *sequential { 172 for i, filename := range filenames { 173 var err error 174 files[i], err = parse(filename, nil) 175 if err != nil { 176 return nil, err // leave unfinished goroutines hanging 177 } 178 } 179 } else { 180 type parseResult struct { 181 file *ast.File 182 err error 183 } 184 185 out := make(chan parseResult) 186 for _, filename := range filenames { 187 go func(filename string) { 188 file, err := parse(filename, nil) 189 out <- parseResult{file, err} 190 }(filename) 191 } 192 193 for i := range filenames { 194 res := <-out 195 if res.err != nil { 196 return nil, res.err // leave unfinished goroutines hanging 197 } 198 files[i] = res.file 199 } 200 } 201 202 return files, nil 203 } 204 205 func parseDir(dirname string) ([]*ast.File, error) { 206 ctxt := build.Default 207 pkginfo, err := ctxt.ImportDir(dirname, 0) 208 if _, nogo := err.(*build.NoGoError); err != nil && !nogo { 209 return nil, err 210 } 211 filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...) 212 if *allFiles { 213 filenames = append(filenames, pkginfo.TestGoFiles...) 214 } 215 216 // complete file names 217 for i, filename := range filenames { 218 filenames[i] = filepath.Join(dirname, filename) 219 } 220 221 return parseFiles(filenames) 222 } 223 224 func getPkgFiles(args []string) ([]*ast.File, error) { 225 if len(args) == 0 { 226 // stdin 227 file, err := parseStdin() 228 if err != nil { 229 return nil, err 230 } 231 return []*ast.File{file}, nil 232 } 233 234 if len(args) == 1 { 235 // possibly a directory 236 path := args[0] 237 info, err := os.Stat(path) 238 if err != nil { 239 return nil, err 240 } 241 if info.IsDir() { 242 return parseDir(path) 243 } 244 } 245 246 // list of files 247 return parseFiles(args) 248 } 249 250 func checkPkgFiles(files []*ast.File) { 251 compiler := "gc" 252 if *gccgo { 253 compiler = "gccgo" 254 } 255 type bailout struct{} 256 conf := types.Config{ 257 FakeImportC: true, 258 Error: func(err error) { 259 if !*allErrors && errorCount >= 10 { 260 panic(bailout{}) 261 } 262 report(err) 263 }, 264 Importer: importer.For(compiler, nil), 265 Sizes: sizes, 266 } 267 268 defer func() { 269 switch p := recover().(type) { 270 case nil, bailout: 271 // normal return or early exit 272 default: 273 // re-panic 274 panic(p) 275 } 276 }() 277 278 const path = "pkg" // any non-empty string will do for now 279 conf.Check(path, fset, files, nil) 280 } 281 282 func printStats(d time.Duration) { 283 fileCount := 0 284 lineCount := 0 285 fset.Iterate(func(f *token.File) bool { 286 fileCount++ 287 lineCount += f.LineCount() 288 return true 289 }) 290 291 fmt.Printf( 292 "%s (%d files, %d lines, %d lines/s)\n", 293 d, fileCount, lineCount, int64(float64(lineCount)/d.Seconds()), 294 ) 295 } 296 297 func main() { 298 flag.Usage = usage 299 flag.Parse() 300 if *printAST || *printTrace { 301 *sequential = true 302 } 303 initParserMode() 304 initSizes() 305 306 start := time.Now() 307 308 files, err := getPkgFiles(flag.Args()) 309 if err != nil { 310 report(err) 311 os.Exit(2) 312 } 313 314 checkPkgFiles(files) 315 if errorCount > 0 { 316 os.Exit(2) 317 } 318 319 if *verbose { 320 printStats(time.Since(start)) 321 } 322 }