@imc-trading/svlangserver
Version:
A language server for systemverilog
232 lines (231 loc) • 9.34 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.VerilogDiagnostics = void 0;
const node_1 = require("vscode-languageserver/node");
const genutils_1 = require("./genutils");
const path = require('path');
function getVerilatorSeverity(severityString) {
let result = node_1.DiagnosticSeverity.Information;
if (severityString.startsWith('Error')) {
result = node_1.DiagnosticSeverity.Error;
}
else if (severityString.startsWith('Warning')) {
result = node_1.DiagnosticSeverity.Warning;
}
return result;
}
function parseVerilatorDiagnostics(stdout, stderr, file, whitelistedMessages = []) {
let diagnostics = [];
let lines = stderr.split(/\r?\n/g);
// RegExp expression for matching Verilator messages
// Group 1: Severity
// Group 2: Type (optional)
// Group 3: Filename
// Group 4: Line number
// Group 5: Column number (optional)
// Group 6: Message
let regex = new RegExp(String.raw `%(Error|Warning)(-[A-Z0-9_]+)?: (${file.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')}):(\d+):(?:(\d+):)? (.*)`, 'i');
// Parse output lines
for (let i = 0; i < lines.length; ++i) {
const line = lines[i];
let terms = line.match(regex);
if (terms != null) {
let severity = getVerilatorSeverity(terms[1]);
let message = "";
let lineNum = parseInt(terms[4]) - 1;
let colNum = 0;
let colNumEnd = 0;
if (terms[5]) {
colNum = parseInt(terms[5]) - 1;
}
message = terms[6];
let messageWhiteListed = false;
for (let whitelistedMessage of whitelistedMessages) {
if (message.search(whitelistedMessage) >= 0) {
messageWhiteListed = true;
break;
}
}
if (messageWhiteListed) {
continue;
}
// Match the ^~~~~~~ under the error message
if (/\s*\^~+/.exec(lines[i + 2])) {
let startColNum = lines[i + 2].indexOf('|') + 2;
colNum = lines[i + 2].indexOf('^');
colNum = colNum > startColNum ? colNum - startColNum : colNum;
colNumEnd = lines[i + 2].lastIndexOf('~');
colNumEnd = colNumEnd > startColNum ? colNumEnd - startColNum : colNumEnd;
i += 2;
}
if ((lineNum != NaN) && (colNum != NaN)) {
diagnostics.push({
severity: severity,
range: node_1.Range.create(lineNum, colNum, lineNum, colNumEnd < colNum ? colNum : colNumEnd),
message: message,
code: 'verilator',
source: 'verilator'
});
}
}
}
return diagnostics;
}
function getIcarusSeverity(severityString, message) {
let result = node_1.DiagnosticSeverity.Information;
if (severityString === 'error' || message === 'syntax error') {
result = node_1.DiagnosticSeverity.Error;
}
else if (severityString === 'warning') {
result = node_1.DiagnosticSeverity.Warning;
}
return result;
}
function parseIcarusDiagnostics(stdout, stderr, file, whitelistedMessages = []) {
let diagnostics = [];
let lines = stderr.split(/\r?\n/g);
// RegExp expression for matching Icarus messages
// Group 1: Filename
// Group 2: Line number
// Group 3: Severity (optional)
// Group 4: Message
let regex = new RegExp(String.raw `(${file.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')}):(\d+):(?: (error|warning):)? (.*)`, 'i');
// Parse output lines
for (let i = 0; i < lines.length; ++i) {
const line = lines[i];
let terms = line.match(regex);
if (terms != null) {
let message = terms[4];
let severity = getIcarusSeverity(terms[3], message);
let lineNum = parseInt(terms[2]) - 1;
let messageWhiteListed = false;
for (let whitelistedMessage of whitelistedMessages) {
if (message.search(whitelistedMessage) >= 0) {
messageWhiteListed = true;
break;
}
}
if (messageWhiteListed) {
continue;
}
if (lineNum != NaN) {
diagnostics.push({
severity: severity,
range: node_1.Range.create(lineNum, 0, lineNum, 0),
message: message,
code: 'iverilog',
source: 'iverilog'
});
}
}
}
return diagnostics;
}
class VerilogDiagnostics {
constructor(indexer) {
this._linter = 'icarus';
this._command = "";
this._defines = [];
this._optionsFile = "";
this._childProcMngr = new genutils_1.ChildProcManager();
this._delayedCaller = new genutils_1.DelayedCaller();
this._whitelistedMessages = [];
this._indexer = indexer;
}
setCommand(cmd) {
this._command = cmd;
}
setLinter(linter) {
this._linter = linter;
}
setOptionsFile(file) {
this._optionsFile = file;
}
setWhitelistedMessages(msgs) {
this._whitelistedMessages = msgs.map(m => new RegExp(m));
}
setDefines(defines) {
this._defines = defines || [];
}
_lintImmediate(file, text) {
this._childProcMngr.kill(file);
return new Promise((resolve, reject) => {
let fileWithoutRoot = file.slice(path.parse(file).root.length);
let actFile = text == undefined ? file : genutils_1.tmpFileManager.getTmpFilePath("sources", fileWithoutRoot);
let optionsFile = this._optionsFile;
let vcTmpFileNum;
if (text != undefined) {
genutils_1.fsWriteFileSync(actFile, text);
// if file write takes too long and another process started in the interim
this._childProcMngr.kill(file);
if (this._indexer.fileHasPkg(file)) {
let vcFileContent = this._indexer.getOptionsFileContent()
.map(line => { return (line.endsWith(file)) ? line.slice(0, line.length - file.length) + actFile : line; })
.join('\n');
vcTmpFileNum = genutils_1.tmpFileManager.getFreeTmpFileNum("vcfiles");
let tmpVcFile = genutils_1.tmpFileManager.getTmpFilePath("vcfiles", `lint${vcTmpFileNum}.vc`);
genutils_1.fsWriteFileSync(tmpVcFile, vcFileContent);
optionsFile = tmpVcFile;
// if file write takes too long and another process started in the interim
this._childProcMngr.kill(file);
}
}
let definesArg = this._defines.length > 0 ? this._defines.map(d => ` +define+${d}`).join('') : "";
let optionsFileArg = optionsFile ? ' -f ' + optionsFile : "";
let actFileArg = this._indexer.isMustSrcFile(file) ? "" : " " + actFile;
let command = this._command + definesArg + optionsFileArg + actFileArg;
//ConnectionLogger.log(`DEBUG: verilator command ${command}`);
this._childProcMngr.run(file, command, (status, error, stdout, stderr) => {
if (optionsFile != this._optionsFile) {
genutils_1.tmpFileManager.returnFreeTmpFileNum("vcfiles", vcTmpFileNum);
}
if (status) {
switch (this._linter) {
case "icarus":
resolve(parseIcarusDiagnostics(stdout, stderr, actFile, this._whitelistedMessages));
break;
case "verilator":
resolve(parseVerilatorDiagnostics(stdout, stderr, actFile, this._whitelistedMessages));
break;
default:
reject(new Error(`Unknown linter ${this._linter}`));
break;
}
}
else {
resolve([]);
}
});
});
}
lint(file, text) {
try {
if (text == undefined) {
return this._lintImmediate(file, text)
.catch(error => {
genutils_1.ConnectionLogger.error(error);
return [];
});
}
else {
return this._delayedCaller.run(file, (success) => {
if (!!success) {
return this._lintImmediate(file, text);
}
return [];
}, (error) => {
genutils_1.ConnectionLogger.error(error);
return [];
});
}
}
catch (error) {
genutils_1.ConnectionLogger.error(error);
return Promise.resolve([]);
}
}
cleanupTmpFiles() {
genutils_1.tmpFileManager.cleanupTmpFiles();
}
}
exports.VerilogDiagnostics = VerilogDiagnostics;