[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/cppcheck-opensource/cppcheck/main/lib/errorlogger.cpp [Back]  [Original]

/*
 * Cppcheck - A tool for static C/C++ code analysis
 * Copyright (C) 2007-2026 Cppcheck team.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

#include "errorlogger.h"

#include "color.h"
#include "cppcheck.h"
#include "path.h"
#include "settings.h"
#include "suppressions.h"
#include "symboldatabase.h"
#include "token.h"
#include "tokenlist.h"
#include "utils.h"
#include "checkers.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "xml.h"

const std::set ErrorLogger::mCriticalErrorIds{
    "cppcheckError",
    "cppcheckLimit",
    "includeNestedTooDeeply",
    "internalAstError",
    "instantiationError",
    "internalError",
    "missingFile",
    "premium-internalError",
    "premium-invalidArgument",
    "premium-invalidLicense",
    "preprocessorErrorDirective",
    "syntaxError",
    "unhandledChar",
    "unknownMacro"
};

ErrorMessage::ErrorMessage()
    : severity(Severity::none), cwe(0U), certainty(Certainty::normal)
{}

// TODO: id and msg are swapped compared to other calls
ErrorMessage::ErrorMessage(std::list callStack, std::string file1, Severity severity, const std::string &msg, std::string id, Certainty certainty) :
    callStack(std::move(callStack)), // locations for this error message
    id(std::move(id)),               // set the message id
    file0(std::move(file1)),
    severity(severity),   // severity for this error message
    cwe(0U),
    certainty(certainty)
{
    // set the summary and verbose messages
    setmsg(msg);
}


// TODO: id and msg are swapped compared to other calls
ErrorMessage::ErrorMessage(std::list callStack, std::string file1, Severity severity, const std::string &msg, std::string id, const CWE &cwe, Certainty certainty) :
    callStack(std::move(callStack)), // locations for this error message
    id(std::move(id)),               // set the message id
    file0(std::move(file1)),
    severity(severity),   // severity for this error message
    cwe(cwe.id),
    certainty(certainty)
{
    // set the summary and verbose messages
    setmsg(msg);
}

ErrorMessage::ErrorMessage(const std::list& callstack, const TokenList* list, Severity severity, std::string id, const std::string& msg, Certainty certainty)
    : id(std::move(id)), severity(severity), cwe(0U), certainty(certainty)
{
    // Format callstack
    for (auto it = callstack.cbegin(); it != callstack.cend(); ++it) {
        // --errorlist can provide null values here
        if (!(*it))
            continue;

        callStack.emplace_back(*it, list);
    }

    if (list && !list->getFiles().empty())
        file0 = list->getFiles()[0];

    setmsg(msg);

    calculateWarningHash(callstack);
}


ErrorMessage::ErrorMessage(const std::list& callstack, const TokenList* list, Severity severity, std::string id, const std::string& msg, const CWE &cwe, Certainty certainty)
    : id(std::move(id)), severity(severity), cwe(cwe.id), certainty(certainty)
{
    // Format callstack
    for (const Token *tok: callstack) {
        // --errorlist can provide null values here
        if (!tok)
            continue;

        callStack.emplace_back(tok, list);
    }

    if (list && !list->getFiles().empty())
        file0 = list->getFiles()[0];

    setmsg(msg);

    calculateWarningHash(callstack);
}

ErrorMessage::ErrorMessage(ErrorPath errorPath, const TokenList *tokenList, Severity severity, const char id[], const std::string &msg, const CWE &cwe, Certainty certainty)
    : id(id), severity(severity), cwe(cwe.id), certainty(certainty)
{
    // Format callstack
    for (ErrorPathItem& e: errorPath) {
        const Token *tok = e.first;
        // --errorlist can provide null values here
        if (!tok)
            continue;

        std::string& path_info = e.second;

        std::string info;
        if (startsWith(path_info,"$symbol:") && path_info.find('\n') < path_info.size()) {
            const std::string::size_type pos = path_info.find('\n');
            const std::string symbolName = path_info.substr(8, pos - 8);
            info = replaceStr(path_info.substr(pos+1), "$symbol", symbolName);
        }
        else {
            info = std::move(path_info);
        }

        callStack.emplace_back(tok, std::move(info), tokenList);
    }

    if (tokenList && !tokenList->getFiles().empty())
        file0 = tokenList->getFiles()[0];

    setmsg(msg);

    std::list tokens;
    std::transform(errorPath.cbegin(), errorPath.cend(), std::back_inserter(tokens),
                   [](const ErrorPathItem& e) {
        return e.first;
    });
    calculateWarningHash(tokens);
}

// TODO: improve errorhandling?
ErrorMessage::ErrorMessage(const tinyxml2::XMLElement * const errmsg)
    : severity(Severity::none),
    cwe(0U),
    certainty(Certainty::normal)
{
    const char * const unknown = "";

    const char *attr = errmsg->Attribute("id");
    id = attr ? attr : unknown;

    attr = errmsg->Attribute("file0");
    file0 = attr ? attr : "";

    attr = errmsg->Attribute("severity");
    severity = attr ? severityFromString(attr) : Severity::none;

    attr = errmsg->Attribute("cwe");
    // cppcheck-suppress templateInstantiation - TODO: fix this - see #11631
    cwe.id = attr ? strToInt(attr) : 0;

    attr = errmsg->Attribute("inconclusive");
    certainty = (attr && (std::strcmp(attr, "true") == 0)) ? Certainty::inconclusive : Certainty::normal;

    attr = errmsg->Attribute("msg");
    mShortMessage = attr ? attr : "";

    attr = errmsg->Attribute("verbose");
    mVerboseMessage = attr ? attr : "";

    attr = errmsg->Attribute("hash");
    hash = attr ? strToInt(attr) : 0;

    for (const tinyxml2::XMLElement *e = errmsg->FirstChildElement(); e; e = e->NextSiblingElement()) {
        const char* name = e->Name();
        if (std::strcmp(name,"location")==0) {
            const char *strorigfile = e->Attribute("origfile");
            const char *strfile = e->Attribute("file");
            const char *strinfo = e->Attribute("info");
            const char *strline = e->Attribute("line");
            const char *strcolumn = e->Attribute("column");

            const char *file = strfile ? strfile : unknown;
            const char *origfile = strorigfile ? strorigfile : file;
            const char *info = strinfo ? strinfo : "";
            const int line = strline ? strToInt(strline) : 0;
            const int column = strcolumn ? strToInt(strcolumn) : 0;
            callStack.emplace_front(origfile, info, line, column);
            if (strorigfile)
                callStack.front().setfile(file);
        } else if (std::strcmp(name,"symbol")==0) {
            mSymbolNames += e->GetText();
        }
    }
}

void ErrorMessage::setmsg(const std::string &msg)
{
    // If a message ends to a '\n' and contains only a one '\n'
    // it will cause the mVerboseMessage to be empty which will show
    // as an empty message to the user if --verbose is used.
    // Even this doesn't cause problems with messages that have multiple
    // lines, none of the error messages should end into it.
    assert(!endsWith(msg,'\n'));

    // The summary and verbose message are separated by a newline
    // If there is no newline then both the summary and verbose messages
    // are the given message
    const std::string::size_type pos = msg.find('\n');
    const std::string symbolName = mSymbolNames.empty() ? std::string() : mSymbolNames.substr(0, mSymbolNames.find('\n'));
    if (pos == std::string::npos) {
        mShortMessage = replaceStr(msg, "$symbol", symbolName);
        mVerboseMessage = replaceStr(msg, "$symbol", symbolName);
    } else if (startsWith(msg,"$symbol:")) {
        mSymbolNames += msg.substr(8, pos-7);
        setmsg(msg.substr(pos + 1));
    } else {
        mShortMessage = replaceStr(msg.substr(0, pos), "$symbol", symbolName);
        mVerboseMessage = replaceStr(msg.substr(pos + 1), "$symbol", symbolName);
    }
}

void ErrorMessage::calculateWarningHash(const std::list& callstack)
{
    if (callstack.empty())
        return;
    // Calculate a hash for this warning message
    std::string hashString;
    for (const Token* tok: callstack) {
        if (!tok)
            continue;
        if (!tok->scope())
            return; // might be a syntax error before scope info has been set
        if (tok->scope()->isExecutable()) {
            // Executable scope => include all tokens in the function => if the
            // function is changed the hash is changed
            for (const Token* t = tok; t; t = t->previous()) {
                if (!t->scope()->isExecutable())
                    break;
                hashString += " " + t->str();
            }
            for (const Token* t = tok->next(); t; t = t->next()) {
                if (!t->scope()->isExecutable())
                    break;
                hashString += " " + t->str();
            }
        } else {
            // Non executable scope => include tokens in current statement => if the current statement is changed the hash is changed
            for (const Token* t = tok; t; t = t->previous()) {
                if (t->str() == ";")
                    break;
                if (t->scope() != tok->scope()) // stop on {} unless its an initializer
                    break;
                hashString += " " + t->str();
            }
            for (const Token* t = tok->next(); t; t = t->next()) {
                hashString += " " + t->str();
                if (t->str() == ";")
                    break;
                if (t->scope() != tok->scope()) // stop on {} unless its an initializer
                    break;
            }
        }
    }

    hashString = id + '\n' + mShortMessage + '\n' + hashString;

    // hash algorithm: sdbm
    // any hash algorithm can be used but it has to be the same hash on different platforms and compilers
    hash = std::accumulate(hashString.cbegin(), hashString.cend(), std::size_t{0}, [](std::size_t h, unsigned char c) {
        return static_cast(c) + (h getSourceFilePath(), 0, 0);
        }
    }
    ErrorMessage errmsg(std::move(locationList),
                        tokenList ? tokenList->getSourceFilePath() : filename,
                        Severity::error,
                        (msg.empty() ? "" : (msg + ": ")) + internalError.errorMessage,
                        internalError.id,
                        Certainty::normal);
    // TODO: find a better way
    if (!internalError.details.empty())
        errmsg.mVerboseMessage = errmsg.mVerboseMessage + ": " + internalError.details;
    return errmsg;
}

std::string ErrorMessage::serialize() const
{
    // Serialize this message into a simple string
    std::string oss;
    serializeString(oss, id);
    serializeString(oss, severityToString(severity));
    serializeString(oss, std::to_string(cwe.id));
    serializeString(oss, std::to_string(hash));
    serializeString(oss, fixInvalidChars(remark));
    serializeString(oss, file0);
    serializeString(oss, (certainty == Certainty::inconclusive) ? "1" : "0");

    const std::string saneShortMessage = fixInvalidChars(mShortMessage);
    const std::string saneVerboseMessage = fixInvalidChars(mVerboseMessage);

    serializeString(oss, saneShortMessage);
    serializeString(oss, saneVerboseMessage);
    serializeString(oss, mSymbolNames);
    oss += std::to_string(callStack.size());
    oss += " ";

    for (auto loc = callStack.cbegin(); loc != callStack.cend(); ++loc) {
        std::string frame;
        frame += std::to_string(loc->line);
        frame += '\t';
        frame += std::to_string(loc->column);
        frame += '\t';
        frame += loc->getfile(false);
        frame += '\t';
        frame += loc->getOrigFile(false);
        frame += '\t';
        frame += loc->getinfo();
        serializeString(oss, frame);
    }

    return oss;
}

void ErrorMessage::deserialize(const std::string &data)
{
    // TODO: clear all fields
    certainty = Certainty::normal;
    callStack.clear();

    std::istringstream iss(data);
    std::array results;
    std::size_t elem = 0;
    while (iss.good() && elem < 10) {
        unsigned int len = 0;
        if (!(iss >> len))
            throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid length");

        if (iss.get() != ' ')
            throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid separator");

        if (!iss.good())
            throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - premature end of data");

        std::string temp;
        if (len > 0) {
            temp.resize(len);
            iss.read(&temp[0], len);

            if (!iss.good())
                throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - premature end of data");
        }

        results[elem++] = std::move(temp);
    }

    if (!iss.good())
        throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - premature end of data");

    if (elem != 10)
        throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - insufficient elements");

    id = std::move(results[0]);
    severity = severityFromString(results[1]);
    cwe.id = 0;
    if (!results[2].empty()) {
        std::string err;
        if (!strToInt(results[2], cwe.id, &err))
            throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid CWE ID - " + err);
    }
    hash = 0;
    if (!results[3].empty()) {
        std::string err;
        if (!strToInt(results[3], hash, &err))
            throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid hash - " + err);
    }
    remark = std::move(results[4]);
    file0 = std::move(results[5]);
    if (results[6] == "1")
        certainty = Certainty::inconclusive;
    mShortMessage = std::move(results[7]);
    mVerboseMessage = std::move(results[8]);
    mSymbolNames = std::move(results[9]);

    unsigned int stackSize = 0;
    if (!(iss >> stackSize))
        throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid stack size");

    if (iss.get() != ' ')
        throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid separator");

    if (stackSize == 0)
        return;

    while (iss.good()) {
        unsigned int len = 0;
        if (!(iss >> len))
            throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid length (stack)");

        if (iss.get() != ' ')
            throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid separator (stack)");

        std::string temp;
        if (len > 0) {
            temp.resize(len);
            iss.read(&temp[0], len);

            if (!iss.good())
                throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - premature end of data (stack)");
        }

        std::vector substrings;
        substrings.reserve(5);
        for (std::string::size_type pos = 0; pos < temp.size() && substrings.size() < 5; ++pos) {
            if (substrings.size() == 4) {
                substrings.push_back(temp.substr(pos));
                break;
            }
            const std::string::size_type start = pos;
            pos = temp.find('\t', pos);
            if (pos == std::string::npos) {
                substrings.push_back(temp.substr(start));
                break;
            }
            substrings.push_back(temp.substr(start, pos - start));
        }
        if (substrings.size() < 4)
            throw InternalError(nullptr, "Internal Error: Deserializing of error message failed");

        // (*loc).line \r\n"
          = s.size())
            return s.substr(0,pos1) + to;
        if (s[pos2] == '_' || std::isalnum(s[pos2])) {
            pos1++;
            continue;
        }
        s.replace(pos1, from.size(), to);
        pos1 += to.size();
    }
    return s;
}

void substituteTemplateFormatStatic(std::string& templateFormat, bool eraseColors)
{
    replaceSpecialChars(templateFormat);
    replaceColors(templateFormat, eraseColors);
}

void substituteTemplateLocationStatic(std::string& templateLocation, bool eraseColors)
{
    replaceSpecialChars(templateLocation);
    replaceColors(templateLocation, eraseColors);
}

std::string getClassification(const std::string &guideline, ReportType reportType) {
    if (guideline.empty())
        return "";

    const auto getClassification = [](const std::vector &info, const std::string &guideline) -> std::string {
        const auto it = std::find_if(info.cbegin(), info.cend(), [&](const checkers::Info &i) {
            return caseInsensitiveStringCompare(i.guideline, guideline) == 0;
        });
        if (it == info.cend())
            return "";
        return it->classification;
    };

    switch (reportType) {
    case ReportType::autosar:
        return getClassification(checkers::autosarInfo, guideline);
    case ReportType::certC:
        return getClassification(checkers::certCInfo, guideline);
    case ReportType::certCpp:
        return getClassification(checkers::certCppInfo, guideline);
    case ReportType::misraC2012:
    case ReportType::misraC2023:
    case ReportType::misraC2025:
    {
        const bool isDirective = guideline.rfind("Dir ", 0) == 0;

        const std::size_t offset = isDirective ? 4 : 0;
        auto components = splitString(guideline.substr(offset), '.');
        if (components.size() != 2)
            return "";

        const int a = std::stoi(components[0]);
        const int b = std::stoi(components[1]);

        const std::vector *info = nullptr;
        switch (reportType) {
        case ReportType::misraC2012:
            info = isDirective ? &checkers::misraC2012Directives : &checkers::misraC2012Rules;
            break;
        case ReportType::misraC2023:
            info = isDirective ? &checkers::misraC2023Directives : &checkers::misraC2023Rules;
            break;
        case ReportType::misraC2025:
            info = isDirective ? &checkers::misraC2025Directives : &checkers::misraC2025Rules;
            break;
        default:
            cppcheck::unreachable();
        }

        const auto it = std::find_if(info->cbegin(), info->cend(), [&](const checkers::MisraInfo &i) {
                return i.a == a && i.b == b;
            });

        return it == info->cend() ? "" : it->str;
    }
    case ReportType::misraCpp2008:
    case ReportType::misraCpp2023:
    {
        const std::vector *info;
        std::vector components;

        if (reportType == ReportType::misraCpp2008) {
            info = &checkers::misraCpp2008Rules;
            components = splitString(guideline, '-');
        } else {
            if (guideline.rfind("Dir ", 0) == 0) {
                components = splitString(guideline.substr(4), '.');
                info = &checkers::misraCpp2023Directives;
            } else {
                components = splitString(guideline, '.');
                info = &checkers::misraCpp2023Rules;
            }
        }

        if (components.size() != 3)
            return "";

        const int a = std::stoi(components[0]);
        const int b = std::stoi(components[1]);
        const int c = std::stoi(components[2]);

        const auto it = std::find_if(info->cbegin(), info->cend(), [&](const checkers::MisraCppInfo &i) {
                return i.a == a && i.b == b && i.c == c;
            });

        if (it == info->cend())
            return "";

        return it->classification;
    }
    default:
        return "";
    }
}

std::string getGuideline(const std::string &errId, ReportType reportType,
                         const std::map &guidelineMapping,
                         Severity severity)
{
    std::string guideline;

    switch (reportType) {
    case ReportType::autosar:
        if (errId.rfind("premium-autosar-", 0) == 0) {
            guideline = errId.substr(16);
            break;
        }
        if (errId.rfind("premium-misra-cpp-2008-", 0) == 0)
            guideline = "M" + errId.substr(23);
        break;
    case ReportType::certC:
    case ReportType::certCpp:
        if (errId.rfind("premium-cert-", 0) == 0) {
            guideline = errId.substr(13);
            std::transform(guideline.begin(), guideline.end(),
                           guideline.begin(), static_cast(std::toupper));
        }
        break;
    case ReportType::misraC2012:
    case ReportType::misraC2023:
    case ReportType::misraC2025:
        if (errId.rfind("misra-c20", 0) == 0 || errId.rfind("premium-misra-c-20", 0) == 0) {
            auto pos1 = errId.find("20") + 5;
            if (pos1 >= errId.size())
                break;
            if (errId.compare(pos1,4,"dir-",0,4) == 0)
                pos1 += 4;
            const auto endpos = errId.find('-', pos1);
            guideline = errId.substr(pos1, endpos-pos1);
        }
        break;
    case ReportType::misraCpp2008:
        if (errId.rfind("premium-misra-cpp-2008", 0) == 0)
            guideline = errId.substr(23);
        break;
    case ReportType::misraCpp2023:
        if (errId.rfind("premium-misra-cpp-2023", 0) == 0)
            guideline = errId.substr(errId.rfind('-') + 1);
        break;
    default:
        break;
    }

    if (!guideline.empty()) {
        if (errId.find("-dir-") != std::string::npos)
            guideline = "Dir " + guideline;
        return guideline;
    }

    auto it = guidelineMapping.find(errId);

    if (it != guidelineMapping.cend())
        return it->second;

    if (severity == Severity::error || severity == Severity::warning) {
        it = guidelineMapping.find("error");

        if (it != guidelineMapping.cend())
            return it->second;
    }

    return "";
}

std::map createGuidelineMapping(ReportType reportType) {
    std::map guidelineMapping;
    const std::vector *idMapping1 = nullptr;
    const std::vector *idMapping2 = nullptr;
    std::string ext1, ext2;

    switch (reportType) {
    case ReportType::autosar:
        idMapping1 = &checkers::idMappingAutosar;
        break;
    case ReportType::certCpp:
        idMapping2 = &checkers::idMappingCertCpp;
        ext2 = "-CPP";
        FALLTHROUGH;
    case ReportType::certC:
        idMapping1 = &checkers::idMappingCertC;
        ext1 = "-C";
        break;
    case ReportType::misraC2012:
    case ReportType::misraC2023:
    case ReportType::misraC2025:
        idMapping1 = &checkers::idMappingMisraC;
        break;
    case ReportType::misraCpp2008:
        idMapping1 = &checkers::idMappingMisraCpp2008;
        break;
    case ReportType::misraCpp2023:
        idMapping1 = &checkers::idMappingMisraCpp2023;
        break;
    default:
        break;
    }

    if (idMapping1) {
        for (const auto &i : *idMapping1)
            for (const std::string &cppcheckId : splitString(i.cppcheckId, ','))
                guidelineMapping[cppcheckId] = i.guideline + ext1;
    }

    if (idMapping2) {
        for (const auto &i : *idMapping2)
            for (const std::string &cppcheckId : splitString(i.cppcheckId, ','))
                guidelineMapping[cppcheckId] = i.guideline + ext2;
    }

    return guidelineMapping;
}

Web Proxy Viewer  |  New URL  |  Original Page