github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/base/print.go (about) 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 "github.com/bir3/gocompiler/src/internal/buildcfg" 10 "os" 11 "runtime/debug" 12 "sort" 13 "strings" 14 15 "github.com/bir3/gocompiler/src/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 // Warn 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 Debug.Panic == 0 && 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://go.dev/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 // Assert reports "assertion failed" with Fatalf, unless b is true. 237 func Assert(b bool) { 238 if !b { 239 Fatalf("assertion failed") 240 } 241 } 242 243 // Assertf reports a fatal error with Fatalf, unless b is true. 244 func Assertf(b bool, format string, args ...interface{}) { 245 if !b { 246 Fatalf(format, args...) 247 } 248 } 249 250 // AssertfAt reports a fatal error with FatalfAt, unless b is true. 251 func AssertfAt(b bool, pos src.XPos, format string, args ...interface{}) { 252 if !b { 253 FatalfAt(pos, format, args...) 254 } 255 } 256 257 // hcrash crashes the compiler when -h is set, to find out where a message is generated. 258 func hcrash() { 259 if Flag.LowerH != 0 { 260 FlushErrors() 261 if Flag.LowerO != "" { 262 os.Remove(Flag.LowerO) 263 } 264 panic("-h") 265 } 266 } 267 268 // ErrorExit handles an error-status exit. 269 // It flushes any pending errors, removes the output file, and exits. 270 func ErrorExit() { 271 FlushErrors() 272 if Flag.LowerO != "" { 273 os.Remove(Flag.LowerO) 274 } 275 os.Exit(2) 276 } 277 278 // ExitIfErrors calls ErrorExit if any errors have been reported. 279 func ExitIfErrors() { 280 if Errors() > 0 { 281 ErrorExit() 282 } 283 } 284 285 var AutogeneratedPos src.XPos