Skip to main content

add-diagnostic-parser

Use when adding a compiler or tool output parser for the Problems panel. Touches src/diagnostics/<name>.ts, src/diagnostics/build.ts, package.json (cmake.enabledOutputParsers), and package.nls.json. Triggers: "add parser", "new diagnostic parser", "parse compiler output".

소스 정보

저장소
microsoft/vscode-cmake-tools
최근 소스 활동
2026년 4월 20일 20:29
감지된 SKILL.md 언어
영어
스타
1,687
포크
548

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
add-diagnostic-parser
description
Use when adding a compiler or tool output parser for the Problems panel. Touches src/diagnostics/<name>.ts, src/diagnostics/build.ts, package.json (cmake.enabledOutputParsers), and package.nls.json. Triggers: "add parser", "new diagnostic parser", "parse compiler output".
# Adding a New Diagnostic / Output Parser Recipe for adding a new compiler or tool output parser to CMake Tools. A parser extracts diagnostics (errors, warnings, notes) from build output and surfaces them in the VS Code **Problems** panel. ## Overview Adding a parser touches **4 files** (plus tests and changelog): | File | What to do | |---|---| | `src/diagnostics/<name>.ts` | Create the parser class | | `src/diagnostics/build.ts` | Register the parser in `Compilers` | | `package.json` | Add to `cmake.enabledOutputParsers` enum | | `package.nls.json` | Update description if user-visible text changes | --- ## Step 1: Create the parser file Create `src/diagnostics/<name>.ts`. Every parser extends `RawDiagnosticParser` (defined in `src/diagnostics/util.ts`) and exports a class named `Parser`. ### Base class contract ```typescript // src/diagnostics/util.ts (simplified) export abstract class RawDiagnosticParser { get diagnostics(): readonly RawDiagnostic[] { /* accumulated results */ } /** Called by the build consumer for every line of output. */ handleLine(line: string): boolean { const result = this.doHandleLine(line); if (result === FeedLineResult.Ok) return true; // line consumed, no new diagnostic if (result === FeedLineResult.NotMine) return false; // line not recognized // Otherwise result is a RawDiagnostic — it gets stored automatically this._diagnostics.push(result); return true; } /** Implement this. Return a RawDiagnostic to emit, or a FeedLineResult. */ protected abstract doHandleLine(line: string): RawDiagnostic | FeedLineResult; } ``` ### Minimal single-regex parser (like MSVC) ```typescript /** * Module for parsing <ToolName> diagnostics */ /** */ import * as vscode from 'vscode'; import { oneLess, RawDiagnosticParser, FeedLineResult } from '@cmt/diagnostics/util'; // Regex that captures: file, line, severity, code (optional), message export const REGEX = /^(.+):(\d+):\s+(error|warning|info)\s+(\w+):\s+(.*)$/; export class Parser extends RawDiagnosticParser { doHandleLine(line: string) { const mat = REGEX.exec(line); if (!mat) { return FeedLineResult.NotMine; } const [full, file, lineStr, severity, code, message] = mat; const lineno = oneLess(lineStr); // convert 1-based to 0-based return { full, file, location: new vscode.Range(lineno, 0, lineno, 999), severity, message, code, related: [] }; } } ``` ### Multi-line / stateful parser (like IAR) For tools whose diagnostics span multiple lines, use internal state: ```typescript import { RawDiagnosticParser, RawDiagnostic, FeedLineResult, oneLess } from '@cmt/diagnostics/util'; enum ParserState { init, pending_message } export class Parser extends RawDiagnosticParser { private state = ParserState.init; private pending: RawDiagnostic | null = null; doHandleLine(line: string): RawDiagnostic | FeedLineResult { switch (this.state) { case ParserState.init: { const mat = FIRST_LINE_REGEX.exec(line); if (!mat) return FeedLineResult.NotMine; this.pending = { /* ... build partial diagnostic ... */ }; this.state = ParserState.pending_message; return FeedLineResult.Ok; // consumed, but not complete yet } case ParserState.pending_message: { if (/* line completes the diagnostic */) { const diag = this.pending!; this.reset(); return diag; // emits the diagnostic } return FeedLineResult.Ok; // still accumulating } } return FeedLineResult.NotMine; } } ``` ### Key types ```typescript // src/diagnostics/util.ts interface RawDiagnostic { full: string; // the full matched line(s) file: string; // source file path location: vscode.Range; // 0-based range (use oneLess() for 1-based input) severity: string; // 'error' | 'warning' | 'note' | 'info' | 'fatal error' | 'remark' message: string; code?: string; // optional diagnostic code (e.g. 'C4996', 'LNK2019') related: RawRelated[]; // related diagnostics (notes, template backtraces) } enum FeedLineResult { Ok, // line was consumed (but no new diagnostic produced yet) NotMine, // line not recognized by this parser } ``` `diagnosticSeverity()` in `util.ts` maps the severity string to `vscode.DiagnosticSeverity`. Recognized values: `'error'`, `'fatal error'`, `'catastrophic error'`, `'warning'`, `'note'`, `'info'`, `'remark'`. --- ## Step 2: Register in `src/diagnostics/build.ts` Import the new parser module and add an instance to the `Compilers` class. The **property name** becomes the parser identifier used in `cmake.enabledOutputParsers`. ```typescript // At the top — add import import * as myparser from '@cmt/diagnostics/myparser'; // Inside the Compilers class — add property export class Compilers { [compiler: string]: RawDiagnosticParser; gcc = new gcc.Parser(); gnuld = new gnu_ld.Parser(); ghs = new ghs.Parser(); diab = new diab.Parser(); msvc = new mvsc.Parser(); iar = new iar.Parser(); iwyu = new iwyu.Parser(); myparser = new myparser.Parser(); // ← new parser } ``` The `CompileOutputConsumer.error()` method iterates over all `Compilers` properties in declaration order, calling `parser.handleLine(line)`. The **first** parser to return `true` claims the line — order matters if your parser's regex could match lines from another compiler. The `resolveDiagnostics()` method filters diagnostics by `config.enableOutputParsers` — only parsers whose name appears in that array will have their diagnostics shown in the Problems panel. --- ## Step 3: Update `package.json` Add the parser name to the `cmake.enabledOutputParsers` enum. Decide whether it should be **default-enabled** or **opt-in**. ```jsonc // package.json → contributes.configuration → cmake.enabledOutputParsers "cmake.enabledOutputParsers": { "type": "array", "items": { "type": "string", "enum": [ "cmake", "gcc", "gnuld", "msvc", "ghs", "diab", "iar", "iwyu", "myparser" // ← add to enum ] }, "default": [ "cmake", "gcc", "gnuld", "msvc", "ghs", "diab" // only add here if default-enabled ] } ``` --- ## Step 4: Update `package.nls.json` If any user-visible setting descriptions changed, update the localization key in `package.nls.json`. The `cmake.enabledOutputParsers` description uses the key `cmake-tools.configuration.cmake.enabledOutputParsers.description`. --- ## Step 5: Write tests Add tests in `test/unit-tests/diagnostics.test.ts`. The test pattern: ```typescript test('Parse <tool> error', () => { const build_consumer = new diags.CompileOutputConsumer( new ConfigurationReader({} as ExtensionConfigurationSettings) ); // Feed real compiler output lines build_consumer.error('<tool-specific output line>'); // Verify diagnostics const parser = build_consumer.compilers.myparser; expect(parser.diagnostics).to.have.length(1); expect(parser.diagnostics[0].severity).to.eq('error'); expect(parser.diagnostics[0].file).to.eq('expected/file.cpp'); }); ``` --- ## Step 6: `CHANGELOG.md` Add an entry under the current version: ```markdown Features: - Add `myparser` diagnostic parser for <ToolName> compiler output. [PR #XXXX](https://github.com/microsoft/vscode-cmake-tools/pull/XXXX) ``` --- ## Currently registered parsers | Property in `Compilers` | Module file | Default-enabled | |---|---|---| | `gcc` | `src/diagnostics/gcc.ts` | ✅ | | `gnuld` | `src/diagnostics/gnu-ld.ts` | ✅ | | `ghs` | `src/diagnostics/ghs.ts` | ✅ | | `diab` | `src/diagnostics/diab.ts` | ✅ | | `msvc` | `src/diagnostics/msvc.ts` | ✅ | | `iar` | `src/diagnostics/iar.ts` | ❌ opt-in | | `iwyu` | `src/diagnostics/iwyu.ts` | ❌ opt-in | The `cmake` parser (in `src/diagnostics/cmake.ts`) is separate — it handles CMake's own configure/generate output via `CMakeOutputConsumer`, not build compiler output. It is listed in the `enabledOutputParsers` default array but is **not** part of the `Compilers` class. --- ## Default-enabled vs opt-in - **Default-enabled** parsers (`cmake`, `gcc`, `gnuld`, `msvc`, `ghs`, `diab`) are included in the `"default"` array in `package.json`. Users get them automatically. - **Opt-in** parsers (`iar`, `iwyu`) are in the `"enum"` but **not** in `"default"`. Users must add them to their `cmake.enabledOutputParsers` setting. - Use opt-in for parsers with aggressive regexes that might false-positive on common output, or for niche toolchains. --- ## Verification checklist - [ ] Parser class extends `RawDiagnosticParser` and exports as `Parser` - [ ] `doHandleLine()` returns `FeedLineResult.NotMine` for unrecognized lines - [ ] `doHandleLine()` returns a `RawDiagnostic` (not `FeedLineResult.Ok`) when a diagnostic is complete - [ ] Line/column numbers converted from 1-based to 0-based with `oneLess()` - [ ] Registered in `Compilers` class in `src/diagnostics/build.ts` - [ ] Added to `cmake.enabledOutputParsers` enum in `package.json` - [ ] Default array updated (or deliberately left opt-in) - [ ] Unit tests in `test/unit-tests/diagnostics.test.ts` pass - [ ] `yarn compile` succeeds - [ ] `CHANGELOG.md` entry added --- See also: [`.github/copilot-instructions.md`](../copilot-instructions.md) for project-wide conventions.
GitHub에서 보기