| /* | |
| 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 "ErrorReporting/ErrorContainer.h" | |
| #include <mutex> | |
| #include <iostream> | |
| #include <fstream> | |
| #include "CommandLine/CommandLineParser.h" | |
| #include "ErrorReporting/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::SYNTAX: | |
| severity = "SYNTX"; | |
| 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 += "[ SYNTAX] : " + std::to_string(stats.nbSyntax) + "\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) && (!stats.nbSyntax)); | |
| } | |
| 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::SYNTAX: | |
| stats.nbSyntax++; | |
| 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)); | |
| } |