diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index a6963a3d66..dbb4aaa1e9 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -1352,9 +1352,9 @@ func pkgnotused(lineno src.XPos, path string, name string) { elem = elem[i+1:] } if name == "" || elem == name { - yyerrorl(lineno, "imported and not used: %q", path) + yywarningl(lineno, "imported and not used: %q", path) } else { - yyerrorl(lineno, "imported and not used: %q as %s", path, name) + yywarningl(lineno, "imported and not used: %q as %s", path, name) } } diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index defefd76b3..ed6e2bba53 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -26,6 +26,7 @@ type Error struct { msg string } +var warnings []Error var errors []Error // largeStack is info about a function whose stack frame is too large (rare). @@ -59,6 +60,13 @@ func adderrorname(n *Node) { } } +func addwarn(pos src.XPos, format string, args ...interface{}) { + warnings = append(warnings, Error{ + pos: pos, + msg: fmt.Sprintf("%v: warning: %s\n", linestr(pos), fmt.Sprintf(format, args...)), + }) +} + func adderr(pos src.XPos, format string, args ...interface{}) { msg := fmt.Sprintf(format, args...) // Only add the position if know the position. @@ -95,6 +103,22 @@ func flusherrors() { errors = errors[:0] } +// flushwarnings sorts warnings seen so far by line number, prints them to stdout, +// and empties the warnings array. +func flushwarnings() { + Ctxt.Bso.Flush() + if len(warnings) == 0 { + return + } + sort.Stable(byPos(warnings)) + for i, err := range warnings { + if i == 0 || err.msg != warnings[i-1].msg { + fmt.Printf("%s", err.msg) + } + } + warnings = warnings[:0] +} + func hcrash() { if Debug.h != 0 { flusherrors() @@ -119,6 +143,15 @@ var lasterror struct { msg string // error message of last non-syntax error } +// lastwarning keeps track of the most recently issued warning. +// It is used to avoid multiple warning messages on the same +// line. +var lastwarning struct { + syntax src.XPos // source position of last syntax warning + other src.XPos // source position of last non-syntax warning + msg string // warning message of last non-syntax warning +} + // sameline reports whether two positions a, b are on the same line. func sameline(a, b src.XPos) bool { p := Ctxt.PosTable.Pos(a) @@ -126,6 +159,43 @@ func sameline(a, b src.XPos) bool { return p.Base() == q.Base() && p.Line() == q.Line() } +func yywarningl(pos src.XPos, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + + if strings.HasPrefix(msg, "syntax error") { + // only one syntax error per line, no matter what error + if sameline(lastwarning.syntax, pos) { + return + } + lastwarning.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(lastwarning.other, pos) && lastwarning.msg == msg { + return + } + lastwarning.other = pos + lastwarning.msg = msg + } + + addwarn(pos, "%s", msg) + + hcrash() + flushwarnings() +} + +func yywarningv(lang string, format string, args ...interface{}) { + what := fmt.Sprintf(format, args...) + yywarningl(lineno, "%s requires %s or later (-lang was set to %s; check go.mod)", what, lang, flag_lang) +} + +func yywarning(format string, args ...interface{}) { + yywarningl(lineno, format, args...) +} + func yyerrorl(pos src.XPos, format string, args ...interface{}) { msg := fmt.Sprintf(format, args...) @@ -302,7 +372,7 @@ func importdot(opkg *types.Pkg, pack *Node) { if n == 0 { // can't possibly be used - there were no symbols - yyerrorl(pack.Pos, "imported and not used: %q", opkg.Path) + yywarningl(pack.Pos, "imported and not used: %q", opkg.Path) } } diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go index 8d9fbe300e..ad699b8f78 100644 --- a/src/cmd/compile/internal/gc/swt.go +++ b/src/cmd/compile/internal/gc/swt.go @@ -32,7 +32,7 @@ func typecheckTypeSwitch(n *Node) { // declaration itself. So if there are no cases, we won't // notice that it went unused. if v := n.Left.Left; v != nil && !v.isBlank() && n.List.Len() == 0 { - yyerrorl(v.Pos, "%v declared but not used", v.Sym) + yywarningl(v.Pos, "%v declared but not used", v.Sym) } var defCase, nilCase *Node diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 98ebb23991..5e07824410 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -51,10 +51,10 @@ func walk(fn *Node) { if defn.Left.Name.Used() { continue } - yyerrorl(defn.Left.Pos, "%v declared but not used", ln.Sym) + yywarningl(defn.Left.Pos, "%v declared but not used", ln.Sym) defn.Left.Name.SetUsed(true) // suppress repeats } else { - yyerrorl(ln.Pos, "%v declared but not used", ln.Sym) + yywarningl(ln.Pos, "%v declared but not used", ln.Sym) } }