]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/base/print.go
internal/buildcfg: move build configuration out of cmd/internal/objabi
[gostls13.git] / src / cmd / compile / internal / base / print.go
1 // Copyright 2020 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 package base
6
7 import (
8         "fmt"
9         "internal/buildcfg"
10         "os"
11         "runtime/debug"
12         "sort"
13         "strings"
14
15         "cmd/internal/src"
16 )
17
18 // An errorMsg is a queued error message, waiting to be printed.
19 type errorMsg struct {
20         pos src.XPos
21         msg string
22 }
23
24 // Pos is the current source position being processed,
25 // printed by Errorf, ErrorfLang, Fatalf, and Warnf.
26 var Pos src.XPos
27
28 var (
29         errorMsgs       []errorMsg
30         numErrors       int // number of entries in errorMsgs that are errors (as opposed to warnings)
31         numSyntaxErrors int
32 )
33
34 // Errors returns the number of errors reported.
35 func Errors() int {
36         return numErrors
37 }
38
39 // SyntaxErrors returns the number of syntax errors reported
40 func SyntaxErrors() int {
41         return numSyntaxErrors
42 }
43
44 // addErrorMsg adds a new errorMsg (which may be a warning) to errorMsgs.
45 func addErrorMsg(pos src.XPos, format string, args ...interface{}) {
46         msg := fmt.Sprintf(format, args...)
47         // Only add the position if know the position.
48         // See issue golang.org/issue/11361.
49         if pos.IsKnown() {
50                 msg = fmt.Sprintf("%v: %s", FmtPos(pos), msg)
51         }
52         errorMsgs = append(errorMsgs, errorMsg{
53                 pos: pos,
54                 msg: msg + "\n",
55         })
56 }
57
58 // FmtPos formats pos as a file:line string.
59 func FmtPos(pos src.XPos) string {
60         if Ctxt == nil {
61                 return "???"
62         }
63         return Ctxt.OutermostPos(pos).Format(Flag.C == 0, Flag.L == 1)
64 }
65
66 // byPos sorts errors by source position.
67 type byPos []errorMsg
68
69 func (x byPos) Len() int           { return len(x) }
70 func (x byPos) Less(i, j int) bool { return x[i].pos.Before(x[j].pos) }
71 func (x byPos) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
72
73 // FlushErrors sorts errors seen so far by line number, prints them to stdout,
74 // and empties the errors array.
75 func FlushErrors() {
76         if Ctxt != nil && Ctxt.Bso != nil {
77                 Ctxt.Bso.Flush()
78         }
79         if len(errorMsgs) == 0 {
80                 return
81         }
82         sort.Stable(byPos(errorMsgs))
83         for i, err := range errorMsgs {
84                 if i == 0 || err.msg != errorMsgs[i-1].msg {
85                         fmt.Printf("%s", err.msg)
86                 }
87         }
88         errorMsgs = errorMsgs[:0]
89 }
90
91 // lasterror keeps track of the most recently issued error,
92 // to avoid printing multiple error messages on the same line.
93 var lasterror struct {
94         syntax src.XPos // source position of last syntax error
95         other  src.XPos // source position of last non-syntax error
96         msg    string   // error message of last non-syntax error
97 }
98
99 // sameline reports whether two positions a, b are on the same line.
100 func sameline(a, b src.XPos) bool {
101         p := Ctxt.PosTable.Pos(a)
102         q := Ctxt.PosTable.Pos(b)
103         return p.Base() == q.Base() && p.Line() == q.Line()
104 }
105
106 // Errorf reports a formatted error at the current line.
107 func Errorf(format string, args ...interface{}) {
108         ErrorfAt(Pos, format, args...)
109 }
110
111 // ErrorfAt reports a formatted error message at pos.
112 func ErrorfAt(pos src.XPos, format string, args ...interface{}) {
113         msg := fmt.Sprintf(format, args...)
114
115         if strings.HasPrefix(msg, "syntax error") {
116                 numSyntaxErrors++
117                 // only one syntax error per line, no matter what error
118                 if sameline(lasterror.syntax, pos) {
119                         return
120                 }
121                 lasterror.syntax = pos
122         } else {
123                 // only one of multiple equal non-syntax errors per line
124                 // (FlushErrors shows only one of them, so we filter them
125                 // here as best as we can (they may not appear in order)
126                 // so that we don't count them here and exit early, and
127                 // then have nothing to show for.)
128                 if sameline(lasterror.other, pos) && lasterror.msg == msg {
129                         return
130                 }
131                 lasterror.other = pos
132                 lasterror.msg = msg
133         }
134
135         addErrorMsg(pos, "%s", msg)
136         numErrors++
137
138         hcrash()
139         if numErrors >= 10 && Flag.LowerE == 0 {
140                 FlushErrors()
141                 fmt.Printf("%v: too many errors\n", FmtPos(pos))
142                 ErrorExit()
143         }
144 }
145
146 // ErrorfVers reports that a language feature (format, args) requires a later version of Go.
147 func ErrorfVers(lang string, format string, args ...interface{}) {
148         Errorf("%s requires %s or later (-lang was set to %s; check go.mod)", fmt.Sprintf(format, args...), lang, Flag.Lang)
149 }
150
151 // UpdateErrorDot is a clumsy hack that rewrites the last error,
152 // if it was "LINE: undefined: NAME", to be "LINE: undefined: NAME in EXPR".
153 // It is used to give better error messages for dot (selector) expressions.
154 func UpdateErrorDot(line string, name, expr string) {
155         if len(errorMsgs) == 0 {
156                 return
157         }
158         e := &errorMsgs[len(errorMsgs)-1]
159         if strings.HasPrefix(e.msg, line) && e.msg == fmt.Sprintf("%v: undefined: %v\n", line, name) {
160                 e.msg = fmt.Sprintf("%v: undefined: %v in %v\n", line, name, expr)
161         }
162 }
163
164 // Warnf reports a formatted warning at the current line.
165 // In general the Go compiler does NOT generate warnings,
166 // so this should be used only when the user has opted in
167 // to additional output by setting a particular flag.
168 func Warn(format string, args ...interface{}) {
169         WarnfAt(Pos, format, args...)
170 }
171
172 // WarnfAt reports a formatted warning at pos.
173 // In general the Go compiler does NOT generate warnings,
174 // so this should be used only when the user has opted in
175 // to additional output by setting a particular flag.
176 func WarnfAt(pos src.XPos, format string, args ...interface{}) {
177         addErrorMsg(pos, format, args...)
178         if Flag.LowerM != 0 {
179                 FlushErrors()
180         }
181 }
182
183 // Fatalf reports a fatal error - an internal problem - at the current line and exits.
184 // If other errors have already been printed, then Fatalf just quietly exits.
185 // (The internal problem may have been caused by incomplete information
186 // after the already-reported errors, so best to let users fix those and
187 // try again without being bothered about a spurious internal error.)
188 //
189 // But if no errors have been printed, or if -d panic has been specified,
190 // Fatalf prints the error as an "internal compiler error". In a released build,
191 // it prints an error asking to file a bug report. In development builds, it
192 // prints a stack trace.
193 //
194 // If -h has been specified, Fatalf panics to force the usual runtime info dump.
195 func Fatalf(format string, args ...interface{}) {
196         FatalfAt(Pos, format, args...)
197 }
198
199 // FatalfAt reports a fatal error - an internal problem - at pos and exits.
200 // If other errors have already been printed, then FatalfAt just quietly exits.
201 // (The internal problem may have been caused by incomplete information
202 // after the already-reported errors, so best to let users fix those and
203 // try again without being bothered about a spurious internal error.)
204 //
205 // But if no errors have been printed, or if -d panic has been specified,
206 // FatalfAt prints the error as an "internal compiler error". In a released build,
207 // it prints an error asking to file a bug report. In development builds, it
208 // prints a stack trace.
209 //
210 // If -h has been specified, FatalfAt panics to force the usual runtime info dump.
211 func FatalfAt(pos src.XPos, format string, args ...interface{}) {
212         FlushErrors()
213
214         if Debug.Panic != 0 || numErrors == 0 {
215                 fmt.Printf("%v: internal compiler error: ", FmtPos(pos))
216                 fmt.Printf(format, args...)
217                 fmt.Printf("\n")
218
219                 // If this is a released compiler version, ask for a bug report.
220                 if strings.HasPrefix(buildcfg.Version, "go") {
221                         fmt.Printf("\n")
222                         fmt.Printf("Please file a bug report including a short program that triggers the error.\n")
223                         fmt.Printf("https://golang.org/issue/new\n")
224                 } else {
225                         // Not a release; dump a stack trace, too.
226                         fmt.Println()
227                         os.Stdout.Write(debug.Stack())
228                         fmt.Println()
229                 }
230         }
231
232         hcrash()
233         ErrorExit()
234 }
235
236 // hcrash crashes the compiler when -h is set, to find out where a message is generated.
237 func hcrash() {
238         if Flag.LowerH != 0 {
239                 FlushErrors()
240                 if Flag.LowerO != "" {
241                         os.Remove(Flag.LowerO)
242                 }
243                 panic("-h")
244         }
245 }
246
247 // ErrorExit handles an error-status exit.
248 // It flushes any pending errors, removes the output file, and exits.
249 func ErrorExit() {
250         FlushErrors()
251         if Flag.LowerO != "" {
252                 os.Remove(Flag.LowerO)
253         }
254         os.Exit(2)
255 }
256
257 // ExitIfErrors calls ErrorExit if any errors have been reported.
258 func ExitIfErrors() {
259         if Errors() > 0 {
260                 ErrorExit()
261         }
262 }
263
264 var AutogeneratedPos src.XPos