When showing an error, show source code.

This commit is contained in:
Wilson Snyder
2019-07-14 21:42:03 -04:00
parent a4820fc700
commit 97561bf064
175 changed files with 1266 additions and 262 deletions
+123 -8
View File
@@ -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;