// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Error handling // // Code available from: https://verilator.org // //************************************************************************* // // Copyright 2003-2019 by Wilson Snyder. This program is free software; you can // redistribute it and/or modify it under the terms of either the GNU // Lesser General Public License Version 3 or the Perl Artistic License // Version 2.0. // // Verilator is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include "V3Error.h" #include "V3FileLine.h" #include "V3String.h" #ifndef _V3ERROR_NO_GLOBAL_ # include "V3Ast.h" # include "V3Global.h" # include "V3Stats.h" # include "V3Config.h" # include "V3File.h" #endif #include #include #include #include VL_INCLUDE_UNORDERED_SET //###################################################################### // FileLineSingleton class functions const string FileLineSingleton::filenameLetters(int fileno) { const int size = 1 + (64 / 4); // Each letter retires more than 4 bits of a > 64 bit number char out[size]; char* op = out+size-1; *--op = '\0'; // We build backwards int num = fileno; do { *--op = 'a'+num%26; num /= 26; } while (num); return op; } //! Convert filenames to a filenameno //! This lets us assign a nice small identifier for debug messages, but more //! importantly lets us use a 4 byte int instead of 8 byte pointer in every //! FileLine. //! We associate a language with each source file, so we also set the default //! for this. int FileLineSingleton::nameToNumber(const string& filename) { FileNameNumMap::const_iterator it = m_namemap.find(filename); if (VL_LIKELY(it != m_namemap.end())) return it->second; int num = m_names.size(); m_names.push_back(filename); m_languages.push_back(V3LangCode::mostRecent()); m_namemap.insert(make_pair(filename, num)); return num; } //! Support XML output //! Experimental. Updated to also put out the language. void FileLineSingleton::fileNameNumMapDumpXml(std::ostream& os) { os<<"\n"; for (FileNameNumMap::const_iterator it = m_namemap.begin(); it != m_namemap.end(); ++it) { os<<"second) <<"\" filename=\""<first, V3OutFormatter::LA_XML) <<"\" language=\""<second).ascii()<<"\"/>\n"; } os<<"\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 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"<= (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"<ascii(); return os; } //###################################################################### // FileLine class functions FileLine::FileLine(FileLine::EmptySecret) { // Sort of a singleton 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; for (int codei=V3ErrorCode::EC_MIN; codeiascii() : "ct0") +"+"+cvtToStr(m_contentLineno)+"]"); } string FileLine::ascii() const { // 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 <ascii()<<": "<warnIsOff(code)) { warnOff(code, true); } } } void FileLine::v3errorEnd(std::ostringstream& str, const string& locationStr) { std::ostringstream nsstr; if (lastLineno()) nsstr<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 = ""; 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() >= (size_t)(lastColumn()-1)) { out += sourceLine+"\n"; out += string((firstColumn()-1), ' ')+'^'; // Can't use UASSERT_OBJ used in warnings already inside the error end handler if (lastColumn() > firstColumn()) { // Note lastColumn() can be <= firstColumn() in some weird preproc expansions 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; out += parentFl->warnOther()+"... note: In file included from " +parentFl->filebasename()+"\n"; } } return out; } #ifdef VL_LEAK_CHECKS typedef vl_unordered_set FileLineCheckSet; FileLineCheckSet fileLineLeakChecks; void* FileLine::operator new(size_t size) { FileLine* objp = static_cast(::operator new(size)); fileLineLeakChecks.insert(objp); return objp; } void FileLine::operator delete(void* objp, size_t size) { if (!objp) return; FileLine* flp = static_cast(objp); FileLineCheckSet::iterator it = fileLineLeakChecks.find(flp); if (it != fileLineLeakChecks.end()) { fileLineLeakChecks.erase(it); } else { flp->v3fatalSrc("Deleting FileLine object that was never tracked"); } ::operator delete(objp); } #endif void FileLine::deleteAllRemaining() { #ifdef VL_LEAK_CHECKS // FileLines are allocated, but never nicely freed, as it's much faster // that way. Unfortunately this makes our leak checking a big mess, so // only when leak checking we'll track them all and cleanup. while (1) { FileLineCheckSet::iterator it = fileLineLeakChecks.begin(); if (it==fileLineLeakChecks.end()) break; delete *it; // Operator delete will remove the iterated object from the list. // Eventually the list will be empty and terminate the loop. } fileLineLeakChecks.clear(); singleton().clear(); #endif }