From 357c576878137c8840b702c64167470f1669f064 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 16 Nov 2020 11:08:38 -0500 Subject: [PATCH] [dev.regabi] cmd/compile: clean up error API Prepare for factoring the error API out of this package by cleaning it up. The doc comments use the intended new names, which will be introduced in the next CL. Change-Id: Ie4c8d4262422da32a9a9f750fda42c225b6b42a8 Reviewed-on: https://go-review.googlesource.com/c/go/+/272248 Trust: Russ Cox Reviewed-by: Matthew Dempsky --- src/_rex.20201123151057 | 1 + src/cmd/compile/internal/gc/dcl.go | 3 - src/cmd/compile/internal/gc/go.go | 10 - src/cmd/compile/internal/gc/initorder.go | 4 +- src/cmd/compile/internal/gc/lex.go | 4 - src/cmd/compile/internal/gc/main.go | 44 +--- src/cmd/compile/internal/gc/mpfloat.go | 4 +- src/cmd/compile/internal/gc/mpint.go | 24 +-- src/cmd/compile/internal/gc/noder.go | 4 +- src/cmd/compile/internal/gc/pgen.go | 9 +- src/cmd/compile/internal/gc/print.go | 243 +++++++++++++++++++++++ src/cmd/compile/internal/gc/subr.go | 174 ---------------- src/cmd/compile/internal/gc/typecheck.go | 10 +- src/cmd/compile/internal/gc/walk.go | 3 +- 14 files changed, 283 insertions(+), 254 deletions(-) create mode 100644 src/_rex.20201123151057 create mode 100644 src/cmd/compile/internal/gc/print.go diff --git a/src/_rex.20201123151057 b/src/_rex.20201123151057 new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/_rex.20201123151057 @@ -0,0 +1 @@ + diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index e1dc647f82..d3b7590257 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -19,9 +19,6 @@ var externdcl []*Node func testdclstack() { if !types.IsDclstackValid() { - if nerrors != 0 { - errorexit() - } Fatalf("mark left on the dclstack") } } diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index da6b6d6e72..c53fde7e24 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -102,16 +102,6 @@ var pragcgobuf [][]string var outfile string var linkobj string -// nerrors is the number of compiler errors reported -// since the last call to saveerrors. -var nerrors int - -// nsavederrors is the total number of compiler errors -// reported before the last call to saveerrors. -var nsavederrors int - -var nsyntaxerrors int - var decldepth int32 var nolocalimports bool diff --git a/src/cmd/compile/internal/gc/initorder.go b/src/cmd/compile/internal/gc/initorder.go index 2d7c0176d5..102cb769db 100644 --- a/src/cmd/compile/internal/gc/initorder.go +++ b/src/cmd/compile/internal/gc/initorder.go @@ -104,9 +104,7 @@ func initOrder(l []*Node) []*Node { // confused us and there might not be // a loop. Let the user fix those // first. - if nerrors > 0 { - errorexit() - } + ExitIfErrors() findInitLoopAndExit(firstLHS(n), new([]*Node)) Fatalf("initialization unfinished, but failed to identify loop") diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index 7cce371408..c58479952e 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -12,10 +12,6 @@ import ( "strings" ) -// lineno is the source position at the start of the most recently lexed token. -// TODO(gri) rename and eventually remove -var lineno src.XPos - func makePos(base *src.PosBase, line, col uint) src.XPos { return Ctxt.PosTable.XPos(src.MakePos(base, line, col)) } diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index d1b4161277..89dbca0cf1 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -119,7 +119,7 @@ func usage() { } func hidePanic() { - if Debug_panic == 0 && nsavederrors+nerrors > 0 { + if Debug_panic == 0 && Errors() > 0 { // If we've already complained about things // in the program, don't bother complaining // about a panic too; let the user clean up @@ -567,7 +567,6 @@ func Main(archInit func(*Arch)) { initUniverse() dclcontext = PEXTERN - nerrors = 0 autogeneratedPos = makePos(src.NewFileBase("", ""), 1, 0) @@ -625,10 +624,10 @@ func Main(archInit func(*Arch)) { if n.Op == ODCLFUNC { Curfn = n decldepth = 1 - saveerrors() + errorsBefore := Errors() typecheckslice(Curfn.Nbody.Slice(), ctxStmt) checkreturn(Curfn) - if nerrors != 0 { + if Errors() > errorsBefore { Curfn.Nbody.Set(nil) // type errors; do not compile } // Now that we've checked whether n terminates, @@ -641,11 +640,9 @@ func Main(archInit func(*Arch)) { // check past phase 9 isn't sufficient, as we may exit with other errors // before then, thus skipping map key errors. checkMapKeys() - timings.AddEvent(fcount, "funcs") + ExitIfErrors() - if nsavederrors+nerrors != 0 { - errorexit() - } + timings.AddEvent(fcount, "funcs") fninit(xtop) @@ -660,12 +657,8 @@ func Main(archInit func(*Arch)) { } } capturevarscomplete = true - Curfn = nil - - if nsavederrors+nerrors != 0 { - errorexit() - } + ExitIfErrors() // Phase 5: Inlining timings.Start("fe", "inlining") @@ -674,14 +667,10 @@ func Main(archInit func(*Arch)) { // otherwise lazily when used or re-exported. for _, n := range importlist { if n.Func.Inl != nil { - saveerrors() typecheckinl(n) } } - - if nsavederrors+nerrors != 0 { - errorexit() - } + ExitIfErrors() } if Debug.l != 0 { @@ -793,10 +782,7 @@ func Main(archInit func(*Arch)) { // Check the map keys again, since we typechecked the external // declarations. checkMapKeys() - - if nerrors+nsavederrors != 0 { - errorexit() - } + ExitIfErrors() // Write object data to disk. timings.Start("be", "dumpobj") @@ -827,10 +813,7 @@ func Main(archInit func(*Arch)) { } logopt.FlushLoggedOpts(Ctxt, myimportpath) - - if nerrors+nsavederrors != 0 { - errorexit() - } + ExitIfErrors() flusherrors() timings.Stop() @@ -1011,11 +994,6 @@ func readSymABIs(file, myimportpath string) { } } -func saveerrors() { - nsavederrors += nerrors - nerrors = 0 -} - func arsize(b *bufio.Reader, name string) int { var buf [ArhdrSize]byte if _, err := io.ReadFull(b, buf[:]); err != nil { @@ -1396,7 +1374,7 @@ func clearImports() { // leave s->block set to cause redeclaration // errors if a conflicting top-level name is // introduced by a different file. - if !n.Name.Used() && nsyntaxerrors == 0 { + if !n.Name.Used() && SyntaxErrors() == 0 { unused = append(unused, importedPkg{n.Pos, n.Name.Pkg.Path, s.Name}) } s.Def = nil @@ -1405,7 +1383,7 @@ func clearImports() { if IsAlias(s) { // throw away top-level name left over // from previous import . "x" - if n.Name != nil && n.Name.Pack != nil && !n.Name.Pack.Name.Used() && nsyntaxerrors == 0 { + if n.Name != nil && n.Name.Pack != nil && !n.Name.Pack.Name.Used() && SyntaxErrors() == 0 { unused = append(unused, importedPkg{n.Name.Pack.Pos, n.Name.Pack.Name.Pkg.Path, ""}) n.Name.Pack.Name.SetUsed(true) } diff --git a/src/cmd/compile/internal/gc/mpfloat.go b/src/cmd/compile/internal/gc/mpfloat.go index 401aef319d..9962f4b413 100644 --- a/src/cmd/compile/internal/gc/mpfloat.go +++ b/src/cmd/compile/internal/gc/mpfloat.go @@ -136,7 +136,7 @@ func (a *Mpflt) Float64() float64 { x, _ := a.Val.Float64() // check for overflow - if math.IsInf(x, 0) && nsavederrors+nerrors == 0 { + if math.IsInf(x, 0) && Errors() == 0 { Fatalf("ovf in Mpflt Float64") } @@ -148,7 +148,7 @@ func (a *Mpflt) Float32() float64 { x := float64(x32) // check for overflow - if math.IsInf(x, 0) && nsavederrors+nerrors == 0 { + if math.IsInf(x, 0) && Errors() == 0 { Fatalf("ovf in Mpflt Float32") } diff --git a/src/cmd/compile/internal/gc/mpint.go b/src/cmd/compile/internal/gc/mpint.go index 340350bca7..79eb60e65d 100644 --- a/src/cmd/compile/internal/gc/mpint.go +++ b/src/cmd/compile/internal/gc/mpint.go @@ -72,7 +72,7 @@ func (a *Mpint) SetFloat(b *Mpflt) bool { func (a *Mpint) Add(b *Mpint) { if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { + if Errors() == 0 { Fatalf("ovf in Mpint Add") } a.SetOverflow() @@ -88,7 +88,7 @@ func (a *Mpint) Add(b *Mpint) { func (a *Mpint) Sub(b *Mpint) { if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { + if Errors() == 0 { Fatalf("ovf in Mpint Sub") } a.SetOverflow() @@ -104,7 +104,7 @@ func (a *Mpint) Sub(b *Mpint) { func (a *Mpint) Mul(b *Mpint) { if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { + if Errors() == 0 { Fatalf("ovf in Mpint Mul") } a.SetOverflow() @@ -120,7 +120,7 @@ func (a *Mpint) Mul(b *Mpint) { func (a *Mpint) Quo(b *Mpint) { if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { + if Errors() == 0 { Fatalf("ovf in Mpint Quo") } a.SetOverflow() @@ -137,7 +137,7 @@ func (a *Mpint) Quo(b *Mpint) { func (a *Mpint) Rem(b *Mpint) { if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { + if Errors() == 0 { Fatalf("ovf in Mpint Rem") } a.SetOverflow() @@ -154,7 +154,7 @@ func (a *Mpint) Rem(b *Mpint) { func (a *Mpint) Or(b *Mpint) { if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { + if Errors() == 0 { Fatalf("ovf in Mpint Or") } a.SetOverflow() @@ -166,7 +166,7 @@ func (a *Mpint) Or(b *Mpint) { func (a *Mpint) And(b *Mpint) { if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { + if Errors() == 0 { Fatalf("ovf in Mpint And") } a.SetOverflow() @@ -178,7 +178,7 @@ func (a *Mpint) And(b *Mpint) { func (a *Mpint) AndNot(b *Mpint) { if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { + if Errors() == 0 { Fatalf("ovf in Mpint AndNot") } a.SetOverflow() @@ -190,7 +190,7 @@ func (a *Mpint) AndNot(b *Mpint) { func (a *Mpint) Xor(b *Mpint) { if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { + if Errors() == 0 { Fatalf("ovf in Mpint Xor") } a.SetOverflow() @@ -202,7 +202,7 @@ func (a *Mpint) Xor(b *Mpint) { func (a *Mpint) Lsh(b *Mpint) { if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { + if Errors() == 0 { Fatalf("ovf in Mpint Lsh") } a.SetOverflow() @@ -229,7 +229,7 @@ func (a *Mpint) Lsh(b *Mpint) { func (a *Mpint) Rsh(b *Mpint) { if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { + if Errors() == 0 { Fatalf("ovf in Mpint Rsh") } a.SetOverflow() @@ -267,7 +267,7 @@ func (a *Mpint) Neg() { func (a *Mpint) Int64() int64 { if a.Ovf { - if nsavederrors+nerrors == 0 { + if Errors() == 0 { Fatalf("constant overflow") } return 0 diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index 67d24ef0bc..c7119f96f3 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -64,7 +64,7 @@ func parseFiles(filenames []string) uint { lines += p.file.Lines p.file = nil // release memory - if nsyntaxerrors != 0 { + if SyntaxErrors() != 0 { errorexit() } // Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure. @@ -333,7 +333,7 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) { val := p.basicLit(imp.Path) ipkg := importfile(&val) if ipkg == nil { - if nerrors == 0 { + if Errors() == 0 { Fatalf("phase error in import") } return diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index 353f4b08c9..6dbb69281c 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -198,7 +198,7 @@ func funccompile(fn *Node) { } if fn.Type == nil { - if nerrors == 0 { + if Errors() == 0 { Fatalf("funccompile missing type") } return @@ -224,10 +224,9 @@ func funccompile(fn *Node) { } func compile(fn *Node) { - saveerrors() - + errorsBefore := Errors() order(fn) - if nerrors != 0 { + if Errors() > errorsBefore { return } @@ -237,7 +236,7 @@ func compile(fn *Node) { fn.Func.initLSym(true) walk(fn) - if nerrors != 0 { + if Errors() > errorsBefore { return } if instrumenting { diff --git a/src/cmd/compile/internal/gc/print.go b/src/cmd/compile/internal/gc/print.go new file mode 100644 index 0000000000..1dbd58df42 --- /dev/null +++ b/src/cmd/compile/internal/gc/print.go @@ -0,0 +1,243 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gc + +import ( + "cmd/internal/objabi" + "cmd/internal/src" + "fmt" + "os" + "runtime/debug" + "sort" + "strings" +) + +// An errorMsg is a queued error message, waiting to be printed. +type errorMsg struct { + pos src.XPos + msg string +} + +// Pos is the current source position being processed, +// printed by Errorf, ErrorfLang, Fatalf, and Warnf. +var lineno src.XPos + +var ( + errorMsgs []errorMsg + numErrors int // number of entries in errorMsgs that are errors (as opposed to warnings) + numSyntaxErrors int +) + +// Errors returns the number of errors reported. +func Errors() int { + return numErrors +} + +// SyntaxErrors returns the number of syntax errors reported +func SyntaxErrors() int { + return numSyntaxErrors +} + +// addErrorMsg adds a new errorMsg (which may be a warning) to errorMsgs. +func addErrorMsg(pos src.XPos, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + // Only add the position if know the position. + // See issue golang.org/issue/11361. + if pos.IsKnown() { + msg = fmt.Sprintf("%v: %s", linestr(pos), msg) + } + errorMsgs = append(errorMsgs, errorMsg{ + pos: pos, + msg: msg + "\n", + }) +} + +// FmtPos formats pos as a file:line string. +func linestr(pos src.XPos) string { + if Ctxt == nil { + return "???" + } + return Ctxt.OutermostPos(pos).Format(Debug.C == 0, Debug.L == 1) +} + +// byPos sorts errors by source position. +type byPos []errorMsg + +func (x byPos) Len() int { return len(x) } +func (x byPos) Less(i, j int) bool { return x[i].pos.Before(x[j].pos) } +func (x byPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +// FlushErrors sorts errors seen so far by line number, prints them to stdout, +// and empties the errors array. +func flusherrors() { + Ctxt.Bso.Flush() + if len(errorMsgs) == 0 { + return + } + sort.Stable(byPos(errorMsgs)) + for i, err := range errorMsgs { + if i == 0 || err.msg != errorMsgs[i-1].msg { + fmt.Printf("%s", err.msg) + } + } + errorMsgs = errorMsgs[:0] +} + +// lasterror keeps track of the most recently issued error, +// to avoid printing multiple error messages on the same line. +var lasterror struct { + syntax src.XPos // source position of last syntax error + other src.XPos // source position of last non-syntax error + msg string // error message of last non-syntax error +} + +// sameline reports whether two positions a, b are on the same line. +func sameline(a, b src.XPos) bool { + p := Ctxt.PosTable.Pos(a) + q := Ctxt.PosTable.Pos(b) + return p.Base() == q.Base() && p.Line() == q.Line() +} + +// Errorf reports a formatted error at the current line. +func yyerror(format string, args ...interface{}) { + yyerrorl(lineno, format, args...) +} + +// ErrorfAt reports a formatted error message at pos. +func yyerrorl(pos src.XPos, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + + if strings.HasPrefix(msg, "syntax error") { + numSyntaxErrors++ + // only one syntax error per line, no matter what error + if sameline(lasterror.syntax, pos) { + return + } + lasterror.syntax = pos + } else { + // only one of multiple equal non-syntax errors per line + // (flusherrors shows only one of them, so we filter them + // here as best as we can (they may not appear in order) + // so that we don't count them here and exit early, and + // then have nothing to show for.) + if sameline(lasterror.other, pos) && lasterror.msg == msg { + return + } + lasterror.other = pos + lasterror.msg = msg + } + + addErrorMsg(pos, "%s", msg) + numErrors++ + + hcrash() + if numErrors >= 10 && Debug.e == 0 { + flusherrors() + fmt.Printf("%v: too many errors\n", linestr(pos)) + errorexit() + } +} + +// ErrorfVers reports that a language feature (format, args) requires a later version of Go. +func yyerrorv(lang string, format string, args ...interface{}) { + yyerror("%s requires %s or later (-lang was set to %s; check go.mod)", fmt.Sprintf(format, args...), lang, flag_lang) +} + +// UpdateErrorDot is a clumsy hack that rewrites the last error, +// if it was "LINE: undefined: NAME", to be "LINE: undefined: NAME in EXPR". +// It is used to give better error messages for dot (selector) expressions. +func UpdateErrorDot(line string, name, expr string) { + if len(errorMsgs) == 0 { + return + } + e := &errorMsgs[len(errorMsgs)-1] + if strings.HasPrefix(e.msg, line) && e.msg == fmt.Sprintf("%v: undefined: %v\n", line, name) { + e.msg = fmt.Sprintf("%v: undefined: %v in %v\n", line, name, expr) + } +} + +// Warnf reports a formatted warning at the current line. +// In general the Go compiler does NOT generate warnings, +// so this should be used only when the user has opted in +// to additional output by setting a particular flag. +func Warn(format string, args ...interface{}) { + Warnl(lineno, format, args...) +} + +// WarnfAt reports a formatted warning at pos. +// In general the Go compiler does NOT generate warnings, +// so this should be used only when the user has opted in +// to additional output by setting a particular flag. +func Warnl(pos src.XPos, format string, args ...interface{}) { + addErrorMsg(pos, format, args...) + if Debug.m != 0 { + flusherrors() + } +} + +// Fatal reports a fatal error - an internal problem - at the current line and exits. +// If other errors have already been printed, then Fatal just quietly exits. +// (The internal problem may have been caused by incomplete information +// after the already-reported errors, so best to let users fix those and +// try again without being bothered about a spurious internal error.) +// +// But if no errors have been printed, or if -d panic has been specified, +// Fatal prints the error as an "internal compiler error". In a released build, +// it prints an error asking to file a bug report. In development builds, it +// prints a stack trace. +// +// If -h has been specified, Fatal panics to force the usual runtime info dump. +func Fatalf(format string, args ...interface{}) { + flusherrors() + + if Debug_panic != 0 || numErrors == 0 { + fmt.Printf("%v: internal compiler error: ", linestr(lineno)) + fmt.Printf(format, args...) + fmt.Printf("\n") + + // If this is a released compiler version, ask for a bug report. + if strings.HasPrefix(objabi.Version, "go") { + fmt.Printf("\n") + fmt.Printf("Please file a bug report including a short program that triggers the error.\n") + fmt.Printf("https://golang.org/issue/new\n") + } else { + // Not a release; dump a stack trace, too. + fmt.Println() + os.Stdout.Write(debug.Stack()) + fmt.Println() + } + } + + hcrash() + errorexit() +} + +// hcrash crashes the compiler when -h is set, to find out where a message is generated. +func hcrash() { + if Debug.h != 0 { + flusherrors() + if outfile != "" { + os.Remove(outfile) + } + panic("-h") + } +} + +// ErrorExit handles an error-status exit. +// It flushes any pending errors, removes the output file, and exits. +func errorexit() { + flusherrors() + if outfile != "" { + os.Remove(outfile) + } + os.Exit(2) +} + +// ExitIfErrors calls ErrorExit if any errors have been reported. +func ExitIfErrors() { + if Errors() > 0 { + errorexit() + } +} diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index defefd76b3..9760823e96 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -6,13 +6,10 @@ package gc import ( "cmd/compile/internal/types" - "cmd/internal/objabi" "cmd/internal/src" "crypto/md5" "encoding/binary" "fmt" - "os" - "runtime/debug" "sort" "strconv" "strings" @@ -21,13 +18,6 @@ import ( "unicode/utf8" ) -type Error struct { - pos src.XPos - msg string -} - -var errors []Error - // largeStack is info about a function whose stack frame is too large (rare). type largeStack struct { locals int64 @@ -41,170 +31,6 @@ var ( largeStackFrames []largeStack ) -func errorexit() { - flusherrors() - if outfile != "" { - os.Remove(outfile) - } - os.Exit(2) -} - -func adderrorname(n *Node) { - if n.Op != ODOT { - return - } - old := fmt.Sprintf("%v: undefined: %v\n", n.Line(), n.Left) - if len(errors) > 0 && errors[len(errors)-1].pos.Line() == n.Pos.Line() && errors[len(errors)-1].msg == old { - errors[len(errors)-1].msg = fmt.Sprintf("%v: undefined: %v in %v\n", n.Line(), n.Left, n) - } -} - -func adderr(pos src.XPos, format string, args ...interface{}) { - msg := fmt.Sprintf(format, args...) - // Only add the position if know the position. - // See issue golang.org/issue/11361. - if pos.IsKnown() { - msg = fmt.Sprintf("%v: %s", linestr(pos), msg) - } - errors = append(errors, Error{ - pos: pos, - msg: msg + "\n", - }) -} - -// byPos sorts errors by source position. -type byPos []Error - -func (x byPos) Len() int { return len(x) } -func (x byPos) Less(i, j int) bool { return x[i].pos.Before(x[j].pos) } -func (x byPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] } - -// flusherrors sorts errors seen so far by line number, prints them to stdout, -// and empties the errors array. -func flusherrors() { - Ctxt.Bso.Flush() - if len(errors) == 0 { - return - } - sort.Stable(byPos(errors)) - for i, err := range errors { - if i == 0 || err.msg != errors[i-1].msg { - fmt.Printf("%s", err.msg) - } - } - errors = errors[:0] -} - -func hcrash() { - if Debug.h != 0 { - flusherrors() - if outfile != "" { - os.Remove(outfile) - } - var x *int - *x = 0 - } -} - -func linestr(pos src.XPos) string { - return Ctxt.OutermostPos(pos).Format(Debug.C == 0, Debug.L == 1) -} - -// lasterror keeps track of the most recently issued error. -// It is used to avoid multiple error messages on the same -// line. -var lasterror struct { - syntax src.XPos // source position of last syntax error - other src.XPos // source position of last non-syntax error - msg string // error message of last non-syntax error -} - -// sameline reports whether two positions a, b are on the same line. -func sameline(a, b src.XPos) bool { - p := Ctxt.PosTable.Pos(a) - q := Ctxt.PosTable.Pos(b) - return p.Base() == q.Base() && p.Line() == q.Line() -} - -func yyerrorl(pos src.XPos, format string, args ...interface{}) { - msg := fmt.Sprintf(format, args...) - - if strings.HasPrefix(msg, "syntax error") { - nsyntaxerrors++ - // only one syntax error per line, no matter what error - if sameline(lasterror.syntax, pos) { - return - } - lasterror.syntax = pos - } else { - // only one of multiple equal non-syntax errors per line - // (flusherrors shows only one of them, so we filter them - // here as best as we can (they may not appear in order) - // so that we don't count them here and exit early, and - // then have nothing to show for.) - if sameline(lasterror.other, pos) && lasterror.msg == msg { - return - } - lasterror.other = pos - lasterror.msg = msg - } - - adderr(pos, "%s", msg) - - hcrash() - nerrors++ - if nsavederrors+nerrors >= 10 && Debug.e == 0 { - flusherrors() - fmt.Printf("%v: too many errors\n", linestr(pos)) - errorexit() - } -} - -func yyerrorv(lang string, format string, args ...interface{}) { - what := fmt.Sprintf(format, args...) - yyerrorl(lineno, "%s requires %s or later (-lang was set to %s; check go.mod)", what, lang, flag_lang) -} - -func yyerror(format string, args ...interface{}) { - yyerrorl(lineno, format, args...) -} - -func Warn(fmt_ string, args ...interface{}) { - Warnl(lineno, fmt_, args...) -} - -func Warnl(line src.XPos, fmt_ string, args ...interface{}) { - adderr(line, fmt_, args...) - if Debug.m != 0 { - flusherrors() - } -} - -func Fatalf(fmt_ string, args ...interface{}) { - flusherrors() - - if Debug_panic != 0 || nsavederrors+nerrors == 0 { - fmt.Printf("%v: internal compiler error: ", linestr(lineno)) - fmt.Printf(fmt_, args...) - fmt.Printf("\n") - - // If this is a released compiler version, ask for a bug report. - if strings.HasPrefix(objabi.Version, "go") { - fmt.Printf("\n") - fmt.Printf("Please file a bug report including a short program that triggers the error.\n") - fmt.Printf("https://golang.org/issue/new\n") - } else { - // Not a release; dump a stack trace, too. - fmt.Println() - os.Stdout.Write(debug.Stack()) - fmt.Println() - } - } - - hcrash() - errorexit() -} - // hasUniquePos reports whether n has a unique position that can be // used for reporting error messages. // diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 391115637e..41f0c3f2a5 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -280,7 +280,7 @@ func typecheck(n *Node, top int) (res *Node) { yyerrorl(n.Pos, "constant definition loop%s", cycleTrace(cycleFor(n))) } - if nsavederrors+nerrors == 0 { + if Errors() == 0 { var trace string for i := len(typecheck_tcstack) - 1; i >= 0; i-- { x := typecheck_tcstack[i] @@ -891,7 +891,7 @@ func typecheck1(n *Node, top int) (res *Node) { t := n.Left.Type if t == nil { - adderrorname(n) + UpdateErrorDot(n.Line(), n.Left.String(), n.String()) n.Type = nil return n } @@ -3641,7 +3641,7 @@ func typecheckdef(n *Node) { if n.SubOp() != 0 { // like OPRINTN break } - if nsavederrors+nerrors > 0 { + if Errors() > 0 { // Can have undefined variables in x := foo // that make x have an n.name.Defn == nil. // If there are other errors anyway, don't @@ -3686,9 +3686,9 @@ func typecheckdef(n *Node) { n.SetWalkdef(1) setTypeNode(n, types.New(TFORW)) n.Type.Sym = n.Sym - nerrors0 := nerrors + errorsBefore := Errors() typecheckdeftype(n) - if n.Type.Etype == TFORW && nerrors > nerrors0 { + if n.Type.Etype == TFORW && Errors() > errorsBefore { // Something went wrong during type-checking, // but it was reported. Silence future errors. n.Type.SetBroke(true) diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index a7b6e7fcb3..a61cb3f651 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -20,6 +20,7 @@ const zeroValSize = 1024 // must match value of runtime/map.go:maxZero func walk(fn *Node) { Curfn = fn + errorsBefore := Errors() if Debug.W != 0 { s := fmt.Sprintf("\nbefore walk %v", Curfn.Func.Nname.Sym) @@ -59,7 +60,7 @@ func walk(fn *Node) { } lineno = lno - if nerrors != 0 { + if Errors() > errorsBefore { return } walkstmtlist(Curfn.Nbody.Slice()) -- 2.48.1