UNPKG

pi-lens

Version:

Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo

74 lines (73 loc) • 3.52 kB
/** * Go vet runner for dispatch system * * Runs `go vet` for Go files to catch common mistakes. */ import { relative, resolve, sep, posix } from "node:path"; import { GoClient } from "../../go-client.js"; import { safeSpawnAsync } from "../../safe-spawn.js"; import { stripAnsi } from "../../sanitize.js"; import { parseGoVetOutput } from "./utils/diagnostic-parsers.js"; import { PRIORITY } from "../priorities.js"; const goClient = new GoClient(); const goVetRunner = { id: "go-vet", appliesTo: ["go"], priority: PRIORITY.SPECIALIZED_ANALYSIS, enabledByDefault: true, timeoutMs: 40_000, async run(ctx) { // Resolve go path using platform-aware lookup (handles system install paths on Windows) const goExe = await goClient.findGoPathAsync(); if (!goExe) { return { status: "skipped", diagnostics: [], semantic: "none" }; } // Vet the package containing the file from the module root. // // `go vet <one-file.go>` compiles that file in isolation, so same-package // symbols defined in sibling files read as false `undefined: X`, and when // the spawn cwd is outside the module the run also reports // `go.mod file not found` (#263). ctx.cwd is the go.mod module root // (resolveLanguageRootForFile, markers ["go.mod"]), so vet the file's // package from there. const cwd = ctx.cwd || process.cwd(); const fileRel = relative(cwd, ctx.filePath).split(sep).join(posix.sep); const pkgPath = fileRel.startsWith("../") ? // File isn't under ctx.cwd (unexpected — ctx.cwd is the go.mod root). // Vet the root package as a safe fallback rather than emit `./../x`; // the filename filter below still prevents mis-attribution. "." : fileRel.includes("/") ? "./" + fileRel.slice(0, fileRel.lastIndexOf("/")) : "."; const result = await safeSpawnAsync(goExe, ["vet", pkgPath], { timeout: 30000, cwd, }); const raw = stripAnsi(result.stdout + result.stderr); if (result.status === 0 && !raw.trim()) { return { status: "succeeded", diagnostics: [], semantic: "none" }; } // createLineParser ignores the output filename and attributes every // diagnostic to ctx.filePath, so keep only this file's lines — package // vetting reports siblings too. Resolve each output path against the vet // cwd before comparing, so go's path FORM (a leading `./` for the // module-root package, an absolute path, `.` segments) can't cause the // edited file's OWN diagnostics to be silently dropped. const absTarget = resolve(ctx.filePath); const relevant = raw .split("\n") .filter((line) => { const m = line.match(/^(.+?):(\d+):(\d+):\s*(.+)/); return m != null && resolve(cwd, m[1].trim()) === absTarget; }) .join("\n"); const diagnostics = parseGoVetOutput(relevant, ctx.filePath); // Edited file clean → succeeded: a sibling-file error no longer flags // the edited file's turn (it surfaces when that file is itself edited). return diagnostics.length > 0 ? { status: "failed", diagnostics, semantic: "warning" } : { status: "succeeded", diagnostics: [], semantic: "none" }; }, }; export default goVetRunner;