Source file
src/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/fs"
18 "os"
19 "path/filepath"
20 "runtime"
21 "runtime/pprof"
22 "strings"
23
24 "cmd/internal/diff"
25 )
26
27 var (
28
29 list = flag.Bool("l", false, "list files whose formatting differs from gofmt's")
30 write = flag.Bool("w", false, "write result to (source) file instead of stdout")
31 rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')")
32 simplifyAST = flag.Bool("s", false, "simplify code")
33 doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
34 allErrors = flag.Bool("e", false, "report all errors (not just the first 10 on different lines)")
35
36
37 cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
38 )
39
40
41 const (
42 tabWidth = 8
43 printerMode = printer.UseSpaces | printer.TabIndent | printerNormalizeNumbers
44
45
46
47
48
49 printerNormalizeNumbers = 1 << 30
50 )
51
52 var (
53 fileSet = token.NewFileSet()
54 exitCode = 0
55 rewrite func(*ast.File) *ast.File
56 parserMode parser.Mode
57 )
58
59 func report(err error) {
60 scanner.PrintError(os.Stderr, err)
61 exitCode = 2
62 }
63
64 func usage() {
65 fmt.Fprintf(os.Stderr, "usage: gofmt [flags] [path ...]\n")
66 flag.PrintDefaults()
67 }
68
69 func initParserMode() {
70 parserMode = parser.ParseComments
71 if *allErrors {
72 parserMode |= parser.AllErrors
73 }
74 }
75
76 func isGoFile(f fs.DirEntry) bool {
77
78 name := f.Name()
79 return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
80 }
81
82
83 func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error {
84 var perm fs.FileMode = 0644
85 if in == nil {
86 f, err := os.Open(filename)
87 if err != nil {
88 return err
89 }
90 defer f.Close()
91 fi, err := f.Stat()
92 if err != nil {
93 return err
94 }
95 in = f
96 perm = fi.Mode().Perm()
97 }
98
99 src, err := io.ReadAll(in)
100 if err != nil {
101 return err
102 }
103
104 file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, stdin)
105 if err != nil {
106 return err
107 }
108
109 if rewrite != nil {
110 if sourceAdj == nil {
111 file = rewrite(file)
112 } else {
113 fmt.Fprintf(os.Stderr, "warning: rewrite ignored for incomplete programs\n")
114 }
115 }
116
117 ast.SortImports(fileSet, file)
118
119 if *simplifyAST {
120 simplify(file)
121 }
122
123 res, err := format(fileSet, file, sourceAdj, indentAdj, src, printer.Config{Mode: printerMode, Tabwidth: tabWidth})
124 if err != nil {
125 return err
126 }
127
128 if !bytes.Equal(src, res) {
129
130 if *list {
131 fmt.Fprintln(out, filename)
132 }
133 if *write {
134
135 bakname, err := backupFile(filename+".", src, perm)
136 if err != nil {
137 return err
138 }
139 err = os.WriteFile(filename, res, perm)
140 if err != nil {
141 os.Rename(bakname, filename)
142 return err
143 }
144 err = os.Remove(bakname)
145 if err != nil {
146 return err
147 }
148 }
149 if *doDiff {
150 data, err := diffWithReplaceTempFile(src, res, filename)
151 if err != nil {
152 return fmt.Errorf("computing diff: %s", err)
153 }
154 fmt.Fprintf(out, "diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename))
155 out.Write(data)
156 }
157 }
158
159 if !*list && !*write && !*doDiff {
160 _, err = out.Write(res)
161 }
162
163 return err
164 }
165
166 func visitFile(path string, f fs.DirEntry, err error) error {
167 if err != nil || !isGoFile(f) {
168 return err
169 }
170 if err := processFile(path, nil, os.Stdout, false); err != nil {
171 report(err)
172 }
173 return nil
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 args := flag.Args()
204 if len(args) == 0 {
205 if *write {
206 fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input")
207 exitCode = 2
208 return
209 }
210 if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil {
211 report(err)
212 }
213 return
214 }
215
216 for _, arg := range args {
217 switch info, err := os.Stat(arg); {
218 case err != nil:
219 report(err)
220 case !info.IsDir():
221
222 if err := processFile(arg, nil, os.Stdout, false); err != nil {
223 report(err)
224 }
225 default:
226
227 if err := filepath.WalkDir(arg, visitFile); err != nil {
228 report(err)
229 }
230 }
231 }
232 }
233
234 func diffWithReplaceTempFile(b1, b2 []byte, filename string) ([]byte, error) {
235 data, err := diff.Diff("gofmt", b1, b2)
236 if len(data) > 0 {
237 return replaceTempFilename(data, filename)
238 }
239 return data, err
240 }
241
242
243
244
245
246
247
248
249
250
251 func replaceTempFilename(diff []byte, filename string) ([]byte, error) {
252 bs := bytes.SplitN(diff, []byte{'\n'}, 3)
253 if len(bs) < 3 {
254 return nil, fmt.Errorf("got unexpected diff for %s", filename)
255 }
256
257 var t0, t1 []byte
258 if i := bytes.LastIndexByte(bs[0], '\t'); i != -1 {
259 t0 = bs[0][i:]
260 }
261 if i := bytes.LastIndexByte(bs[1], '\t'); i != -1 {
262 t1 = bs[1][i:]
263 }
264
265 f := filepath.ToSlash(filename)
266 bs[0] = []byte(fmt.Sprintf("--- %s%s", f+".orig", t0))
267 bs[1] = []byte(fmt.Sprintf("+++ %s%s", f, t1))
268 return bytes.Join(bs, []byte{'\n'}), nil
269 }
270
271 const chmodSupported = runtime.GOOS != "windows"
272
273
274
275
276 func backupFile(filename string, data []byte, perm fs.FileMode) (string, error) {
277
278 f, err := os.CreateTemp(filepath.Dir(filename), filepath.Base(filename))
279 if err != nil {
280 return "", err
281 }
282 bakname := f.Name()
283 if chmodSupported {
284 err = f.Chmod(perm)
285 if err != nil {
286 f.Close()
287 os.Remove(bakname)
288 return bakname, err
289 }
290 }
291
292
293 _, err = f.Write(data)
294 if err1 := f.Close(); err == nil {
295 err = err1
296 }
297
298 return bakname, err
299 }
300
View as plain text