| /* |
| Copyright 2019 Alain Dargelas |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| /* |
| * File: ErrorContainer.cpp |
| * Author: alain |
| * |
| * Created on March 5, 2017, 11:12 PM |
| */ |
| |
| #include "ErrorContainer.h" |
| #include <mutex> |
| #include <iostream> |
| #include <fstream> |
| #include "../CommandLine/CommandLineParser.hpp" |
| #include "Waiver.h" |
| |
| #include "antlr4-runtime.h" |
| using namespace antlr4; |
| |
| #include "../API/PythonAPI.h" |
| |
| using namespace SURELOG; |
| |
| ErrorContainer::ErrorContainer (SymbolTable* symbolTable) : m_clp(NULL), |
| m_reportedFatalErrorLogFile(false), m_symbolTable(symbolTable), m_interpState(NULL) { |
| m_interpState = PythonAPI::getMainInterp (); |
| /* Do nothing here */ |
| } |
| |
| void ErrorContainer::init () |
| { |
| if (ErrorDefinition::init()) |
| { |
| const std::string& logFileName = m_clp->getSymbolTable()->getSymbol(m_clp->getLogFileId()); |
| std::ofstream ofs; |
| ofs.open(logFileName, std::fstream::out); |
| if (!ofs.good()) |
| { |
| std::cerr << "[FATAL:LG0001] Cannot create log file \"" << logFileName << "\"" << std::endl; |
| return; |
| } |
| ofs.close(); |
| } |
| } |
| |
| ErrorContainer::ErrorContainer (const ErrorContainer& orig) { } |
| |
| ErrorContainer::~ErrorContainer () { } |
| |
| |
| Error& ErrorContainer::addError(Error& error, bool showDuplicates, bool reentrantPython) { |
| std::tuple < std::string, bool, bool> textStatus = createErrorMessage(error, reentrantPython); |
| if (std::get<2>(textStatus)) // filter Message |
| return error; |
| |
| std::multimap<ErrorDefinition::ErrorType, Waiver::WaiverData>& waivers = Waiver::getWaivers (); |
| std::pair <std::multimap<ErrorDefinition::ErrorType, Waiver::WaiverData>::iterator, |
| std::multimap<ErrorDefinition::ErrorType, Waiver::WaiverData>::iterator> ret = waivers.equal_range(error.m_errorId); |
| for (std::multimap<ErrorDefinition::ErrorType, Waiver::WaiverData>::iterator it=ret.first; it!=ret.second; ++it) |
| { |
| if ((((*it).second.m_fileName == "") || (m_symbolTable->getSymbol(error.m_locations[0].m_fileId) == (*it).second.m_fileName)) && |
| (((*it).second.m_line == 0) || (error.m_locations[0].m_line == (*it).second.m_line)) && |
| (((*it).second.m_objectId == "") || (m_symbolTable->getSymbol(error.m_locations[0].m_object) == (*it).second.m_objectId))) { |
| error.m_waived = true; |
| break; |
| } |
| } |
| |
| if (showDuplicates) |
| { |
| m_errors.push_back (error); |
| } |
| else |
| { |
| if (m_errorSet.find (std::get<0>(textStatus)) == m_errorSet.end ()) |
| { |
| m_errors.push_back (error); |
| m_errorSet.insert (std::get<0>(textStatus)); |
| } |
| } |
| return m_errors[m_errors.size() -1]; |
| } |
| |
| |
| void ErrorContainer::appendErrors(ErrorContainer& rhs) { |
| for (unsigned int i = 0; i < rhs.m_errors.size(); i++) |
| { |
| Error err = rhs.m_errors[i]; |
| // Translate IDs to master symbol table |
| for (unsigned int locItr = 0; locItr < err.m_locations.size(); locItr++) |
| { |
| Location& loc = err.m_locations[locItr]; |
| if (loc.m_fileId) |
| loc.m_fileId = m_symbolTable->registerSymbol(rhs.m_symbolTable->getSymbol(loc.m_fileId)); |
| if (loc.m_object) { |
| loc.m_object = m_symbolTable->registerSymbol(rhs.m_symbolTable->getSymbol(loc.m_object)); |
| } |
| } |
| if (!err.m_reported) |
| addError(err); |
| } |
| } |
| |
| std::tuple<std::string, bool, bool> |
| ErrorContainer::createErrorMessage(Error& msg, bool reentrantPython) |
| { |
| const std::map<ErrorDefinition::ErrorType, ErrorDefinition::ErrorInfo>& infoMap = ErrorDefinition::getErrorInfoMap(); |
| std::string tmp; |
| bool reportFatalError = false; |
| bool filterMessage = false; |
| if ((!msg.m_reported) && (!msg.m_waived)) |
| { |
| ErrorDefinition::ErrorType type = msg.m_errorId; |
| std::map<ErrorDefinition::ErrorType, ErrorDefinition::ErrorInfo>::const_iterator itr = infoMap.find (type); |
| if (itr != infoMap.end ()) |
| { |
| ErrorDefinition::ErrorInfo info = (*itr).second; |
| std::string severity; |
| switch (info.m_severity) |
| { |
| case ErrorDefinition::FATAL: |
| severity = "FATAL"; |
| reportFatalError = true; |
| break; |
| case ErrorDefinition::ERROR: |
| severity = "ERROR"; |
| break; |
| case ErrorDefinition::WARNING: |
| severity = "WARNI"; |
| if (m_clp->filterWarning()) |
| filterMessage = true; |
| break; |
| case ErrorDefinition::INFO: |
| severity = "INFO "; |
| if (m_clp->filterInfo() && (type != ErrorDefinition::PP_PROCESSING_SOURCE_FILE)) |
| filterMessage = true; |
| break; |
| case ErrorDefinition::NOTE: |
| severity = "NOTE "; |
| if (m_clp->filterNote()) |
| filterMessage = true; |
| break; |
| } |
| std::string category = ErrorDefinition::getCategoryName(info.m_category); |
| |
| Location& loc = msg.m_locations[0]; |
| /* Object */ |
| std::string text = info.m_errorText; |
| const std::string& objectName = m_symbolTable->getSymbol (loc.m_object); |
| if (objectName != m_symbolTable->getBadSymbol ()) |
| { |
| size_t objectOffset = text.find ("%s"); |
| if (objectOffset != std::string::npos) |
| { |
| text = text.replace (objectOffset, 2, objectName); |
| } |
| } |
| |
| /* Location */ |
| std::string location; |
| if (loc.m_fileId == 0) |
| { |
| } |
| else |
| { |
| const std::string& fileName = m_symbolTable->getSymbol (loc.m_fileId); |
| location = fileName; |
| if (loc.m_line > 0) |
| { |
| location += ":" + std::to_string (loc.m_line); |
| // Emacs does not like column |
| //if (loc.m_column > 0) |
| // location += ":" + std::to_string (loc.m_column); |
| } |
| location += " "; |
| } |
| |
| /* Extra locations */ |
| unsigned int nbExtraLoc = msg.m_locations.size (); |
| for (unsigned int i = 1; i < nbExtraLoc; i++) |
| { |
| Location& extraLoc = msg.m_locations[i]; |
| if (extraLoc.m_fileId) |
| { |
| std::string extraLocation; |
| const std::string& fileName = m_symbolTable->getSymbol (extraLoc.m_fileId); |
| extraLocation = fileName; |
| if (extraLoc.m_line > 0) |
| { |
| extraLocation += ":" + std::to_string (extraLoc.m_line); |
| // Emacs does not like column |
| //if (extraLoc.m_column > 0) |
| // extraLocation += ":" + std::to_string (extraLoc.m_column); |
| } |
| size_t objectOffset = text.find ("%exloc"); |
| if (objectOffset != std::string::npos) |
| { |
| text = text.replace (objectOffset, 6, extraLocation); |
| } |
| else if (info.m_extraText != "") |
| { |
| text += ",\n " + info.m_extraText; |
| //text += ",\n" + info.m_extraText; |
| size_t objectOffset = text.find ("%exloc"); |
| if (objectOffset != std::string::npos) |
| { |
| text = text.replace (objectOffset, 6, extraLocation); |
| } |
| } |
| } |
| else |
| { |
| if (info.m_extraText != "") |
| { |
| if ((nbExtraLoc == 2) && (extraLoc.m_fileId == 0)) |
| text += ",\n" + info.m_extraText; |
| else |
| text += ",\n " + info.m_extraText; |
| } |
| } |
| if (extraLoc.m_object) |
| { |
| const std::string& objString = m_symbolTable->getSymbol (extraLoc.m_object); |
| size_t objectOffset = text.find ("%exobj"); |
| if (objectOffset != std::string::npos) |
| { |
| text = text.replace (objectOffset, 6, objString); |
| } |
| } |
| } |
| text += "."; |
| std::string padding; |
| if (msg.m_errorId < 10) padding = "000"; |
| else if (msg.m_errorId < 100) padding = "00"; |
| else if (msg.m_errorId < 1000) padding = "0"; |
| if ((reentrantPython == false) || (!m_clp->pythonAllowed ())) |
| { |
| tmp = "[" + severity + ":" + category + padding + std::to_string (msg.m_errorId) + "] " + location + text + "\n\n"; |
| } |
| else |
| { |
| std::vector<std::string> args; |
| args.push_back (severity); |
| args.push_back (category); |
| args.push_back (padding + std::to_string (msg.m_errorId)); |
| args.push_back (location); |
| args.push_back (text); |
| tmp = PythonAPI::evalScript ("__main__", "SLformatMsg", args, m_interpState); |
| } |
| } |
| } |
| return std::make_tuple(tmp, reportFatalError, filterMessage); |
| } |
| |
| bool ErrorContainer::hasFatalErrors() { |
| const std::map<ErrorDefinition::ErrorType, ErrorDefinition::ErrorInfo>& infoMap = ErrorDefinition::getErrorInfoMap(); |
| bool reportFatalError = false; |
| for (unsigned int i = 0; i < m_errors.size(); i++) |
| { |
| Error& msg = m_errors[i]; |
| ErrorDefinition::ErrorType type = msg.m_errorId; |
| std::map<ErrorDefinition::ErrorType, ErrorDefinition::ErrorInfo>::const_iterator itr = infoMap.find (type); |
| if (itr != infoMap.end ()) |
| { |
| ErrorDefinition::ErrorInfo info = (*itr).second; |
| std::string severity; |
| switch (info.m_severity) |
| { |
| case ErrorDefinition::FATAL: |
| reportFatalError = true; |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| return reportFatalError; |
| } |
| |
| |
| std::pair<std::string, bool> ErrorContainer::createReport_() { |
| std::string report; |
| bool reportFatalError = false; |
| for (unsigned int i = 0; i < m_errors.size(); i++) |
| { |
| Error& msg = m_errors[i]; |
| std::tuple<std::string, bool, bool> textStatus = createErrorMessage(msg); |
| if (std::get<1>(textStatus)) |
| reportFatalError = true; |
| if (std::get<2>(textStatus)) // Filtered |
| continue; |
| report += std::get<0>(textStatus); |
| msg.m_reported = true; |
| } |
| return std::make_pair(report, reportFatalError); |
| } |
| |
| |
| std::pair<std::string, bool> ErrorContainer::createReport_(Error& error) |
| { |
| std::string report; |
| bool reportFatalError = false; |
| Error& msg = error; |
| std::tuple <std::string, bool, bool> textStatus = createErrorMessage(msg); |
| if (std::get<1>(textStatus)) |
| reportFatalError = true; |
| if (!std::get<2>(textStatus)) //Filtered |
| report += std::get<0>(textStatus); |
| msg.m_reported = true; |
| return std::make_pair (report, reportFatalError); |
| } |
| |
| |
| bool ErrorContainer::printStats(ErrorContainer::Stats stats, bool muteStdout) { |
| std::string report; |
| report += "[ FATAL] : " + std::to_string(stats.nbFatal) + "\n"; |
| report += "[ ERROR] : " + std::to_string(stats.nbError) + "\n"; |
| report += "[WARNING] : " + std::to_string(stats.nbWarning) + "\n"; |
| // BOGUS NUMBER IN CACHED MODE report += "[ INFO] : " + std::to_string(stats.nbInfo) + "\n"; |
| report += "[ NOTE] : " + std::to_string(stats.nbNote) + "\n"; |
| if (!muteStdout) |
| { |
| std::cout << report << std::flush; |
| } |
| bool successLogFile = printToLogFile(report); |
| return (successLogFile && (!stats.nbFatal)); |
| } |
| |
| |
| ErrorContainer::Stats ErrorContainer::getErrorStats() { |
| const std::map<ErrorDefinition::ErrorType, ErrorDefinition::ErrorInfo>& infoMap = ErrorDefinition::getErrorInfoMap(); |
| ErrorContainer::Stats stats; |
| for (auto msg : m_errors) |
| { |
| if (!msg.m_waived) |
| { |
| ErrorDefinition::ErrorType type = msg.m_errorId; |
| std::map<ErrorDefinition::ErrorType, ErrorDefinition::ErrorInfo>::const_iterator itr = infoMap.find(type); |
| if (itr != infoMap.end()) |
| { |
| ErrorDefinition::ErrorInfo info = (*itr).second; |
| switch (info.m_severity) |
| { |
| case ErrorDefinition::FATAL: |
| stats.nbFatal++; |
| break; |
| case ErrorDefinition::ERROR: |
| stats.nbError++; |
| break; |
| case ErrorDefinition::WARNING: |
| stats.nbWarning++; |
| break; |
| case ErrorDefinition::INFO: |
| stats.nbInfo++; |
| break; |
| case ErrorDefinition::NOTE: |
| stats.nbNote++; |
| break; |
| } |
| } |
| } |
| } |
| return stats; |
| } |
| |
| |
| static std::mutex m; |
| bool ErrorContainer::printToLogFile(std::string report) { |
| m.lock(); |
| const std::string& logFileName = m_clp->getSymbolTable()->getSymbol(m_clp->getLogFileId()); |
| std::ofstream ofs; |
| ofs.open(logFileName, std::fstream::app); |
| if (!ofs.good()) |
| { |
| if (!m_reportedFatalErrorLogFile) |
| { |
| std::cerr << "[FATAL:LG0002] Cannot open log file \"" << logFileName << "\" in append mode" << std::endl; |
| m_reportedFatalErrorLogFile = true; |
| } |
| m.unlock(); |
| return false; |
| } |
| else |
| { |
| ofs << report << std::flush; |
| ofs.close(); |
| } |
| m.unlock(); |
| return true; |
| } |
| |
| bool ErrorContainer::printMessage(Error& error, bool muteStdout) { |
| if (error.m_reported) |
| return false; |
| std::pair<std::string, bool> report = createReport_(error); |
| |
| if (!muteStdout) |
| { |
| std::cout << report.first << std::flush; |
| } |
| bool successLogFile = printToLogFile(report.first); |
| error.m_reported = true; |
| return (successLogFile && (!report.second)); |
| } |
| |
| |
| bool ErrorContainer::printMessages(bool muteStdout) { |
| std::pair<std::string, bool> report = createReport_(); |
| |
| if (!muteStdout) |
| { |
| std::cout << report.first << std::flush; |
| } |
| bool successLogFile = printToLogFile(report.first); |
| return (successLogFile && (!report.second)); |
| } |
| |
| |
| |
| |
| |