mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 18:13:50 +02:00
When showing an error, show source code.
This commit is contained in:
+123
-8
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
#include "V3String.h"
|
||||
#ifndef _V3ERROR_NO_GLOBAL_
|
||||
# include "V3Ast.h"
|
||||
# include "V3Global.h"
|
||||
@@ -31,6 +32,7 @@
|
||||
# include "V3File.h"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include VL_INCLUDE_UNORDERED_SET
|
||||
|
||||
@@ -80,13 +82,73 @@ void FileLineSingleton::fileNameNumMapDumpXml(std::ostream& os) {
|
||||
os<<"</files>\n";
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// VFileContents class functions
|
||||
|
||||
int VFileContent::debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
|
||||
void VFileContent::pushText(const string& text) {
|
||||
if (m_lines.size() == 0) {
|
||||
m_lines.push_back(""); // no such thing as line [0]
|
||||
m_lines.push_back(""); // start with no leftover
|
||||
}
|
||||
|
||||
// Any leftover text is stored on largest line (might be "")
|
||||
string leftover = m_lines.back() + text; m_lines.pop_back();
|
||||
|
||||
// Insert line-by-line
|
||||
string::size_type pos = 0;
|
||||
string::size_type line_start = 0;
|
||||
while (1) {
|
||||
string::size_type line_end = leftover.find("\n", line_start);
|
||||
if (line_end != string::npos) {
|
||||
string oneline (leftover, line_start, line_end-line_start+1);
|
||||
m_lines.push_back(oneline); // Keeps newline
|
||||
UINFO(9, "PushStream[ct"<<m_id<<"+"<<(m_lines.size()-1)<<"]: "<<oneline);
|
||||
line_start = line_end+1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Keep leftover for next time
|
||||
m_lines.push_back(string(leftover, line_start)); // Might be ""
|
||||
}
|
||||
|
||||
string VFileContent::getLine(int lineno) const {
|
||||
// Return error text rather than asserting so the user isn't left without a message
|
||||
if (VL_UNCOVERABLE(lineno < 0 || lineno >= (int)m_lines.size())) {
|
||||
if (debug() || v3Global.opt.debugCheck()) {
|
||||
return ("%Error-internal-contents-bad-ct"+cvtToStr(m_id)
|
||||
+"-ln"+cvtToStr(lineno));
|
||||
} else return "";
|
||||
}
|
||||
string text = m_lines[lineno];
|
||||
UINFO(9, "Get Stream[ct"<<m_id<<"+"<<lineno<<"]: "<<text);
|
||||
return text;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, VFileContent* contentp) {
|
||||
if (!contentp) os <<"ct0";
|
||||
else os <<contentp->ascii();
|
||||
return os;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// FileLine class functions
|
||||
|
||||
FileLine::FileLine(FileLine::EmptySecret) {
|
||||
// Sort of a singleton
|
||||
m_lineno = 0;
|
||||
m_firstLineno = 0;
|
||||
m_lastLineno = 0;
|
||||
m_firstColumn = 0;
|
||||
m_lastColumn = 0;
|
||||
m_filenameno = singleton().nameToNumber(FileLine::builtInFilename());
|
||||
m_contentp = NULL;
|
||||
m_contentLineno = 0;
|
||||
m_parent = NULL;
|
||||
|
||||
m_warnOn = 0;
|
||||
@@ -97,13 +159,16 @@ FileLine::FileLine(FileLine::EmptySecret) {
|
||||
}
|
||||
|
||||
string FileLine::lineDirectiveStrg(int enterExit) const {
|
||||
char numbuf[20]; sprintf(numbuf, "%d", lineno());
|
||||
char numbuf[20]; sprintf(numbuf, "%d", lastLineno());
|
||||
char levelbuf[20]; sprintf(levelbuf, "%d", enterExit);
|
||||
return (string("`line ")+numbuf+" \""+filename()+"\" "+levelbuf+"\n");
|
||||
}
|
||||
|
||||
void FileLine::lineDirective(const char* textp, int& enterExitRef) {
|
||||
// Handle `line directive
|
||||
// Does not parse streamNumber/streamLineno as the next input token
|
||||
// will come from the same stream as the previous line.
|
||||
|
||||
// Skip `line
|
||||
while (*textp && isspace(*textp)) textp++;
|
||||
while (*textp && !isspace(*textp)) textp++;
|
||||
@@ -134,6 +199,18 @@ void FileLine::lineDirective(const char* textp, int& enterExitRef) {
|
||||
//printf ("PPLINE %d '%s'\n", s_lineno, s_filename.c_str());
|
||||
}
|
||||
|
||||
void FileLine::forwardToken(const char* textp, size_t size, bool trackLines) {
|
||||
for (const char* sp = textp; size && *sp; ++sp, --size) {
|
||||
if (*sp == '\n') {
|
||||
if (trackLines) linenoInc();
|
||||
m_lastColumn = 1;
|
||||
} else if (*sp == '\r') {
|
||||
} else { // Tabs are considered one column; hence column means number of chars
|
||||
++m_lastColumn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileLine* FileLine::copyOrSameFileLine() {
|
||||
// When a fileline is "used" to produce a node, calls this function.
|
||||
// Return this, or a copy of this
|
||||
@@ -177,12 +254,19 @@ const string FileLine::profileFuncname() const {
|
||||
!= string::npos) {
|
||||
name.replace(pos, 1, "_");
|
||||
}
|
||||
name += "__l"+cvtToStr(lineno());
|
||||
name += "__l"+cvtToStr(lastLineno());
|
||||
return name;
|
||||
}
|
||||
|
||||
string FileLine::asciiLineCol() const {
|
||||
return (cvtToStr(firstLineno())+"-"+cvtToStr(lastLineno())
|
||||
+":"+cvtToStr(firstColumn())+"-"+cvtToStr(lastColumn())
|
||||
+"["+(m_contentp ? m_contentp->ascii() : "ct0")
|
||||
+"+"+cvtToStr(m_contentLineno)+"]");
|
||||
}
|
||||
string FileLine::ascii() const {
|
||||
return filename()+":"+cvtToStr(lineno());
|
||||
// For most errors especially in the parser the lastLineno is more accurate than firstLineno
|
||||
return filename()+":"+cvtToStr(lastLineno());
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, FileLine* fileline) {
|
||||
os <<fileline->ascii()<<": "<<std::hex;
|
||||
@@ -238,7 +322,7 @@ void FileLine::modifyStateInherit(const FileLine* fromp) {
|
||||
|
||||
void FileLine::v3errorEnd(std::ostringstream& str) {
|
||||
std::ostringstream nsstr;
|
||||
if (m_lineno) nsstr<<this;
|
||||
if (lastLineno()) nsstr<<this;
|
||||
nsstr<<str.str();
|
||||
nsstr<<endl;
|
||||
if (warnIsOff(V3Error::errorCode())) V3Error::suppressThisWarning();
|
||||
@@ -247,24 +331,55 @@ void FileLine::v3errorEnd(std::ostringstream& str) {
|
||||
}
|
||||
|
||||
string FileLine::warnMore() const {
|
||||
if (m_lineno) {
|
||||
if (lastLineno()) {
|
||||
return V3Error::warnMore()+string(ascii().size(), ' ')+": ";
|
||||
} else {
|
||||
return V3Error::warnMore();
|
||||
}
|
||||
}
|
||||
string FileLine::warnOther() const {
|
||||
if (m_lineno) {
|
||||
if (lastLineno()) {
|
||||
return V3Error::warnMore()+ascii()+": ";
|
||||
} else {
|
||||
return V3Error::warnMore();
|
||||
}
|
||||
}
|
||||
|
||||
string FileLine::source() const {
|
||||
if (VL_UNCOVERABLE(!m_contentp)) {
|
||||
if (debug() || v3Global.opt.debugCheck()) return "%Error-internal-no-contents";
|
||||
else return "";
|
||||
}
|
||||
return m_contentp->getLine(m_contentLineno);
|
||||
}
|
||||
string FileLine::prettySource() const {
|
||||
string out = source();
|
||||
// Drop ignore trailing newline
|
||||
string::size_type pos = out.find('\n');
|
||||
if (pos != string::npos) out = string(out, 0, pos);
|
||||
// Column tracking counts tabs = 1, so match that when print source
|
||||
return VString::spaceUnprintable(out);
|
||||
}
|
||||
|
||||
string FileLine::warnContext(bool secondary) const {
|
||||
V3Error::errorContexted(true);
|
||||
string out = "";
|
||||
// TODO: Eventually print the original source code here
|
||||
if (firstLineno()==lastLineno() && firstColumn()) {
|
||||
string sourceLine = prettySource();
|
||||
// Don't show super-long lines as can fill screen and unlikely to help user
|
||||
if (!sourceLine.empty()
|
||||
&& sourceLine.length() < SHOW_SOURCE_MAX_LENGTH
|
||||
&& sourceLine.length() >= (lastColumn()-1)) {
|
||||
out += sourceLine+"\n";
|
||||
out += string((firstColumn()-1), ' ')+'^';
|
||||
// Can't use UASSERT_OBJ used in warnings already inside the error end handler
|
||||
UASSERT_STATIC(lastColumn() >= firstColumn(), "Column numbers backwards");
|
||||
if (lastColumn() != firstColumn()) {
|
||||
out += string((lastColumn()-firstColumn()-1), '~');
|
||||
}
|
||||
out += "\n";
|
||||
}
|
||||
}
|
||||
if (!secondary) { // Avoid printing long paths on informational part of error
|
||||
for (FileLine* parentFl = parent(); parentFl; parentFl = parentFl->parent()) {
|
||||
if (parentFl->filenameIsGlobal()) break;
|
||||
|
||||
Reference in New Issue
Block a user