Files
wehub-resource-sync a06f331eb8
install-script / powershell-syntax (push) Waiting to run
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
install-script / install (macos-14) (push) Waiting to run
install-script / install (ubuntu-latest) (push) Waiting to run
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

143 lines
3.4 KiB
Go

package lint
import (
"bytes"
"encoding/json"
"path/filepath"
"regexp"
"strconv"
"strings"
)
// extLang maps file extensions to the linter languages this package supports.
var extLang = map[string]string{
".go": "go",
".py": "python",
".pyi": "python",
".sh": "shell",
".bash": "shell",
".zsh": "shell",
".ksh": "shell",
}
// LanguageForPath infers the linter language from a file's extension, or "" if
// no built-in linter targets that type.
func LanguageForPath(p string) string {
return extLang[strings.ToLower(filepath.Ext(p))]
}
// gccLineRe matches the `file:line[:col]: message` convention emitted by gofmt
// -e and many other compilers/linters.
var gccLineRe = regexp.MustCompile(`^(.*?):(\d+):(?:(\d+):)?\s*(.*)$`)
func parseGCCFormat(out []byte, linter string) []Diagnostic {
var diags []Diagnostic
for _, line := range strings.Split(string(out), "\n") {
line = strings.TrimRight(line, "\r")
if strings.TrimSpace(line) == "" {
continue
}
m := gccLineRe.FindStringSubmatch(line)
if m == nil {
continue
}
ln, _ := strconv.Atoi(m[2])
col := 0
if m[3] != "" {
col, _ = strconv.Atoi(m[3])
}
diags = append(diags, Diagnostic{
File: m[1],
Line: ln,
Column: col,
Severity: SeverityError,
Message: strings.TrimSpace(m[4]),
Linter: linter,
})
}
return diags
}
// shellcheckRe matches shellcheck's `-f gcc` output:
// `file:line:col: severity: message [SCxxxx]`.
var shellcheckRe = regexp.MustCompile(`^(.*?):(\d+):(\d+):\s*(\w+):\s*(.*?)(?:\s*\[(SC\d+)\])?$`)
func parseShellcheckGCC(out []byte, linter string) []Diagnostic {
var diags []Diagnostic
for _, line := range strings.Split(string(out), "\n") {
line = strings.TrimRight(line, "\r")
m := shellcheckRe.FindStringSubmatch(line)
if m == nil {
continue
}
ln, _ := strconv.Atoi(m[2])
col, _ := strconv.Atoi(m[3])
diags = append(diags, Diagnostic{
File: m[1],
Line: ln,
Column: col,
Severity: shellcheckSeverity(m[4]),
Message: strings.TrimSpace(m[5]),
Rule: m[6],
Linter: linter,
})
}
return diags
}
func shellcheckSeverity(s string) Severity {
switch strings.ToLower(s) {
case "error":
return SeverityError
case "warning":
return SeverityWarning
default: // note, style, info
return SeverityInfo
}
}
type ruffEntry struct {
Code string `json:"code"`
Message string `json:"message"`
Filename string `json:"filename"`
Location struct {
Row int `json:"row"`
Column int `json:"column"`
} `json:"location"`
}
func parseRuff(out []byte, linter string) []Diagnostic {
out = bytes.TrimSpace(out)
if len(out) == 0 {
return nil
}
var entries []ruffEntry
if err := json.Unmarshal(out, &entries); err != nil {
return nil
}
diags := make([]Diagnostic, 0, len(entries))
for _, e := range entries {
diags = append(diags, Diagnostic{
File: e.Filename,
Line: e.Location.Row,
Column: e.Location.Column,
Severity: ruffSeverity(e.Code, e.Message),
Message: e.Message,
Rule: e.Code,
Linter: linter,
})
}
return diags
}
// ruffSeverity treats syntax errors (E999, the E9 class, or a SyntaxError
// message — ruff emits these with a null code) as errors; everything else is a
// lint warning.
func ruffSeverity(code, message string) Severity {
if code == "" || strings.HasPrefix(code, "E9") ||
strings.Contains(strings.ToLower(message), "syntaxerror") {
return SeverityError
}
return SeverityWarning
}