// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Error handling // // Code available from: https://verilator.org // //************************************************************************* // // Copyright 2003-2020 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. // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // //************************************************************************* #ifndef _V3FILELINE_H_ #define _V3FILELINE_H_ 1 #include "config_build.h" #include "verilatedos.h" #include "V3Error.h" #include "V3LangCode.h" #include #include #include #include #include //###################################################################### class FileLine; //! Singleton class with tables of per-file data. //! This singleton class contains tables of data that are unchanging in each //! source file (each with its own unique filename number). class FileLineSingleton { // TYPES typedef std::map FileNameNumMap; // MEMBERS FileNameNumMap m_namemap; // filenameno for each filename std::deque m_names; // filename text for each filenameno std::deque m_languages; // language for each filenameno // CONSTRUCTORS FileLineSingleton() = default; ~FileLineSingleton() = default; protected: friend class FileLine; int nameToNumber(const string& filename); string numberToName(int filenameno) const { return m_names[filenameno]; } V3LangCode numberToLang(int filenameno) const { return m_languages[filenameno]; } void numberToLang(int filenameno, const V3LangCode& l) { m_languages[filenameno] = l; } void clear() { m_namemap.clear(); m_names.clear(); m_languages.clear(); } void fileNameNumMapDumpXml(std::ostream& os); static string filenameLetters(int fileno); }; //! All source lines from a file/stream, to enable errors to show sources class VFileContent { // MEMBERS int m_id; // Content ID number std::deque m_lines; // Source text lines public: VFileContent() { static int s_id = 0; m_id = ++s_id; } ~VFileContent() = default; // METHODS void pushText(const string& text); // Add arbitrary text (need not be line-by-line) string getLine(int lineno) const; string ascii() const { return "ct" + cvtToStr(m_id); } static int debug(); }; std::ostream& operator<<(std::ostream& os, VFileContent* contentp); //! File and line number of an object, mostly for error reporting //! This class is instantiated for every source code line (potentially //! millions). To save space, per-file information (e.g. filename, source //! language is held in tables in the FileLineSingleton class. class FileLine { // CONSTANTS static constexpr unsigned SHOW_SOURCE_MAX_LENGTH = 400; // Don't show source lines > this long // MEMBERS // Columns here means number of chars from beginning (i.e. tabs count as one) int m_firstLineno = 0; // `line corrected token's first line number int m_firstColumn = 0; // `line corrected token's first column number int m_lastLineno = 0; // `line corrected token's last line number int m_lastColumn = 0; // `line corrected token's last column number int m_filenameno; // `line corrected filename number int m_contentLineno = 0; // Line number within source stream VFileContent* m_contentp = nullptr; // Source text contents line is within FileLine* m_parent = nullptr; // Parent line that included this line std::bitset m_warnOn; bool m_waive = false; // Waive warning protected: // User routines should never need to change line numbers // We are storing pointers, so we CAN'T change them after initial reading. friend class FileLineSingleton; friend class V3ParseImp; friend class V3PreLex; friend class V3PreProcImp; friend class V3PreShellImp; private: // CONSTRUCTORS static FileLineSingleton& singleton() { static FileLineSingleton s; return s; } static FileLine& defaultFileLine() { static FileLine* defFilelinep = new FileLine(FileLine::EmptySecret()); return *defFilelinep; } public: explicit FileLine(const string& filename) : m_filenameno{singleton().nameToNumber(filename)} , m_warnOn{defaultFileLine().m_warnOn} {} explicit FileLine(FileLine* fromp) : m_firstLineno{fromp->m_firstLineno} , m_firstColumn{fromp->m_firstColumn} , m_lastLineno{fromp->m_lastLineno} , m_lastColumn{fromp->m_lastColumn} , m_filenameno{fromp->m_filenameno} , m_contentLineno{fromp->m_contentLineno} , m_contentp{fromp->m_contentp} , m_parent{fromp->m_parent} , m_warnOn{fromp->m_warnOn} , m_waive{fromp->m_waive} {} struct EmptySecret {}; // Constructor selection explicit FileLine(EmptySecret); FileLine* copyOrSameFileLine(); static void deleteAllRemaining(); ~FileLine() = default; #ifdef VL_LEAK_CHECKS static void* operator new(size_t size); static void operator delete(void* obj, size_t size); #endif // METHODS void newContent(); void lineno(int num) { m_firstLineno = num; m_lastLineno = num; m_firstColumn = m_lastColumn = 1; } void language(V3LangCode lang) { singleton().numberToLang(filenameno(), lang); } void filename(const string& name) { m_filenameno = singleton().nameToNumber(name); } void parent(FileLine* fileline) { m_parent = fileline; } void lineDirective(const char* textp, int& enterExitRef); void linenoInc() { m_lastLineno++; m_lastColumn = 1; m_contentLineno++; } void startToken() { m_firstLineno = m_lastLineno; m_firstColumn = m_lastColumn; } // Advance last line/column based on given text void forwardToken(const char* textp, size_t size, bool trackLines = true); int firstLineno() const { return m_firstLineno; } int firstColumn() const { return m_firstColumn; } int lastLineno() const { return m_lastLineno; } int lastColumn() const { return m_lastColumn; } VFileContent* contentp() const { return m_contentp; } // If not otherwise more specific, use last lineno for errors etc, // as the parser errors etc generally make more sense pointing at the last parse point int lineno() const { return m_lastLineno; } string source() const; string prettySource() const; // Source, w/stripped unprintables and newlines FileLine* parent() const { return m_parent; } V3LangCode language() const { return singleton().numberToLang(filenameno()); } string ascii() const; string asciiLineCol() const; int filenameno() const { return m_filenameno; } string filename() const { return singleton().numberToName(filenameno()); } bool filenameIsGlobal() const { return (filename() == commandLineFilename() || filename() == builtInFilename()); } string filenameLetters() const { return FileLineSingleton::filenameLetters(filenameno()); } string filebasename() const; string filebasenameNoExt() const; string firstColumnLetters() const; string profileFuncname() const; string xml() const { return "fl=\"" + filenameLetters() + cvtToStr(lastLineno()) + "\""; } string xmlDetailedLocation() const; string lineDirectiveStrg(int enterExit) const; // Turn on/off warning messages on this line. void warnOn(V3ErrorCode code, bool flag) { m_warnOn.set(code, flag); } void warnOff(V3ErrorCode code, bool flag) { warnOn(code, !flag); } bool warnOff(const string& msg, bool flag); // Returns 1 if ok bool warnIsOff(V3ErrorCode code) const; void warnLintOff(bool flag); void warnStyleOff(bool flag); void warnStateFrom(const FileLine& from) { m_warnOn = from.m_warnOn; } void warnResetDefault() { warnStateFrom(defaultFileLine()); } bool lastWarnWaived() { return m_waive; } // Specific flag ACCESSORS/METHODS bool celldefineOn() const { return m_warnOn.test(V3ErrorCode::I_CELLDEFINE); } void celldefineOn(bool flag) { warnOn(V3ErrorCode::I_CELLDEFINE, flag); } bool coverageOn() const { return m_warnOn.test(V3ErrorCode::I_COVERAGE); } void coverageOn(bool flag) { warnOn(V3ErrorCode::I_COVERAGE, flag); } bool tracingOn() const { return m_warnOn.test(V3ErrorCode::I_TRACING); } void tracingOn(bool flag) { warnOn(V3ErrorCode::I_TRACING, flag); } // METHODS - Global // and match what GCC outputs static string commandLineFilename() { return ""; } static string builtInFilename() { return ""; } static void globalWarnLintOff(bool flag) { defaultFileLine().warnLintOff(flag); } static void globalWarnStyleOff(bool flag) { defaultFileLine().warnStyleOff(flag); } static void globalWarnOff(V3ErrorCode code, bool flag) { defaultFileLine().warnOff(code, flag); } static bool globalWarnOff(const string& code, bool flag) { return defaultFileLine().warnOff(code, flag); } static void fileNameNumMapDumpXml(std::ostream& os) { singleton().fileNameNumMapDumpXml(os); } // METHODS - Called from netlist // Merge warning disables from another fileline void modifyStateInherit(const FileLine* fromp); // Change the current fileline due to actions discovered after parsing // and may have side effects on other nodes sharing this FileLine. // Use only when this is intended void modifyWarnOff(V3ErrorCode code, bool flag) { warnOff(code, flag); } // OPERATORS void v3errorEnd(std::ostringstream& str, const string& locationStr = ""); void v3errorEndFatal(std::ostringstream& str); /// When building an error, prefix for printing continuation lines /// e.g. information referring to the same FileLine as before string warnMore() const; /// When building an error, prefix for printing secondary information /// from a different FileLine than the original error string warnOther() const; /// When building an error, current location in include etc /// If not used in a given error, automatically pasted at end of error string warnContextPrimary() const { return warnContext(false); } /// When building an error, additional location for additional references /// Simplified information vs warnContextPrimary() to make dump clearer string warnContextSecondary() const { return warnContext(true); } bool operator==(const FileLine& rhs) const { return (m_firstLineno == rhs.m_firstLineno && m_firstColumn == rhs.m_firstColumn && m_lastLineno == rhs.m_lastLineno && m_lastColumn == rhs.m_lastColumn && m_filenameno == rhs.m_filenameno && m_warnOn == rhs.m_warnOn); } // Returns -1 if (*this) should come before rhs after sorted. 1 for the opposite case. 0 for // equivalent. int operatorCompare(const FileLine& rhs) const { if (m_filenameno != rhs.m_filenameno) return (m_filenameno < rhs.m_filenameno) ? -1 : 1; if (m_firstLineno != rhs.m_firstLineno) return (m_firstLineno < rhs.m_firstLineno) ? -1 : 1; if (m_firstColumn != rhs.m_firstColumn) return (m_firstColumn < rhs.m_firstColumn) ? -1 : 1; if (m_lastLineno != rhs.m_lastLineno) return (m_lastLineno < rhs.m_lastLineno) ? -1 : 1; if (m_lastColumn != rhs.m_lastColumn) return (m_lastColumn < rhs.m_lastColumn) ? -1 : 1; for (size_t i = 0; i < m_warnOn.size(); ++i) { if (m_warnOn[i] != rhs.m_warnOn[i]) return (m_warnOn[i] < rhs.m_warnOn[i]) ? -1 : 1; } return 0; // (*this) and rhs are equivalent } private: string warnContext(bool secondary) const; }; std::ostream& operator<<(std::ostream& os, FileLine* fileline); inline void FileLine::v3errorEndFatal(std::ostringstream& str) { v3errorEnd(str); assert(0); // LCOV_EXCL_LINE VL_UNREACHABLE } #endif // Guard