| 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
export abstract class RawDiagnosticParser {
get diagnostics(): readonly RawDiagnostic[] { }
handleLine(line: string): boolean {
const result = this.doHandleLine(line);
if (result === FeedLineResult.Ok) return true;
if (result === FeedLineResult.NotMine) return false;
this._diagnostics.push(result);
return true;
}
protected abstract doHandleLine(line: string): RawDiagnostic | FeedLineResult;
}
Minimal single-regex parser (like MSVC)
import * as vscode from 'vscode';
import { oneLess, RawDiagnosticParser, FeedLineResult } from '@cmt/diagnostics/util';
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);
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:
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 = { };
this.state = ParserState.pending_message;
return FeedLineResult.Ok;
}
case ParserState.pending_message: {
if () {
const diag = this.pending!;
this.reset();
return diag;
}
return FeedLineResult.Ok;
}
}
return FeedLineResult.NotMine;
}
}
Key types
interface RawDiagnostic {
full: string;
file: string;
location: vscode.Range;
severity: string;
message: string;
code?: string;
related: RawRelated[];
}
enum FeedLineResult {
Ok,
NotMine,
}
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.
import * as myparser from '@cmt/diagnostics/myparser';
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();
}
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.
"cmake.enabledOutputParsers": {
"type": "array",
"items": {
"type": "string",
"enum": [
"cmake",
"gcc",
"gnuld",
"msvc",
"ghs",
"diab",
"iar",
"iwyu",
"myparser"
]
},
"default": [
"cmake",
"gcc",
"gnuld",
"msvc",
"ghs",
"diab"
]
}
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:
test('Parse <tool> error', () => {
const build_consumer = new diags.CompileOutputConsumer(
new ConfigurationReader({} as ExtensionConfigurationSettings)
);
build_consumer.error('<tool-specific output line>');
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:
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
See also: .github/copilot-instructions.md for project-wide conventions.