Source file src/pkg/cmd/gofmt/gofmt.go
1
2
3
4
5 package main
6
7 import (
8 "bytes"
9 "flag"
10 "fmt"
11 "go/ast"
12 "go/parser"
13 "go/printer"
14 "go/scanner"
15 "go/token"
16 "io"
17 "io/ioutil"
18 "os"
19 "os/exec"
20 "path/filepath"
21 "runtime"
22 "runtime/pprof"
23 "strings"
24 )
25
26 var (
27
28 list = flag.Bool("l", false, "list files whose formatting differs from gofmt's")
29 write = flag.Bool("w", false, "write result to (source) file instead of stdout")
30 rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')")
31 simplifyAST = flag.Bool("s", false, "simplify code")
32 doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
33 allErrors = flag.Bool("e", false, "report all errors (not just the first 10 on different lines)")
34
35
36 cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
37 )
38
39 const (
40 tabWidth = 8
41 printerMode = printer.UseSpaces | printer.TabIndent
42 )
43
44 var (
45 fileSet = token.NewFileSet()
46 exitCode = 0
47 rewrite func(*ast.File) *ast.File
48 parserMode parser.Mode
49 )
50
51 func report(err error) {
52 scanner.PrintError(os.Stderr, err)
53 exitCode = 2
54 }
55
56 func usage() {
57 fmt.Fprintf(os.Stderr, "usage: gofmt [flags] [path ...]\n")
58 flag.PrintDefaults()
59 }
60
61 func initParserMode() {
62 parserMode = parser.ParseComments
63 if *allErrors {
64 parserMode |= parser.AllErrors
65 }
66 }
67
68 func isGoFile(f os.FileInfo) bool {
69
70 name := f.Name()
71 return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
72 }
73
74
75 func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error {
76 var perm os.FileMode = 0644
77 if in == nil {
78 f, err := os.Open(filename)
79 if err != nil {
80 return err
81 }
82 defer f.Close()
83 fi, err := f.Stat()
84 if err != nil {
85 return err
86 }
87 in = f
88 perm = fi.Mode().Perm()
89 }
90
91 src, err := ioutil.ReadAll(in)
92 if err != nil {
93 return err
94 }
95
96 file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, stdin)
97 if err != nil {
98 return err
99 }
100
101 if rewrite != nil {
102 if sourceAdj == nil {
103 file = rewrite(file)
104 } else {
105 fmt.Fprintf(os.Stderr, "warning: rewrite ignored for incomplete programs\n")
106 }
107 }
108
109 ast.SortImports(fileSet, file)
110
111 if *simplifyAST {
112 simplify(file)
113 }
114
115 ast.Inspect(file, normalizeNumbers)
116
117 res, err := format(fileSet, file, sourceAdj, indentAdj, src, printer.Config{Mode: printerMode, Tabwidth: tabWidth})
118 if err != nil {
119 return err
120 }
121
122 if !bytes.Equal(src, res) {
123
124 if *list {
125 fmt.Fprintln(out, filename)
126 }
127 if *write {
128
129 bakname, err := backupFile(filename+".", src, perm)
130 if err != nil {
131 return err
132 }
133 err = ioutil.WriteFile(filename, res, perm)
134 if err != nil {
135 os.Rename(bakname, filename)
136 return err
137 }
138 err = os.Remove(bakname)
139 if err != nil {
140 return err
141 }
142 }
143 if *doDiff {
144 data, err := diff(src, res, filename)
145 if err != nil {
146 return fmt.Errorf("computing diff: %s", err)
147 }
148 fmt.Printf("diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename))
149 out.Write(data)
150 }
151 }
152
153 if !*list && !*write && !*doDiff {
154 _, err = out.Write(res)
155 }
156
157 return err
158 }
159
160 func visitFile(path string, f os.FileInfo, err error) error {
161 if err == nil && isGoFile(f) {
162 err = processFile(path, nil, os.Stdout, false)
163 }
164
165
166 if err != nil && !os.IsNotExist(err) {
167 report(err)
168 }
169 return nil
170 }
171
172 func walkDir(path string) {
173 filepath.Walk(path, visitFile)
174 }
175
176 func main() {
177
178
179
180 gofmtMain()
181 os.Exit(exitCode)
182 }
183
184 func gofmtMain() {
185 flag.Usage = usage
186 flag.Parse()
187
188 if *cpuprofile != "" {
189 f, err := os.Create(*cpuprofile)
190 if err != nil {
191 fmt.Fprintf(os.Stderr, "creating cpu profile: %s\n", err)
192 exitCode = 2
193 return
194 }
195 defer f.Close()
196 pprof.StartCPUProfile(f)
197 defer pprof.StopCPUProfile()
198 }
199
200 initParserMode()
201 initRewrite()
202
203 if flag.NArg() == 0 {
204 if *write {
205 fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input")
206 exitCode = 2
207 return
208 }
209 if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil {
210 report(err)
211 }
212 return
213 }
214
215 for i := 0; i < flag.NArg(); i++ {
216 path := flag.Arg(i)
217 switch dir, err := os.Stat(path); {
218 case err != nil:
219 report(err)
220 case dir.IsDir():
221 walkDir(path)
222 default:
223 if err := processFile(path, nil, os.Stdout, false); err != nil {
224 report(err)
225 }
226 }
227 }
228 }
229
230 func writeTempFile(dir, prefix string, data []byte) (string, error) {
231 file, err := ioutil.TempFile(dir, prefix)
232 if err != nil {
233 return "", err
234 }
235 _, err = file.Write(data)
236 if err1 := file.Close(); err == nil {
237 err = err1
238 }
239 if err != nil {
240 os.Remove(file.Name())
241 return "", err
242 }
243 return file.Name(), nil
244 }
245
246 func diff(b1, b2 []byte, filename string) (data []byte, err error) {
247 f1, err := writeTempFile("", "gofmt", b1)
248 if err != nil {
249 return
250 }
251 defer os.Remove(f1)
252
253 f2, err := writeTempFile("", "gofmt", b2)
254 if err != nil {
255 return
256 }
257 defer os.Remove(f2)
258
259 cmd := "diff"
260 if runtime.GOOS == "plan9" {
261 cmd = "/bin/ape/diff"
262 }
263
264 data, err = exec.Command(cmd, "-u", f1, f2).CombinedOutput()
265 if len(data) > 0 {
266
267
268 return replaceTempFilename(data, filename)
269 }
270 return
271 }
272
273
274
275
276
277
278
279
280
281
282 func replaceTempFilename(diff []byte, filename string) ([]byte, error) {
283 bs := bytes.SplitN(diff, []byte{'\n'}, 3)
284 if len(bs) < 3 {
285 return nil, fmt.Errorf("got unexpected diff for %s", filename)
286 }
287
288 var t0, t1 []byte
289 if i := bytes.LastIndexByte(bs[0], '\t'); i != -1 {
290 t0 = bs[0][i:]
291 }
292 if i := bytes.LastIndexByte(bs[1], '\t'); i != -1 {
293 t1 = bs[1][i:]
294 }
295
296 f := filepath.ToSlash(filename)
297 bs[0] = []byte(fmt.Sprintf("--- %s%s", f+".orig", t0))
298 bs[1] = []byte(fmt.Sprintf("+++ %s%s", f, t1))
299 return bytes.Join(bs, []byte{'\n'}), nil
300 }
301
302 const chmodSupported = runtime.GOOS != "windows"
303
304
305
306
307 func backupFile(filename string, data []byte, perm os.FileMode) (string, error) {
308
309 f, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename))
310 if err != nil {
311 return "", err
312 }
313 bakname := f.Name()
314 if chmodSupported {
315 err = f.Chmod(perm)
316 if err != nil {
317 f.Close()
318 os.Remove(bakname)
319 return bakname, err
320 }
321 }
322
323
324 _, err = f.Write(data)
325 if err1 := f.Close(); err == nil {
326 err = err1
327 }
328
329 return bakname, err
330 }
331
332
333
334
335
336 func normalizeNumbers(n ast.Node) bool {
337 lit, _ := n.(*ast.BasicLit)
338 if lit == nil || (lit.Kind != token.INT && lit.Kind != token.FLOAT && lit.Kind != token.IMAG) {
339 return true
340 }
341 if len(lit.Value) < 2 {
342 return false
343 }
344
345
346
347
348 x := lit.Value
349 switch x[:2] {
350 default:
351
352 if i := strings.LastIndexByte(x, 'E'); i >= 0 {
353 x = x[:i] + "e" + x[i+1:]
354 break
355 }
356
357 if x[len(x)-1] == 'i' && strings.IndexByte(x, '.') < 0 && strings.IndexByte(x, 'e') < 0 {
358 x = strings.TrimLeft(x, "0_")
359 if x == "i" {
360 x = "0i"
361 }
362 }
363 case "0X":
364 x = "0x" + x[2:]
365 fallthrough
366 case "0x":
367
368 if i := strings.LastIndexByte(x, 'P'); i >= 0 {
369 x = x[:i] + "p" + x[i+1:]
370 }
371 case "0O":
372 x = "0o" + x[2:]
373 case "0o":
374
375 case "0B":
376 x = "0b" + x[2:]
377 case "0b":
378
379 }
380
381 lit.Value = x
382 return false
383 }
384
View as plain text