Skip to content

Commit

Permalink
Fix GCC linker errors not showing up in Problems View (microsoft#3950)
Browse files Browse the repository at this point in the history
* gcc.ts: Replace single regex pattern with array

* CHANGELOG.md: Add GCC parsing fix

* Change parser from gnuLD to gcc for linker error diagnostics unit test

* Fix GNU LD diagnostics parser not being enabled due to inconsistency in config and in build.ts by_source strings

* Improve GCC and GNU LD diagnostics parsers

* Remove obsolete french linker error unit test

* Add gcc and gnuld diagnostics unit tests

* Clean up regexes, add unit tests

* Make gcc and gnuld parsers execution order agnostic

* Share the RegexPattern interface and the MatchType interface rather than doubly defining it

* Remove C++ template compiler error detection from gnu-ld, move common data types to util.ts

* fix extra line

* update changelog

* Remove obsolete return statement

* Add comments

---------

Co-authored-by: 0xemgy <0xemgy>
Co-authored-by: Garrett Campbell <[email protected]>
Co-authored-by: Garrett Campbell <[email protected]>
  • Loading branch information
3 people authored Nov 1, 2024
1 parent c3a5713 commit 220ac74
Show file tree
Hide file tree
Showing 6 changed files with 515 additions and 113 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Bug Fixes:

- Fix our setting of `isUserPreset` for presets, only set it to `true` if it's defined in a user presets file. [#4059](https://github.com/microsoft/vscode-cmake-tools/issues/4059)
- Fix issue where duplicate presets are being listed in dropdown. [#4104](https://github.com/microsoft/vscode-cmake-tools/issues/4104)
- Fix various GCC compiler errors and GCC linker errors not showing up in Problems View [#2864](https://github.com/microsoft/vscode-cmake-tools/issues/2864)

## 1.19.52

Expand Down
4 changes: 2 additions & 2 deletions src/diagnostics/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ export class Compilers {
[compiler: string]: RawDiagnosticParser;

gcc = new gcc.Parser();
gnuld = new gnu_ld.Parser();
ghs = new ghs.Parser();
diab = new diab.Parser();
gnuLD = new gnu_ld.Parser();
msvc = new mvsc.Parser();
iar = new iar.Parser();
}
Expand Down Expand Up @@ -82,7 +82,7 @@ export class CompileOutputConsumer implements OutputConsumer {
MSVC: this.compilers.msvc.diagnostics,
GHS: this.compilers.ghs.diagnostics,
DIAB: this.compilers.diab.diagnostics,
link: this.compilers.gnuLD.diagnostics,
GNULD: this.compilers.gnuld.diagnostics,
IAR: this.compilers.iar.diagnostics
};
const parsers = util.objectPairs(by_source)
Expand Down
135 changes: 96 additions & 39 deletions src/diagnostics/gcc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,23 @@

import * as vscode from 'vscode';

import { oneLess, RawDiagnostic, RawDiagnosticParser, RawRelated, FeedLineResult } from '@cmt/diagnostics/util';
import { oneLess, RawDiagnostic, RawDiagnosticParser, RawRelated, FeedLineResult, MatchType, RegexPattern } from '@cmt/diagnostics/util';

export const REGEX = /^(.*):(\d+):(\d+):\s+(?:fatal )?(\w*)(?:\sfatale)?\s?:\s+(.*)/;
// Patterns to identify and capture GCC diagnostic messages.
const regexPatterns: RegexPattern[] = [
{ // path/to/file:line:column: severity: message
regexPattern: /^(.+):(\d+):(\d+):\s+(?:fatal\s+)?(\w+):\s+(.+)/,
matchTypes: [MatchType.Full, MatchType.File, MatchType.Line, MatchType.Column, MatchType.Severity, MatchType.Message]
},
{ // path/to/file:line: severity: message (but not starting with "path/to/ld[.exe]:")
regexPattern: /^(?!.*?ld(?:\.exe)?:)(.+):(\d+):\s+(?:fatal\s+)?(\w+):\s+(.+)/,
matchTypes: [MatchType.Full, MatchType.File, MatchType.Line, MatchType.Severity, MatchType.Message]
},
{ // path/to/cc1[.exe]|arm-none-eabi-gcc[.exe]: severity: message
regexPattern: /^(.*(?:cc1|arm-none-eabi-gcc)(?:\.exe)?):\s+(?:fatal\s+)?(\w+):\s+(.+)/,
matchTypes: [MatchType.Full, MatchType.File, MatchType.Severity, MatchType.Message]
}
];

interface PendingTemplateBacktrace {
rootInstantiation: string;
Expand All @@ -19,6 +33,11 @@ export class Parser extends RawDiagnosticParser {
private _pendingTemplateError?: PendingTemplateBacktrace;

doHandleLine(line: string) {
// Detect the first line of a C++ template error
// This is a special case which consists of 3 lines:
// path/to/file: In instantiation of ‘...’:
// path/to/file:lineno:columnno: required from here
// path/to/file:lineno:columnno: severity: message
let mat = /(.*): (In instantiation of.+)/.exec(line);
if (mat) {
const [, , message] = mat;
Expand All @@ -28,8 +47,8 @@ export class Parser extends RawDiagnosticParser {
};
return FeedLineResult.Ok;
}

if (this._pendingTemplateError) {
// Detect the second line of a pending C++ template error
mat = /(.*):(\d+):(\d+):( +required from.+)/.exec(line);
if (mat) {
const [, file, linestr, column, message] = mat;
Expand All @@ -43,7 +62,7 @@ export class Parser extends RawDiagnosticParser {
}
}

// Early-catch backtrace limit notes
// Detect backtrace limit notes in GCC diagnostics and append them to the previous diagnostic if one exists
mat = /note: \((.*backtrace-limit.*)\)/.exec(line);
if (mat && this._prevDiag && this._prevDiag.related.length !== 0) {
const prevRelated = this._prevDiag.related[0];
Expand All @@ -55,47 +74,85 @@ export class Parser extends RawDiagnosticParser {
return FeedLineResult.Ok;
}

// Test if this is a diagnostic
mat = REGEX.exec(line);
if (!mat) {
// Nothing to see on this line of output...
// Attempt to parse a general diagnostic message using regex patterns defined in regexPatterns
let mat2 = null;

let full = "";
let file = "";
let lineno = oneLess("1");
let columnno = oneLess("1");
let severity = 'error';
let message = "";

for (const [, regexPattern] of regexPatterns.entries()) {
mat2 = line.match(regexPattern.regexPattern);

if (mat2 !== null) {
// For each matchType in the pattern, assign values accordingly
for (let i = 0; i < mat2.length; i++) {
switch (regexPattern.matchTypes[i]) {
case MatchType.Full:
full = mat2[i];
break;
case MatchType.File:
file = mat2[i];
break;
case MatchType.Line:
lineno = oneLess(mat2[i]);
break;
case MatchType.Column:
columnno = oneLess(mat2[i]);
break;
case MatchType.Severity:
severity = mat2[i];
break;
case MatchType.Message:
message = mat2[i];
break;
default:
break;
}
}
break;
}
}

if (!mat2) {
// Ignore this line because it is no matching diagnostic
return FeedLineResult.NotMine;
} else {
const [full, file, lineno_, column_, severity, message] = mat;
if (file && lineno_ && column_ && severity && message) {
const lineno = oneLess(lineno_);
const column = oneLess(column_);
if (severity === 'note' && this._prevDiag) {
this._prevDiag.related.push({
// If severity is "note", append the message to the previous diagnostic's related messages
if (severity === 'note' && this._prevDiag) {
this._prevDiag.related.push({
file,
location: new vscode.Range(lineno, columnno, lineno, 999),
message
});
return FeedLineResult.Ok;
} else {
const related: RawRelated[] = [];
const location = new vscode.Range(lineno, columnno, lineno, 999);
if (this._pendingTemplateError) {
// If the diagnostic is the third line of a pending C++ template error, finalize it here
related.push({
location,
file,
location: new vscode.Range(lineno, column, lineno, 999),
message
message: this._pendingTemplateError.rootInstantiation
});
return FeedLineResult.Ok;
} else {
const related: RawRelated[] = [];
const location = new vscode.Range(lineno, column, lineno, 999);
if (this._pendingTemplateError) {
related.push({
location,
file,
message: this._pendingTemplateError.rootInstantiation
});
related.push(...this._pendingTemplateError.requiredFrom);
this._pendingTemplateError = undefined;
}

return this._prevDiag = {
full,
file,
location,
severity,
message,
related
};
related.push(...this._pendingTemplateError.requiredFrom);
this._pendingTemplateError = undefined;
}

// Store and return the current diagnostic
return this._prevDiag = {
full,
file,
location,
severity,
message,
related
};
}
return FeedLineResult.NotMine;
}
}
}
113 changes: 92 additions & 21 deletions src/diagnostics/gnu-ld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,104 @@

import * as vscode from 'vscode';

import { FeedLineResult, oneLess, RawDiagnosticParser } from '@cmt/diagnostics/util';
import { oneLess, RawDiagnostic, RawDiagnosticParser, RawRelated, FeedLineResult, MatchType, RegexPattern } from '@cmt/diagnostics/util';

export const REGEX = /^(.*):(\d+)\s?:\s+(.*[^\]])$/;
// Patterns to identify and capture GNU linker diagnostic messages
const regexPatterns: RegexPattern[] = [
{ // path/to/ld[.exe]:[ ]path/to/file:line: severity: message
regexPattern: /^(?:.*ld(?:\.exe)?:)(?:\s*)?(.+):(\d+):\s+(?:fatal )?(\w+):\s+(.+)/,
matchTypes: [MatchType.Full, MatchType.File, MatchType.Line, MatchType.Severity, MatchType.Message]
},
{ // path/to/ld[.exe]:[ ]path/to/file:line: message
regexPattern: /^(?:.*ld(?:\.exe)?\:)(?:\s*)?(.+):(\d+):\s+(.+)/,
matchTypes: [MatchType.Full, MatchType.File, MatchType.Line, MatchType.Message]
},
{ // path/to/ld[.exe]: severity: message
regexPattern: /^(.*ld(?:\.exe)?):\s+(?:fatal )?(\w+):\s+(.+)/,
matchTypes: [MatchType.Full, MatchType.File, MatchType.Severity, MatchType.Message]
},
{ // path/to/ld[.exe]: message (without trailing colon)
regexPattern: /^(.*ld(?:\.exe)?):\s+(.+)(?<!:)$/,
matchTypes: [MatchType.Full, MatchType.File, MatchType.Message]
},
{ // /path/to/file:line: message (without "[fatal] severity:" or trailing colon)
regexPattern: /^(.+?):(\d+):\s+(?!fatal\s+\w+:)(?!\w+:)(.+)(?<!:)$/,
matchTypes: [MatchType.Full, MatchType.File, MatchType.Line, MatchType.Message]
}
];

export class Parser extends RawDiagnosticParser {
private _prevDiag?: RawDiagnostic;

doHandleLine(line: string) {
// Try to parse for GNU ld
if (line.startsWith('make')) {
// This is a Make error. It may *look* like an LD error, so we abort early
return FeedLineResult.NotMine;
// Test if this is a diagnostic
let mat = null;

let full = "";
let file = "";
let lineno = oneLess("1");
let columnno = oneLess("1");
let severity = 'error';
let message = "foobar";

for (const [, regexPattern] of regexPatterns.entries()) {
mat = line.match(regexPattern.regexPattern);

if (mat !== null) {
// For each matchType in the pattern, assign values accordingly
for (let i = 0; i < mat.length; i++) {
switch (regexPattern.matchTypes[i]) {
case MatchType.Full:
full = mat[i];
break;
case MatchType.File:
file = mat[i];
break;
case MatchType.Line:
lineno = oneLess(mat[i]);
break;
case MatchType.Column:
columnno = oneLess(mat[i]);
break;
case MatchType.Severity:
severity = mat[i];
break;
case MatchType.Message:
message = mat[i];
break;
default:
break;
}
}
break;
}
}
const res = REGEX.exec(line);
if (!res) {

if (!mat) {
// Ignore this line because it is no matching diagnostic
return FeedLineResult.NotMine;
} else {
// If severity is "note", append the message to the previous diagnostic's related messages
if (severity === 'note' && this._prevDiag) {
this._prevDiag.related.push({
file,
location: new vscode.Range(lineno, columnno, lineno, 999),
message
});
return FeedLineResult.Ok;
} else {
const related: RawRelated[] = [];

// Store and return the current diagnostic
return this._prevDiag = {
full,
file,
location: new vscode.Range(lineno, columnno, lineno, 999),
severity,
message,
related
};
}
}
const [full, file, lineno_, message] = res;
const lineno = oneLess(lineno_);
if (file && lineno && message) {
return {
full,
file,
location: new vscode.Range(lineno, 0, lineno, 999),
severity: 'error',
message,
related: []
};
}
return FeedLineResult.NotMine;
}
}
20 changes: 20 additions & 0 deletions src/diagnostics/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,23 @@ export abstract class RawDiagnosticParser {
*/
protected abstract doHandleLine(line: string): RawDiagnostic | FeedLineResult;
}

/**
* Match types for gcc related regex diagnostics
*/
export enum MatchType {
Full,
File,
Line,
Column,
Severity,
Message
}

/**
* Regex pattern interface for generic gcc related regex diagnostics
*/
export interface RegexPattern {
regexPattern: RegExp;
matchTypes: MatchType[];
}
Loading

0 comments on commit 220ac74

Please sign in to comment.