format.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package lintcmd
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "go/token"
  6. "io"
  7. "os"
  8. "path/filepath"
  9. "text/tabwriter"
  10. "honnef.co/go/tools/analysis/lint"
  11. )
  12. func shortPath(path string) string {
  13. cwd, err := os.Getwd()
  14. if err != nil {
  15. return path
  16. }
  17. if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) {
  18. return rel
  19. }
  20. return path
  21. }
  22. func relativePositionString(pos token.Position) string {
  23. s := shortPath(pos.Filename)
  24. if pos.IsValid() {
  25. if s != "" {
  26. s += ":"
  27. }
  28. s += fmt.Sprintf("%d:%d", pos.Line, pos.Column)
  29. }
  30. if s == "" {
  31. s = "-"
  32. }
  33. return s
  34. }
  35. type statter interface {
  36. Stats(total, errors, warnings, ignored int)
  37. }
  38. type formatter interface {
  39. Format(checks []*lint.Analyzer, diagnostics []diagnostic)
  40. }
  41. type textFormatter struct {
  42. W io.Writer
  43. }
  44. func (o textFormatter) Format(_ []*lint.Analyzer, ps []diagnostic) {
  45. for _, p := range ps {
  46. fmt.Fprintf(o.W, "%s: %s\n", relativePositionString(p.Position), p.String())
  47. for _, r := range p.Related {
  48. fmt.Fprintf(o.W, "\t%s: %s\n", relativePositionString(r.Position), r.Message)
  49. }
  50. }
  51. }
  52. type nullFormatter struct{}
  53. func (nullFormatter) Format([]*lint.Analyzer, []diagnostic) {}
  54. type jsonFormatter struct {
  55. W io.Writer
  56. }
  57. func (o jsonFormatter) Format(_ []*lint.Analyzer, ps []diagnostic) {
  58. type location struct {
  59. File string `json:"file"`
  60. Line int `json:"line"`
  61. Column int `json:"column"`
  62. }
  63. type related struct {
  64. Location location `json:"location"`
  65. End location `json:"end"`
  66. Message string `json:"message"`
  67. }
  68. enc := json.NewEncoder(o.W)
  69. for _, p := range ps {
  70. jp := struct {
  71. Code string `json:"code"`
  72. Severity string `json:"severity,omitempty"`
  73. Location location `json:"location"`
  74. End location `json:"end"`
  75. Message string `json:"message"`
  76. Related []related `json:"related,omitempty"`
  77. }{
  78. Code: p.Category,
  79. Severity: p.Severity.String(),
  80. Location: location{
  81. File: p.Position.Filename,
  82. Line: p.Position.Line,
  83. Column: p.Position.Column,
  84. },
  85. End: location{
  86. File: p.End.Filename,
  87. Line: p.End.Line,
  88. Column: p.End.Column,
  89. },
  90. Message: p.Message,
  91. }
  92. for _, r := range p.Related {
  93. jp.Related = append(jp.Related, related{
  94. Location: location{
  95. File: r.Position.Filename,
  96. Line: r.Position.Line,
  97. Column: r.Position.Column,
  98. },
  99. End: location{
  100. File: r.End.Filename,
  101. Line: r.End.Line,
  102. Column: r.End.Column,
  103. },
  104. Message: r.Message,
  105. })
  106. }
  107. _ = enc.Encode(jp)
  108. }
  109. }
  110. type stylishFormatter struct {
  111. W io.Writer
  112. prevFile string
  113. tw *tabwriter.Writer
  114. }
  115. func (o *stylishFormatter) Format(_ []*lint.Analyzer, ps []diagnostic) {
  116. for _, p := range ps {
  117. pos := p.Position
  118. if pos.Filename == "" {
  119. pos.Filename = "-"
  120. }
  121. if pos.Filename != o.prevFile {
  122. if o.prevFile != "" {
  123. o.tw.Flush()
  124. fmt.Fprintln(o.W)
  125. }
  126. fmt.Fprintln(o.W, pos.Filename)
  127. o.prevFile = pos.Filename
  128. o.tw = tabwriter.NewWriter(o.W, 0, 4, 2, ' ', 0)
  129. }
  130. fmt.Fprintf(o.tw, " (%d, %d)\t%s\t%s\n", pos.Line, pos.Column, p.Category, p.Message)
  131. for _, r := range p.Related {
  132. fmt.Fprintf(o.tw, " (%d, %d)\t\t %s\n", r.Position.Line, r.Position.Column, r.Message)
  133. }
  134. }
  135. }
  136. func (o *stylishFormatter) Stats(total, errors, warnings, ignored int) {
  137. if o.tw != nil {
  138. o.tw.Flush()
  139. fmt.Fprintln(o.W)
  140. }
  141. fmt.Fprintf(o.W, " ✖ %d problems (%d errors, %d warnings, %d ignored)\n",
  142. total, errors, warnings, ignored)
  143. }