diff --git a/src/parsers/pluscal.ts b/src/parsers/pluscal.ts index f3923d5..0fb4b6a 100644 --- a/src/parsers/pluscal.ts +++ b/src/parsers/pluscal.ts @@ -112,6 +112,7 @@ export class TranspilerStdoutParser extends ProcessOutputHandler { * * So we can start looking for labels as soon as we see (1) * and stop as soon as we stop seeing label strings. + * */ private tryParseAddedLabels(line: string) { // https://github.com/tlaplus/tlaplus/blob/21f92/tlatools/org.lamport.tlatools/src/pcal/ParseAlgorithm.java#L668 @@ -124,7 +125,7 @@ export class TranspilerStdoutParser extends ProcessOutputHandler { return false; } - const matcher = /^\s\s([A-Za-z0-9_]+) at line \d+, column \d+$/g.exec(line); + const matcher = /^\s\s([A-Za-z0-9_]+) at line \d+, column \d+/g.exec(line); // no closing `$` to handle macro statements if (!matcher) { // done parsing this.nowParsingAddedLabels = false; return false; @@ -132,13 +133,17 @@ export class TranspilerStdoutParser extends ProcessOutputHandler { const labelname = matcher[1]; const message = `Missing label, translator inserted \`${labelname}\` here`; - const locInfo = this.parseLocation(line) || ZERO_LOCATION_INFO; + const locInfo = this.parseLabelLocation(line) || ZERO_LOCATION_INFO; const locRange = new vscode.Range(locInfo.location, locInfo.location); this.result.addMessage(this.filePath, locRange, message, vscode.DiagnosticSeverity.Information); return true; } + /** + * Extracts location info from PlusCal translation strings. + * Note: not robust for labels. Use parseLabelLocation instead. + */ private parseLocation(line: string): LocationInfo | undefined { const rxLocation = /\s*(?:at )?line (\d+), column (\d+).?\s*$/g; const matches = rxLocation.exec(line); @@ -149,4 +154,30 @@ export class TranspilerStdoutParser extends ProcessOutputHandler { const posCol = parseInt(matches[2]); return new LocationInfo(new vscode.Position(posLine, posCol), matches[0].length); } + + /** + * Extracts label location info from label added strings. + * Similar to `parseLocation`, EXCEPT it's also robust against insertions due to macros: + * + * Lbl_2 at line 1, column 2 of macro called at line 23, column 5 + * + * In this case, we want to capture "line 23, column 5" NOT "line 1". + */ + + private parseLabelLocation(line: string): LocationInfo | undefined { + const rxLocation = /\s*(?:at )?line (\d+), column (\d+)(?: of macro called at line (\d+), column (\d+))?.?\s*$/g; + const matches = rxLocation.exec(line); + if (!matches) { + return undefined; + } + + // get macro match if it exists, otherwise get label match + const matchLine = matches[3] || matches[1]; + const matchCol = matches[4] || matches[2]; + + const posLine = parseInt(matchLine) - 1; + const posCol = parseInt(matchCol); + + return new LocationInfo(new vscode.Position(posLine, posCol), matches[0].length); + } } diff --git a/tests/suite/parsers/pluscal.test.ts b/tests/suite/parsers/pluscal.test.ts index 7e4ff2f..53ee923 100644 --- a/tests/suite/parsers/pluscal.test.ts +++ b/tests/suite/parsers/pluscal.test.ts @@ -142,6 +142,33 @@ suite('PlusCal Transpiler Output Parser Test Suite', () => { ]); }); + test('Captures inserted macro labels', () => { + const stdout = [ + 'pcal.trans Version 1.11 of 31 December 2020', + 'The following labels were added:', + ' Lbl_1 at line 16, column 5', + ' Lbl_2 at line 1, column 5 of macro called at line 23, column 5', + ' Lbl_3 at line 46, column 5', + 'Parsing completed.', + 'Translation completed.', + 'New file saturation2.tla written.' + ].join('\n'); + assertOutput(stdout, '/Users/bob/TLA/needs_labels.tla', [ + new vscode.Diagnostic( + new vscode.Range(15, 5, 15, 5), + 'Missing label, translator inserted `Lbl_1` here', + vscode.DiagnosticSeverity.Information), + new vscode.Diagnostic( + new vscode.Range(22, 5, 22, 5), + 'Missing label, translator inserted `Lbl_2` here', + vscode.DiagnosticSeverity.Information), + new vscode.Diagnostic( + new vscode.Range(45, 5, 45, 5), + 'Missing label, translator inserted `Lbl_3` here', + vscode.DiagnosticSeverity.Information) + ]); + }); + test('Captures multiple errors after blank message', () => { const stdout = [ 'pcal.trans Version 1.11 of 31 December 2020',