| /* |
| 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 "Python.h" |
| #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 */ |
| } |
| |
| #include <unistd.h> |
| #include <stdio.h> |
| |
| 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, |
| (PyThreadState*) 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)); |
| } |