blob: d7c0430117f74ad18260ab22051488260e9eeb89 [file] [log] [blame]
// Copyright 2024 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 checker
// This file defines helpers for printing analysis results.
// They should all be pure functions.
import (
"bytes"
"fmt"
"go/token"
"io"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/internal/analysisflags"
)
// PrintText emits diagnostics as plain text to w.
//
// If contextLines is nonnegative, it also prints the
// offending line, plus that many lines of context
// before and after the line.
func (g *Graph) PrintText(w io.Writer, contextLines int) error {
return writeTextDiagnostics(w, g.Roots, contextLines)
}
func writeTextDiagnostics(w io.Writer, roots []*Action, contextLines int) error {
// De-duplicate diagnostics by position (not token.Pos) to
// avoid double-reporting in source files that belong to
// multiple packages, such as foo and foo.test.
// (We cannot assume that such repeated files were parsed
// only once and use syntax nodes as the key.)
type key struct {
pos token.Position
end token.Position
*analysis.Analyzer
message string
}
seen := make(map[key]bool)
// TODO(adonovan): opt: plumb errors back from PrintPlain and avoid buffer.
buf := new(bytes.Buffer)
forEach(roots, func(act *Action) error {
if act.Err != nil {
fmt.Fprintf(w, "%s: %v\n", act.Analyzer.Name, act.Err)
} else if act.IsRoot {
for _, diag := range act.Diagnostics {
// We don't display Analyzer.Name/diag.Category
// as most users don't care.
posn := act.Package.Fset.Position(diag.Pos)
end := act.Package.Fset.Position(diag.End)
k := key{posn, end, act.Analyzer, diag.Message}
if seen[k] {
continue // duplicate
}
seen[k] = true
analysisflags.PrintPlain(buf, act.Package.Fset, contextLines, diag)
}
}
return nil
})
_, err := w.Write(buf.Bytes())
return err
}
// PrintJSON emits diagnostics in JSON form to w.
// Diagnostics are shown only for the root nodes,
// but errors (if any) are shown for all dependencies.
func (g *Graph) PrintJSON(w io.Writer) error {
return writeJSONDiagnostics(w, g.Roots)
}
func writeJSONDiagnostics(w io.Writer, roots []*Action) error {
tree := make(analysisflags.JSONTree)
forEach(roots, func(act *Action) error {
var diags []analysis.Diagnostic
if act.IsRoot {
diags = act.Diagnostics
}
tree.Add(act.Package.Fset, act.Package.ID, act.Analyzer.Name, diags, act.Err)
return nil
})
return tree.Print(w)
}