]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.regabi] cmd/compile: clean up error API
authorRuss Cox <rsc@golang.org>
Mon, 16 Nov 2020 16:08:38 +0000 (11:08 -0500)
committerRuss Cox <rsc@golang.org>
Tue, 24 Nov 2020 15:07:09 +0000 (15:07 +0000)
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 <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
14 files changed:
src/_rex.20201123151057 [new file with mode: 0644]
src/cmd/compile/internal/gc/dcl.go
src/cmd/compile/internal/gc/go.go
src/cmd/compile/internal/gc/initorder.go
src/cmd/compile/internal/gc/lex.go
src/cmd/compile/internal/gc/main.go
src/cmd/compile/internal/gc/mpfloat.go
src/cmd/compile/internal/gc/mpint.go
src/cmd/compile/internal/gc/noder.go
src/cmd/compile/internal/gc/pgen.go
src/cmd/compile/internal/gc/print.go [new file with mode: 0644]
src/cmd/compile/internal/gc/subr.go
src/cmd/compile/internal/gc/typecheck.go
src/cmd/compile/internal/gc/walk.go

diff --git a/src/_rex.20201123151057 b/src/_rex.20201123151057
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
index e1dc647f824ab19df92b9cb0db0d35b05e72987f..d3b75902570843a907b91b2c398238f10d3daf64 100644 (file)
@@ -19,9 +19,6 @@ var externdcl []*Node
 
 func testdclstack() {
        if !types.IsDclstackValid() {
-               if nerrors != 0 {
-                       errorexit()
-               }
                Fatalf("mark left on the dclstack")
        }
 }
index da6b6d6e7274e2a6310ae9ddfefdaa89e31fdcc8..c53fde7e242bacb463ffb06e4cf822a95e064404 100644 (file)
@@ -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
index 2d7c0176d58fb1b7c4c17d3ba0d7e102c661c77f..102cb769db1a5b07de24d92adc9b2808484b4be4 100644 (file)
@@ -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")
index 7cce371408ae81f2e476da906e5197a454a0193f..c58479952ed1ab8bfaee1040226d4db43a61de85 100644 (file)
@@ -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))
 }
index d1b4161277ef9d0f46a4e8e4c240e3583512c240..89dbca0cf1b941aa3f2a7432949369d96f5b659e 100644 (file)
@@ -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("<autogenerated>", "<autogenerated>"), 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)
                        }
index 401aef319de7ccc58a2a5d8b3188dade255233d5..9962f4b41320bad7c85f5ac528a0f95c45894367 100644 (file)
@@ -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")
        }
 
index 340350bca7b2bf7db60039cb15e969015468c46d..79eb60e65d2b0e652a4fd1e41ebca03c722510a2 100644 (file)
@@ -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
index 67d24ef0bc70672be7c579b9ee211066b34d80c6..c7119f96f3eadbe762f6492d34793b9de459d4cf 100644 (file)
@@ -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
index 353f4b08c9dcb3b3ec91b8f0788e316c0be9874f..6dbb69281c4d472bd323c2aeb54a5a7bd63e4ae8 100644 (file)
@@ -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 (file)
index 0000000..1dbd58d
--- /dev/null
@@ -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()
+       }
+}
index defefd76b342b9f46fc6ce8431aecebd5be5477b..9760823e96e2a4e41df506bbb101d8c8b33742cd 100644 (file)
@@ -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.
 //
index 391115637ed1b095fd3094da5766b95fe4e0544a..41f0c3f2a59f0d4a7b5eb636d3d1456dc39937c8 100644 (file)
@@ -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)
index a7b6e7fcb3c7657ed7574cc267c63fe2ea42bca9..a61cb3f6517428b1f360d3a03eb3b25ff11a9916 100644 (file)
@@ -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())