From 08d754d536c34884e484939f4e5313cfa714fd49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Mon, 8 Jan 2024 19:46:12 +0100 Subject: [PATCH 01/12] ClangImport: avoid unchecked pointer dereferences (#5856) The `Tokenizer` and `TokenList` objects which are passed around always need to exist so pass them by reference. --- lib/clangimport.cpp | 148 +++++++++++++++++++-------------------- lib/clangimport.h | 2 +- lib/cppcheck.cpp | 2 +- test/testclangimport.cpp | 4 +- 4 files changed, 78 insertions(+), 78 deletions(-) diff --git a/lib/clangimport.cpp b/lib/clangimport.cpp index a31956e68f1..1833e27e9f1 100644 --- a/lib/clangimport.cpp +++ b/lib/clangimport.cpp @@ -325,15 +325,15 @@ namespace clangimport { std::string nodeType; std::vector children; - void setLocations(TokenList *tokenList, int file, int line, int col); + void setLocations(TokenList &tokenList, int file, int line, int col); void dumpAst(int num = 0, int indent = 0) const; - void createTokens1(TokenList *tokenList) { + void createTokens1(TokenList &tokenList) { //dumpAst(); - if (!tokenList->back()) + if (!tokenList.back()) setLocations(tokenList, 0, 1, 1); else - setLocations(tokenList, tokenList->back()->fileIndex(), tokenList->back()->linenr(), 1); + setLocations(tokenList, tokenList.back()->fileIndex(), tokenList.back()->linenr(), 1); createTokens(tokenList); if (nodeType == VarDecl || nodeType == RecordDecl || nodeType == TypedefDecl) addtoken(tokenList, ";"); @@ -351,22 +351,22 @@ namespace clangimport { return children[c]; } private: - Token *createTokens(TokenList *tokenList); - Token *addtoken(TokenList *tokenList, const std::string &str, bool valueType=true); - const ::Type *addTypeTokens(TokenList *tokenList, const std::string &str, const Scope *scope = nullptr); - void addFullScopeNameTokens(TokenList *tokenList, const Scope *recordScope); - Scope *createScope(TokenList *tokenList, Scope::ScopeType scopeType, AstNodePtr astNode, const Token *def); - Scope *createScope(TokenList *tokenList, Scope::ScopeType scopeType, const std::vector &children2, const Token *def); - Token *createTokensCall(TokenList *tokenList); - void createTokensFunctionDecl(TokenList *tokenList); - void createTokensForCXXRecord(TokenList *tokenList); - Token *createTokensVarDecl(TokenList *tokenList); + Token *createTokens(TokenList &tokenList); + Token *addtoken(TokenList &tokenList, const std::string &str, bool valueType=true); + const ::Type *addTypeTokens(TokenList &tokenList, const std::string &str, const Scope *scope = nullptr); + void addFullScopeNameTokens(TokenList &tokenList, const Scope *recordScope); + Scope *createScope(TokenList &tokenList, Scope::ScopeType scopeType, AstNodePtr astNode, const Token *def); + Scope *createScope(TokenList &tokenList, Scope::ScopeType scopeType, const std::vector &children2, const Token *def); + Token *createTokensCall(TokenList &tokenList); + void createTokensFunctionDecl(TokenList &tokenList); + void createTokensForCXXRecord(TokenList &tokenList); + Token *createTokensVarDecl(TokenList &tokenList); std::string getSpelling() const; std::string getType(int index = 0) const; std::string getFullType(int index = 0) const; bool isDefinition() const; std::string getTemplateParameters() const; - const Scope *getNestedInScope(TokenList *tokenList); + const Scope *getNestedInScope(TokenList &tokenList); void setValueType(Token *tok); int mFile = 0; @@ -496,7 +496,7 @@ void clangimport::AstNode::dumpAst(int num, int indent) const } } -void clangimport::AstNode::setLocations(TokenList *tokenList, int file, int line, int col) +void clangimport::AstNode::setLocations(TokenList &tokenList, int file, int line, int col) { for (const std::string &ext: mExtTokens) { if (startsWith(ext, " 3 && ext[2] == ':'; const std::string::size_type sep1 = windowsPath ? ext.find(':', 4) : colon; const std::string::size_type sep2 = ext.find(':', sep1 + 1); - file = tokenList->appendFileIfNew(ext.substr(1, sep1 - 1)); + file = tokenList.appendFileIfNew(ext.substr(1, sep1 - 1)); line = strToInt(ext.substr(sep1 + 1, sep2 - sep1 - 1)); } } @@ -526,17 +526,17 @@ void clangimport::AstNode::setLocations(TokenList *tokenList, int file, int line } } -Token *clangimport::AstNode::addtoken(TokenList *tokenList, const std::string &str, bool valueType) +Token *clangimport::AstNode::addtoken(TokenList &tokenList, const std::string &str, bool valueType) { const Scope *scope = getNestedInScope(tokenList); - tokenList->addtoken(str, mLine, mCol, mFile); - tokenList->back()->scope(scope); + tokenList.addtoken(str, mLine, mCol, mFile); + tokenList.back()->scope(scope); if (valueType) - setValueType(tokenList->back()); - return tokenList->back(); + setValueType(tokenList.back()); + return tokenList.back(); } -const ::Type * clangimport::AstNode::addTypeTokens(TokenList *tokenList, const std::string &str, const Scope *scope) +const ::Type * clangimport::AstNode::addTypeTokens(TokenList &tokenList, const std::string &str, const Scope *scope) { if (str.find("\':\'") != std::string::npos) { return addTypeTokens(tokenList, str.substr(0, str.find("\':\'") + 1), scope); @@ -574,11 +574,11 @@ const ::Type * clangimport::AstNode::addTypeTokens(TokenList *tokenList, const s // Set Type if (!scope) { - scope = tokenList->back() ? tokenList->back()->scope() : nullptr; + scope = tokenList.back() ? tokenList.back()->scope() : nullptr; if (!scope) return nullptr; } - for (const Token *typeToken = tokenList->back(); Token::Match(typeToken, "&|*|%name%"); typeToken = typeToken->previous()) { + for (const Token *typeToken = tokenList.back(); Token::Match(typeToken, "&|*|%name%"); typeToken = typeToken->previous()) { if (!typeToken->isName()) continue; const ::Type *recordType = scope->check->findVariableType(scope, typeToken); @@ -590,12 +590,12 @@ const ::Type * clangimport::AstNode::addTypeTokens(TokenList *tokenList, const s return nullptr; } -void clangimport::AstNode::addFullScopeNameTokens(TokenList *tokenList, const Scope *recordScope) +void clangimport::AstNode::addFullScopeNameTokens(TokenList &tokenList, const Scope *recordScope) { if (!recordScope) return; std::list scopes; - while (recordScope && recordScope != tokenList->back()->scope() && !recordScope->isExecutable()) { + while (recordScope && recordScope != tokenList.back()->scope() && !recordScope->isExecutable()) { scopes.push_front(recordScope); recordScope = recordScope->nestedIn; } @@ -607,13 +607,13 @@ void clangimport::AstNode::addFullScopeNameTokens(TokenList *tokenList, const Sc } } -const Scope *clangimport::AstNode::getNestedInScope(TokenList *tokenList) +const Scope *clangimport::AstNode::getNestedInScope(TokenList &tokenList) { - if (!tokenList->back()) + if (!tokenList.back()) return &mData->mSymbolDatabase->scopeList.front(); - if (tokenList->back()->str() == "}" && mData->mNotScope.find(tokenList->back()) == mData->mNotScope.end()) - return tokenList->back()->scope()->nestedIn; - return tokenList->back()->scope(); + if (tokenList.back()->str() == "}" && mData->mNotScope.find(tokenList.back()) == mData->mNotScope.end()) + return tokenList.back()->scope()->nestedIn; + return tokenList.back()->scope(); } void clangimport::AstNode::setValueType(Token *tok) @@ -626,7 +626,7 @@ void clangimport::AstNode::setValueType(Token *tok) continue; TokenList decl(nullptr); - addTypeTokens(&decl, type, tok->scope()); + addTypeTokens(decl, type, tok->scope()); if (!decl.front()) break; @@ -638,13 +638,13 @@ void clangimport::AstNode::setValueType(Token *tok) } } -Scope *clangimport::AstNode::createScope(TokenList *tokenList, Scope::ScopeType scopeType, AstNodePtr astNode, const Token *def) +Scope *clangimport::AstNode::createScope(TokenList &tokenList, Scope::ScopeType scopeType, AstNodePtr astNode, const Token *def) { std::vector children2{std::move(astNode)}; return createScope(tokenList, scopeType, children2, def); } -Scope *clangimport::AstNode::createScope(TokenList *tokenList, Scope::ScopeType scopeType, const std::vector & children2, const Token *def) +Scope *clangimport::AstNode::createScope(TokenList &tokenList, Scope::ScopeType scopeType, const std::vector & children2, const Token *def) { SymbolDatabase *symbolDatabase = mData->mSymbolDatabase; @@ -682,7 +682,7 @@ Scope *clangimport::AstNode::createScope(TokenList *tokenList, Scope::ScopeType } } scope->bodyStart = addtoken(tokenList, "{"); - tokenList->back()->scope(scope); + tokenList.back()->scope(scope); mData->scopeAccessControl[scope] = scope->defaultAccess(); if (!children2.empty()) { for (const AstNodePtr &astNode: children2) { @@ -700,7 +700,7 @@ Scope *clangimport::AstNode::createScope(TokenList *tokenList, Scope::ScopeType astNode->createTokens(tokenList); if (scopeType == Scope::ScopeType::eEnum) astNode->addtoken(tokenList, ","); - else if (!Token::Match(tokenList->back(), "[;{}]")) + else if (!Token::Match(tokenList.back(), "[;{}]")) astNode->addtoken(tokenList, ";"); } } @@ -710,7 +710,7 @@ Scope *clangimport::AstNode::createScope(TokenList *tokenList, Scope::ScopeType return scope; } -Token *clangimport::AstNode::createTokens(TokenList *tokenList) +Token *clangimport::AstNode::createTokens(TokenList &tokenList) { if (nodeType == ArraySubscriptExpr) { Token *array = getChild(0)->createTokens(tokenList); @@ -796,7 +796,7 @@ Token *clangimport::AstNode::createTokens(TokenList *tokenList) if (nodeType == CompoundStmt) { for (const AstNodePtr& child: children) { child->createTokens(tokenList); - if (!Token::Match(tokenList->back(), "[;{}]")) + if (!Token::Match(tokenList.back(), "[;{}]")) child->addtoken(tokenList, ";"); } return nullptr; @@ -818,14 +818,14 @@ Token *clangimport::AstNode::createTokens(TokenList *tokenList) return getChild(0)->createTokens(tokenList); if (nodeType == CXXBoolLiteralExpr) { addtoken(tokenList, mExtTokens.back()); - tokenList->back()->setValueType(new ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0)); - return tokenList->back(); + tokenList.back()->setValueType(new ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0)); + return tokenList.back(); } if (nodeType == CXXConstructExpr) { if (!children.empty()) return getChild(0)->createTokens(tokenList); addTypeTokens(tokenList, '\'' + getType() + '\''); - Token *type = tokenList->back(); + Token *type = tokenList.back(); Token *par1 = addtoken(tokenList, "("); Token *par2 = addtoken(tokenList, ")"); par1->link(par2); @@ -866,7 +866,7 @@ Token *clangimport::AstNode::createTokens(TokenList *tokenList) } } if (!range) - throw InternalError(tokenList->back(), "Failed to import CXXForRangeStmt. Range?"); + throw InternalError(tokenList.back(), "Failed to import CXXForRangeStmt. Range?"); Token *expr2 = range->createTokens(tokenList); Token *par2 = addtoken(tokenList, ")"); @@ -961,7 +961,7 @@ Token *clangimport::AstNode::createTokens(TokenList *tokenList) } if (nodeType == DoStmt) { addtoken(tokenList, "do"); - createScope(tokenList, Scope::ScopeType::eDo, getChild(0), tokenList->back()); + createScope(tokenList, Scope::ScopeType::eDo, getChild(0), tokenList.back()); Token *tok1 = addtoken(tokenList, "while"); Token *par1 = addtoken(tokenList, "("); Token *expr = children[1]->createTokens(tokenList); @@ -1088,7 +1088,7 @@ Token *clangimport::AstNode::createTokens(TokenList *tokenList) createScope(tokenList, Scope::ScopeType::eIf, thenCode, iftok); if (elseCode) { elseCode->addtoken(tokenList, "else"); - createScope(tokenList, Scope::ScopeType::eElse, elseCode, tokenList->back()); + createScope(tokenList, Scope::ScopeType::eElse, elseCode, tokenList.back()); } return nullptr; } @@ -1099,11 +1099,11 @@ Token *clangimport::AstNode::createTokens(TokenList *tokenList) return expr; } if (nodeType == InitListExpr) { - const Scope *scope = tokenList->back()->scope(); + const Scope *scope = tokenList.back()->scope(); Token *start = addtoken(tokenList, "{"); start->scope(scope); for (const AstNodePtr& child: children) { - if (tokenList->back()->str() != "{") + if (tokenList.back()->str() != "{") addtoken(tokenList, ","); child->createTokens(tokenList); } @@ -1261,7 +1261,7 @@ Token *clangimport::AstNode::createTokens(TokenList *tokenList) return addtoken(tokenList, "?" + nodeType + "?"); } -Token * clangimport::AstNode::createTokensCall(TokenList *tokenList) +Token * clangimport::AstNode::createTokensCall(TokenList &tokenList) { int firstParam; Token *f; @@ -1302,7 +1302,7 @@ Token * clangimport::AstNode::createTokensCall(TokenList *tokenList) return par1; } -void clangimport::AstNode::createTokensFunctionDecl(TokenList *tokenList) +void clangimport::AstNode::createTokensFunctionDecl(TokenList &tokenList) { const bool prev = contains(mExtTokens, "prev"); const bool hasBody = !children.empty() && children.back()->nodeType == CompoundStmt; @@ -1317,9 +1317,9 @@ void clangimport::AstNode::createTokensFunctionDecl(TokenList *tokenList) addtoken(tokenList, "static"); if (isInline) addtoken(tokenList, "inline"); - const Token * const before = tokenList->back(); + const Token * const before = tokenList.back(); addTypeTokens(tokenList, '\'' + getType() + '\''); - startToken = before ? before->next() : tokenList->front(); + startToken = before ? before->next() : tokenList.front(); } if (mExtTokens.size() > 4 && mExtTokens[1] == "parent") @@ -1346,7 +1346,7 @@ void clangimport::AstNode::createTokensFunctionDecl(TokenList *tokenList) auto * const function = const_cast(nameToken->function()); if (!prev) { - auto accessControl = mData->scopeAccessControl.find(tokenList->back()->scope()); + auto accessControl = mData->scopeAccessControl.find(tokenList.back()->scope()); if (accessControl != mData->scopeAccessControl.end()) function->access = accessControl->second; } @@ -1377,10 +1377,10 @@ void clangimport::AstNode::createTokensFunctionDecl(TokenList *tokenList) AstNodePtr child = children[i]; if (child->nodeType != ParmVarDecl) continue; - if (tokenList->back() != par1) + if (tokenList.back() != par1) addtoken(tokenList, ","); const Type *recordType = addTypeTokens(tokenList, child->mExtTokens.back(), nestedIn); - const Token *typeEndToken = tokenList->back(); + const Token *typeEndToken = tokenList.back(); const std::string spelling = child->getSpelling(); Token *vartok = nullptr; if (!spelling.empty()) @@ -1424,7 +1424,7 @@ void clangimport::AstNode::createTokensFunctionDecl(TokenList *tokenList) } } -void clangimport::AstNode::createTokensForCXXRecord(TokenList *tokenList) +void clangimport::AstNode::createTokensForCXXRecord(TokenList &tokenList) { const bool isStruct = contains(mExtTokens, "struct"); Token * const classToken = addtoken(tokenList, isStruct ? "struct" : "class"); @@ -1466,10 +1466,10 @@ void clangimport::AstNode::createTokensForCXXRecord(TokenList *tokenList) const_cast(classToken->scope())->definedTypesMap[className] = scope->definedType; } addtoken(tokenList, ";"); - const_cast(tokenList->back())->scope(classToken->scope()); + const_cast(tokenList.back())->scope(classToken->scope()); } -Token * clangimport::AstNode::createTokensVarDecl(TokenList *tokenList) +Token * clangimport::AstNode::createTokensVarDecl(TokenList &tokenList) { const std::string addr = mExtTokens.front(); if (contains(mExtTokens, "static")) @@ -1479,14 +1479,14 @@ Token * clangimport::AstNode::createTokensVarDecl(TokenList *tokenList) typeIndex--; const std::string type = mExtTokens[typeIndex]; const std::string name = mExtTokens[typeIndex - 1]; - const Token *startToken = tokenList->back(); + const Token *startToken = tokenList.back(); const ::Type *recordType = addTypeTokens(tokenList, type); if (!startToken) - startToken = tokenList->front(); + startToken = tokenList.front(); else if (startToken->str() != "static") startToken = startToken->next(); Token *vartok1 = addtoken(tokenList, name); - auto *scope = const_cast(tokenList->back()->scope()); + auto *scope = const_cast(tokenList.back()->scope()); scope->varlist.emplace_back(vartok1, unquote(type), startToken, vartok1->previous(), 0, scope->defaultAccess(), recordType, scope); mData->varDecl(addr, vartok1, &scope->varlist.back()); if (mExtTokens.back() == "cinit" && !children.empty()) { @@ -1510,9 +1510,9 @@ Token * clangimport::AstNode::createTokensVarDecl(TokenList *tokenList) return vartok1; } -static void setTypes(TokenList *tokenList) +static void setTypes(TokenList &tokenList) { - for (Token *tok = tokenList->front(); tok; tok = tok->next()) { + for (Token *tok = tokenList.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { for (Token *typeToken = tok->tokAt(2); typeToken->str() != ")"; typeToken = typeToken->next()) { if (typeToken->type()) @@ -1523,9 +1523,9 @@ static void setTypes(TokenList *tokenList) } } -static void setValues(const Tokenizer *tokenizer, const SymbolDatabase *symbolDatabase) +static void setValues(const Tokenizer &tokenizer, const SymbolDatabase *symbolDatabase) { - const Settings * const settings = tokenizer->getSettings(); + const Settings * const settings = tokenizer.getSettings(); for (const Scope& scope : symbolDatabase->scopeList) { if (!scope.definedType) @@ -1542,7 +1542,7 @@ static void setValues(const Tokenizer *tokenizer, const SymbolDatabase *symbolDa scope.definedType->sizeOf = typeSize; } - for (auto *tok = const_cast(tokenizer->tokens()); tok; tok = tok->next()) { + for (auto *tok = const_cast(tokenizer.tokens()); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { ValueType vt = ValueType::parseDecl(tok->tokAt(2), *settings); const int sz = vt.typeSize(settings->platform, true); @@ -1563,18 +1563,18 @@ static void setValues(const Tokenizer *tokenizer, const SymbolDatabase *symbolDa } } -void clangimport::parseClangAstDump(Tokenizer *tokenizer, std::istream &f) +void clangimport::parseClangAstDump(Tokenizer &tokenizer, std::istream &f) { - TokenList *tokenList = &tokenizer->list; + TokenList &tokenList = tokenizer.list; - tokenizer->createSymbolDatabase(); - auto *symbolDatabase = const_cast(tokenizer->getSymbolDatabase()); + tokenizer.createSymbolDatabase(); + auto *symbolDatabase = const_cast(tokenizer.getSymbolDatabase()); symbolDatabase->scopeList.emplace_back(nullptr, nullptr, nullptr); symbolDatabase->scopeList.back().type = Scope::ScopeType::eGlobal; symbolDatabase->scopeList.back().check = symbolDatabase; clangimport::Data data; - data.mSettings = tokenizer->getSettings(); + data.mSettings = tokenizer.getSettings(); data.mSymbolDatabase = symbolDatabase; std::string line; std::vector tree; @@ -1617,16 +1617,16 @@ void clangimport::parseClangAstDump(Tokenizer *tokenizer, std::istream &f) tree[0]->createTokens1(tokenList); // Validation - for (const Token *tok = tokenList->front(); tok; tok = tok->next()) { + for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { if (Token::Match(tok, "(|)|[|]|{|}") && !tok->link()) throw InternalError(tok, "Token::link() is not set properly"); } - if (tokenList->front()) - tokenList->front()->assignIndexes(); + if (tokenList.front()) + tokenList.front()->assignIndexes(); symbolDatabase->clangSetVariables(data.getVariableList()); symbolDatabase->createSymbolDatabaseExprIds(); - tokenList->clangSetOrigFiles(); + tokenList.clangSetOrigFiles(); setTypes(tokenList); setValues(tokenizer, symbolDatabase); } diff --git a/lib/clangimport.h b/lib/clangimport.h index 5aabe25fafe..4e96fe8ee58 100644 --- a/lib/clangimport.h +++ b/lib/clangimport.h @@ -29,7 +29,7 @@ class Tokenizer; namespace clangimport { - void CPPCHECKLIB parseClangAstDump(Tokenizer *tokenizer, std::istream &f); + void CPPCHECKLIB parseClangAstDump(Tokenizer &tokenizer, std::istream &f); } #endif diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 032a4a47049..b7a27607cba 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -494,7 +494,7 @@ unsigned int CppCheck::checkClang(const std::string &path) std::istringstream ast(output2); Tokenizer tokenizer(&mSettings, this); tokenizer.list.appendFileIfNew(path); - clangimport::parseClangAstDump(&tokenizer, ast); + clangimport::parseClangAstDump(tokenizer, ast); ValueFlow::setValues(tokenizer.list, const_cast(*tokenizer.getSymbolDatabase()), this, diff --git a/test/testclangimport.cpp b/test/testclangimport.cpp index e301c41db02..43de93146a8 100644 --- a/test/testclangimport.cpp +++ b/test/testclangimport.cpp @@ -142,7 +142,7 @@ class TestClangImport : public TestFixture { const Settings settings = settingsBuilder().clang().build(); Tokenizer tokenizer(&settings, this); std::istringstream istr(clang); - clangimport::parseClangAstDump(&tokenizer, istr); + clangimport::parseClangAstDump(tokenizer, istr); if (!tokenizer.tokens()) { return std::string(); } @@ -1057,7 +1057,7 @@ class TestClangImport : public TestFixture { Tokenizer tokenizer(&settings, this); \ { \ std::istringstream istr(AST); \ - clangimport::parseClangAstDump(&tokenizer, istr); \ + clangimport::parseClangAstDump(tokenizer, istr); \ } \ const SymbolDatabase *db = tokenizer.getSymbolDatabase(); \ ASSERT(db) From 8903f0e4b14af52f96621cee6dc8a8ff26f8f7bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Tue, 9 Jan 2024 11:45:44 +0100 Subject: [PATCH 02/12] daca: Add compare-normal-exhaustive.py script to compare normal and exhaustive check level (#5860) --- tools/compare-normal-exhaustive.py | 213 +++++++++++++++++++++++++++++ tools/donate_cpu_lib.py | 16 ++- 2 files changed, 224 insertions(+), 5 deletions(-) create mode 100755 tools/compare-normal-exhaustive.py diff --git a/tools/compare-normal-exhaustive.py b/tools/compare-normal-exhaustive.py new file mode 100755 index 00000000000..7139748331f --- /dev/null +++ b/tools/compare-normal-exhaustive.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python3 + +# Compare "normal" check level and "exhaustive" check level + +import donate_cpu_lib as lib +import argparse +import glob +import os +import sys +import random +import subprocess + + +def format_float(a, b=1): + if a > 0 and b > 0: + return '{:.2f}'.format(a / b) + return 'N/A' + + +if __name__ == "__main__": + __my_script_name = os.path.splitext(os.path.basename(sys.argv[0]))[0] + __work_path = os.path.expanduser(os.path.join('~', 'cppcheck-' + __my_script_name + '-workfolder')) + + parser = argparse.ArgumentParser(description='Compare --check-level=normal and --check-level=exhaustive output') + parser.add_argument('-j', default=1, type=int, help='Concurency execution threads') + parser.add_argument('--cppcheck-path', default=None, type=str, help='Path to Cppcheck binary, if not given then clone and compile') + package_group = parser.add_mutually_exclusive_group() + package_group.add_argument('-p', default=256, type=int, help='Count of packages to check') + package_group.add_argument('--packages', nargs='+', help='Check specific packages and then stop.') + package_group.add_argument('--packages-path', default=None, type=str, help='Check packages in path.') + parser.add_argument('-o', default='my_check_diff.log', help='Filename of result inside a working path dir') + + language_group = parser.add_mutually_exclusive_group() + language_group.add_argument('--c-only', dest='c_only', help='Only process c packages', action='store_true') + language_group.add_argument('--cpp-only', dest='cpp_only', help='Only process c++ packages', action='store_true') + parser.add_argument('--work-path', '--work-path=', default=__work_path, type=str, help='Working directory for reference repo') + args = parser.parse_args() + + print(args) + + if not lib.check_requirements(): + print("Error: Check requirements") + sys.exit(1) + + work_path = os.path.abspath(args.work_path) + if not os.path.exists(work_path): + os.makedirs(work_path) + + lib.set_jobs('-j' + str(args.j)) + result_file = os.path.join(work_path, args.o) + (f, ext) = os.path.splitext(result_file) + timing_file = f + '_timing' + ext + normal_results = f + '_normal' + ext + exhaustive_results = f + '_exhaustive' + ext + + if os.path.exists(result_file): + os.remove(result_file) + if os.path.exists(timing_file): + os.remove(timing_file) + if os.path.exists(normal_results): + os.remove(normal_results) + if os.path.exists(exhaustive_results): + os.remove(exhaustive_results) + + cppcheck_path = args.cppcheck_path + + if cppcheck_path is None: + cppcheck_path = os.path.join(work_path, 'cppcheck') + try: + lib.clone_cppcheck(cppcheck_path, '') + pass + except Exception as e: + print('Failed to clone Cppcheck repository ({}), retry later'.format(e)) + sys.exit(1) + + if not lib.compile_cppcheck(cppcheck_path): + print('Failed to compile Cppcheck') + sys.exit(1) + + if args.packages_path: + # You can download packages using daca2-download.py + args.packages = glob.glob(os.path.join(args.packages_path, '*.tar.xz')) + args.p = len(args.packages) + packages_idxs = list(range(args.p)) + random.shuffle(packages_idxs) + elif args.packages: + args.p = len(args.packages) + packages_idxs = [] + else: + packages_count = lib.get_packages_count() + if not packages_count: + print("network or server might be temporarily down..") + sys.exit(1) + + packages_idxs = list(range(packages_count)) + random.shuffle(packages_idxs) + + packages_processed = 0 + crashes = [] + timeouts = [] + + while (packages_processed < args.p and len(packages_idxs) > 0) or args.packages: + if args.packages: + package = args.packages.pop() + else: + package = lib.get_package(packages_idxs.pop()) + + if package.startswith('ftp://') or package.startswith('http://'): + tgz = lib.download_package(work_path, package, None) + if tgz is None: + print("No package downloaded") + continue + else: + print('Package: ' + package) + tgz = package + + source_path, source_found = lib.unpack_package(work_path, tgz, c_only=args.c_only, cpp_only=args.cpp_only) + if not source_found: + print("No files to process") + continue + + results_to_diff = list() + + normal_crashed = False + exhaustive_crashed = False + + normal_timeout = False + exhaustive_timeout = False + + enable = 'style' + debug_warnings = False + + libraries = lib.library_includes.get_libraries(source_path) + c, errout, info, time_normal, cppcheck_options, timing_info = lib.scan_package(cppcheck_path, source_path, libraries, enable=enable, debug_warnings=debug_warnings, check_level='normal') + if c < 0: + if c == -101 and 'error: could not find or open any of the paths given.' in errout: + # No sourcefile found (for example only headers present) + print('Error: 101') + elif c == lib.RETURN_CODE_TIMEOUT: + print('Normal check level timed out!') + normal_timeout = True + continue # we don't want to compare timeouts + else: + print('Normal check level crashed!') + normal_crashed = True + results_to_diff.append(errout) + + c, errout, info, time_exhaustive, cppcheck_options, timing_info = lib.scan_package(cppcheck_path, source_path, libraries, enable=enable, debug_warnings=debug_warnings, check_level='exhaustive') + if c < 0: + if c == -101 and 'error: could not find or open any of the paths given.' in errout: + # No sourcefile found (for example only headers present) + print('Error: 101') + elif c == lib.RETURN_CODE_TIMEOUT: + print('Exhaustive check level timed out!') + exhaustive_timeout = True + continue # we don't want to compare timeouts + else: + print('Exhaustive check level crashed!') + exhaustive_crashed = True + results_to_diff.append(errout) + + if normal_crashed or exhaustive_crashed: + who = None + if normal_crashed and exhaustive_crashed: + who = 'Both' + elif normal_crashed: + who = 'Normal' + else: + who = 'Exhaustive' + crashes.append(package + ' ' + who) + + if normal_timeout or exhaustive_timeout: + who = None + if normal_timeout and exhaustive_timeout: + who = 'Both' + elif normal_timeout: + who = 'Normal' + else: + who = 'Exhaustive' + timeouts.append(package + ' ' + who) + + with open(result_file, 'a') as myfile: + myfile.write(package + '\n') + diff = lib.diff_results('normal', results_to_diff[0], 'exhaustive', results_to_diff[1]) + if not normal_crashed and not exhaustive_crashed and diff != '': + myfile.write('libraries:' + ','.join(libraries) +'\n') + myfile.write('diff:\n' + diff + '\n') + + if not normal_crashed and not exhaustive_crashed: + with open(timing_file, 'a') as myfile: + package_width = '140' + timing_width = '>7' + myfile.write('{:{package_width}} {:{timing_width}} {:{timing_width}} {:{timing_width}}\n'.format( + package, format_float(time_normal), + format_float(time_exhaustive), format_float(time_normal, time_exhaustive), + package_width=package_width, timing_width=timing_width)) + with open(normal_results, 'a') as myfile: + myfile.write(results_to_diff[0]) + with open(exhaustive_results, 'a') as myfile: + myfile.write(results_to_diff[1]) + + packages_processed += 1 + print(str(packages_processed) + ' of ' + str(args.p) + ' packages processed\n') + + with open(result_file, 'a') as myfile: + myfile.write('\n\ncrashes\n') + myfile.write('\n'.join(crashes)) + + with open(result_file, 'a') as myfile: + myfile.write('\n\ntimeouts\n') + myfile.write('\n'.join(timeouts) + '\n') + + print('Result saved to: ' + result_file) diff --git a/tools/donate_cpu_lib.py b/tools/donate_cpu_lib.py index f09db15d242..adf020757ac 100644 --- a/tools/donate_cpu_lib.py +++ b/tools/donate_cpu_lib.py @@ -16,7 +16,7 @@ # Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/ # Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic # changes) -CLIENT_VERSION = "1.3.53" +CLIENT_VERSION = "1.3.54" # Timeout for analysis with Cppcheck in seconds CPPCHECK_TIMEOUT = 30 * 60 @@ -431,7 +431,7 @@ def __run_command(cmd, print_cmd=True): return return_code, stdout, stderr, elapsed_time -def scan_package(cppcheck_path, source_path, libraries, capture_callstack=True): +def scan_package(cppcheck_path, source_path, libraries, capture_callstack=True, enable='style,information', debug_warnings=True, check_level=None): print('Analyze..') libs = '' for library in libraries: @@ -441,10 +441,16 @@ def scan_package(cppcheck_path, source_path, libraries, capture_callstack=True): dir_to_scan = source_path # TODO: temporarily disabled timing information - use --showtime=top5_summary when next version is released - # TODO: remove missingInclude disabling when it no longer is implied by --enable=information # Reference for GNU C: https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html - options = libs + ' --check-library --inconclusive --enable=style,information --inline-suppr --disable=missingInclude --suppress=unmatchedSuppression --template=daca2' - options += ' --debug-warnings --suppress=autoNoType --suppress=valueFlowBailout --suppress=bailoutUninitVar --suppress=symbolDatabaseWarning' + options = '{} --inconclusive --enable={} --inline-suppr --template=daca2'.format(libs, enable) + if 'information' in enable: + # TODO: remove missingInclude disabling when it no longer is implied by --enable=information + options += ' --disable=missingInclude --suppress=unmatchedSuppression' + if check_level: + options += ' --check-level=' + check_level + if debug_warnings: + options += ' --check-library --debug-warnings --suppress=autoNoType --suppress=valueFlowBailout' \ + ' --suppress=bailoutUninitVar --suppress=symbolDatabaseWarning' options += ' -D__GNUC__ --platform=unix64' options_rp = options + ' -rp={}'.format(dir_to_scan) if __make_cmd == 'msbuild.exe': From c21166565f2f005c9e588dec1849b01cdec0310d Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Tue, 9 Jan 2024 12:28:22 +0100 Subject: [PATCH 03/12] Fix #12322 detect usage of partially-initialized object in constructor (#5851) --- lib/checkclass.cpp | 49 +++++++++++++++++++++++++++++-------------- lib/checkclass.h | 2 +- test/testclass.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 17 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index ec3a9521b10..5934dcd8556 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -2633,6 +2633,7 @@ namespace { // avoid one-definition-rule violation const Variable *var; const Token *tok; + std::vector initArgs; }; } @@ -2663,25 +2664,38 @@ void CheckClass::initializerListOrder() tok = tok->next(); // find all variable initializations in list - while (tok && tok != func->functionScope->bodyStart) { + for (; tok && tok != func->functionScope->bodyStart; tok = tok->next()) { if (Token::Match(tok, "%name% (|{")) { const Variable *var = scope->getVariable(tok->str()); if (var) vars.emplace_back(var, tok); + else + continue; - if (Token::Match(tok->tokAt(2), "%name% =")) { - var = scope->getVariable(tok->strAt(2)); - - if (var) - vars.emplace_back(var, tok->tokAt(2)); + const Token* const end = tok->next()->link(); + for (; tok != end; tok = tok->next()) { + if (const Variable* argVar = scope->getVariable(tok->str())) { + if (var->isPointer() && (argVar->isArray() || Token::simpleMatch(tok->astParent(), "&"))) + continue; + if (var->isReference()) + continue; + if (Token::simpleMatch(tok->astParent(), "=")) + continue; + vars.back().initArgs.emplace_back(argVar); + } } - tok = tok->next()->link()->next(); - } else - tok = tok->next(); + } } - // need at least 2 members to have out of order initialization - for (int j = 1; j < vars.size(); j++) { + for (int j = 0; j < vars.size(); j++) { + // check for use of uninitialized arguments + for (const auto& arg : vars[j].initArgs) + if (vars[j].var->index() < arg->index()) + initializerListError(vars[j].tok, vars[j].var->nameToken(), scope->className, vars[j].var->name(), /*isArgument*/ true); + + // need at least 2 members to have out of order initialization + if (j == 0) + continue; // check for out of order initialization if (vars[j].var->index() < vars[j - 1].var->index()) initializerListError(vars[j].tok,vars[j].var->nameToken(), scope->className, vars[j].var->name()); @@ -2692,15 +2706,18 @@ void CheckClass::initializerListOrder() } } -void CheckClass::initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname) +void CheckClass::initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname, bool isArgument) { std::list toks = { tok1, tok2 }; + const std::string msg = isArgument ? + "Member variable '$symbol' uses an uninitialized argument due to the order of declarations." : + "Member variable '$symbol' is in the wrong place in the initializer list."; reportError(toks, Severity::style, "initializerList", - "$symbol:" + classname + "::" + varname +"\n" - "Member variable '$symbol' is in the wrong place in the initializer list.\n" - "Member variable '$symbol' is in the wrong place in the initializer list. " + "$symbol:" + classname + "::" + varname + '\n' + + msg + '\n' + + msg + ' ' + "Members are initialized in the order they are declared, not in the " - "order they are in the initializer list. Keeping the initializer list " + "order they are in the initializer list. Keeping the initializer list " "in the same order that the members were declared prevents order dependent " "initialization errors.", CWE398, Certainty::inconclusive); } diff --git a/lib/checkclass.h b/lib/checkclass.h index bac9f6dd1d5..79258491b90 100644 --- a/lib/checkclass.h +++ b/lib/checkclass.h @@ -199,7 +199,7 @@ class CPPCHECKLIB CheckClass : public Check { void operatorEqToSelfError(const Token *tok); void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic); void checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic); - void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname); + void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname, bool isArgument = false); void suggestInitializationList(const Token *tok, const std::string& varname); void selfInitializationError(const Token* tok, const std::string& varname); void pureVirtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &purefuncname); diff --git a/test/testclass.cpp b/test/testclass.cpp index 52818894722..f6dddcdce36 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -215,6 +215,7 @@ class TestClass : public TestFixture { TEST_CASE(qualifiedNameMember); // #10872 TEST_CASE(initializerListOrder); + TEST_CASE(initializerListArgument); TEST_CASE(initializerListUsage); TEST_CASE(selfInitialization); @@ -7571,6 +7572,57 @@ class TestClass : public TestFixture { "};"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::b' is in the wrong place in the initializer list.\n" "[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::a' is in the wrong place in the initializer list.\n", errout.str()); + + checkInitializerListOrder("struct S {\n" + " S() : b(a = 1) {}\n" + " int a, b;\n" + "};"); + ASSERT_EQUALS("", errout.str()); + } + + void initializerListArgument() { + checkInitializerListOrder("struct A { A(); };\n" // #12322 + "struct B { explicit B(const A* a); };\n" + "struct C {\n" + " C() : b(&a) {}\n" + " B b;\n" + " const A a;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (style, inconclusive) Member variable 'C::b' uses an uninitialized argument due to the order of declarations.\n", + errout.str()); + + checkInitializerListOrder("struct S {\n" + " S(const std::string& f, std::string i, int b, int c) : a(0), b(b), c(c) {}\n" + " int a, b, c;\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkInitializerListOrder("struct S {\n" + " S() : p(a) {}\n" + " int* p;\n" + " int a[1];\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkInitializerListOrder("struct S {\n" + " S() : p(&i) {}\n" + " int* p;\n" + " int i;\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkInitializerListOrder("struct S {\n" + " S() : a(b = 1) {}\n" + " int a, b;\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkInitializerListOrder("struct S {\n" + " S() : r(i) {}\n" + " int& r;\n" + " int i{};\n" + "};"); + ASSERT_EQUALS("", errout.str()); } #define checkInitializationListUsage(code) checkInitializationListUsage_(code, __FILE__, __LINE__) From 9fceba4fbc97c0aaf9da3ca709aca1686b61bb48 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Tue, 9 Jan 2024 14:52:32 +0100 Subject: [PATCH 04/12] Fix FP initializerList with base member (#5862) --- lib/checkclass.cpp | 2 ++ test/testclass.cpp | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 5934dcd8556..f1a87685b4c 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -2675,6 +2675,8 @@ void CheckClass::initializerListOrder() const Token* const end = tok->next()->link(); for (; tok != end; tok = tok->next()) { if (const Variable* argVar = scope->getVariable(tok->str())) { + if (scope != argVar->scope()) + continue; if (var->isPointer() && (argVar->isArray() || Token::simpleMatch(tok->astParent(), "&"))) continue; if (var->isReference()) diff --git a/test/testclass.cpp b/test/testclass.cpp index f6dddcdce36..abb77819b5b 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -7623,6 +7623,15 @@ class TestClass : public TestFixture { " int i{};\n" "};"); ASSERT_EQUALS("", errout.str()); + + checkInitializerListOrder("struct B {\n" + " int a{}, b{};\n" + "};\n" + "struct D : B {\n" + " D() : B(), j(b) {}\n" + " int j;\n" + "};"); + ASSERT_EQUALS("", errout.str()); } #define checkInitializationListUsage(code) checkInitializationListUsage_(code, __FILE__, __LINE__) From 30397f0d539c3192e1ad6fc5593f2d2fbdf38879 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Tue, 9 Jan 2024 19:42:23 +0100 Subject: [PATCH 05/12] Fix FP initializerList with static member (#5864) --- lib/checkclass.cpp | 4 ++++ test/testclass.cpp | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index f1a87685b4c..2b80383d54a 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -2677,6 +2677,10 @@ void CheckClass::initializerListOrder() if (const Variable* argVar = scope->getVariable(tok->str())) { if (scope != argVar->scope()) continue; + if (argVar->isStatic()) + continue; + if (tok->variable() && tok->variable()->isArgument()) + continue; if (var->isPointer() && (argVar->isArray() || Token::simpleMatch(tok->astParent(), "&"))) continue; if (var->isReference()) diff --git a/test/testclass.cpp b/test/testclass.cpp index abb77819b5b..785b6fcf005 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -7632,6 +7632,20 @@ class TestClass : public TestFixture { " int j;\n" "};"); ASSERT_EQUALS("", errout.str()); + + checkInitializerListOrder("struct S {\n" + " S() : a(i) {}\n" + " int a;\n" + " static int i;\n" + "};\n" + "int S::i = 0;"); + ASSERT_EQUALS("", errout.str()); + + checkInitializerListOrder("struct S {\n" + " S(int b) : a(b) {}\n" + " int a, b{};\n" + "};"); + ASSERT_EQUALS("", errout.str()); } #define checkInitializationListUsage(code) checkInitializationListUsage_(code, __FILE__, __LINE__) From 5ef846afed3fb8268cbdafec6b586bc1335f22e2 Mon Sep 17 00:00:00 2001 From: olabetskyi <153490942+olabetskyi@users.noreply.github.com> Date: Wed, 10 Jan 2024 10:10:31 +0200 Subject: [PATCH 06/12] Add maybe_unused to dumpFile (#5863) Co-authored-by: Oleksandr Labetskyi --- lib/tokenize.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 15c747b7fe0..03b31da4bcc 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -5971,6 +5971,10 @@ void Tokenizer::dump(std::ostream &out) const outs += " isAtomic=\"true\""; if (tok->isAttributeExport()) outs += " isAttributeExport=\"true\""; + if (tok->isAttributeMaybeUnused()) + outs += " isAttributeMaybeUnused=\"true\""; + if (tok->isAttributeUnused()) + outs += " isAttributeUnused=\"true\""; if (tok->link()) { outs += " link=\""; outs += id_string(tok->link()); From d008dddf0dcc46b92531feaadc762bae687700eb Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Wed, 10 Jan 2024 12:26:35 +0100 Subject: [PATCH 07/12] Fix FP initializerList for constructor args (#5865) --- lib/checkclass.cpp | 4 ++-- test/testclass.cpp | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 2b80383d54a..4118d31a896 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -2666,13 +2666,13 @@ void CheckClass::initializerListOrder() // find all variable initializations in list for (; tok && tok != func->functionScope->bodyStart; tok = tok->next()) { if (Token::Match(tok, "%name% (|{")) { + const Token* const end = tok->linkAt(1); const Variable *var = scope->getVariable(tok->str()); if (var) vars.emplace_back(var, tok); else - continue; + tok = end; - const Token* const end = tok->next()->link(); for (; tok != end; tok = tok->next()) { if (const Variable* argVar = scope->getVariable(tok->str())) { if (scope != argVar->scope()) diff --git a/test/testclass.cpp b/test/testclass.cpp index 785b6fcf005..317d8956b77 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -7578,6 +7578,29 @@ class TestClass : public TestFixture { " int a, b;\n" "};"); ASSERT_EQUALS("", errout.str()); + + checkInitializerListOrder("struct S {\n" + " int nCols() const;\n" + " int nRows() const;\n" + "};\n" + "struct B {\n" + " const char* m_name;\n" + " int nCols;\n" + " int nRows;\n" + " B(const char* p_name, int nR, int nC)\n" + " : m_name(p_name)\n" + " , nCols(nC)\n" + " , nRows(nR)\n" + " {}\n" + "};\n" + "struct D : public B {\n" + " const int m_i;\n" + " D(const S& s, int _i)\n" + " : B(\"abc\", s.nRows(), s.nCols())\n" + " , m_i(_i)\n" + " {}\n" + "};"); + ASSERT_EQUALS("", errout.str()); } void initializerListArgument() { From e2a2c5651c1b44c83c7fdf0f6b1ad23af1cab3aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Wed, 10 Jan 2024 14:04:57 +0100 Subject: [PATCH 08/12] TestSuppressions: added missing asserts (#5866) --- test/testsuppressions.cpp | 44 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index 85432331a02..b65616177fb 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -195,7 +195,7 @@ class TestSuppressions : public TestFixture { void suppressionsFileNameWithExtraPath() const { // Ticket #2797 Suppressions suppressions; - suppressions.addSuppressionLine("errorid:./a.c:123"); + ASSERT_EQUALS("", suppressions.addSuppressionLine("errorid:./a.c:123")); ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "a.c", 123))); ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "x/../a.c", 123))); } @@ -265,7 +265,7 @@ class TestSuppressions : public TestFixture { SingleExecutor executor(cppCheck, filelist, fileSettings, settings, settings.nomsg, *this); const unsigned int exitCode = executor.check(); - CppCheckExecutor::reportSuppressions(settings, settings.nomsg, false, filelist, fileSettings, *this); + CppCheckExecutor::reportSuppressions(settings, settings.nomsg, false, filelist, fileSettings, *this); // TODO: check result return exitCode; } @@ -312,7 +312,7 @@ class TestSuppressions : public TestFixture { ThreadExecutor executor(filelist, fileSettings, settings, settings.nomsg, *this, CppCheckExecutor::executeCommand); const unsigned int exitCode = executor.check(); - CppCheckExecutor::reportSuppressions(settings, settings.nomsg, false, filelist, fileSettings, *this); + CppCheckExecutor::reportSuppressions(settings, settings.nomsg, false, filelist, fileSettings, *this); // TODO: check result return exitCode; } @@ -360,7 +360,7 @@ class TestSuppressions : public TestFixture { ProcessExecutor executor(filelist, fileSettings, settings, settings.nomsg, *this, CppCheckExecutor::executeCommand); const unsigned int exitCode = executor.check(); - CppCheckExecutor::reportSuppressions(settings, settings.nomsg, false, filelist, fileSettings, *this); + CppCheckExecutor::reportSuppressions(settings, settings.nomsg, false, filelist, fileSettings, *this); // TODO: check result return exitCode; } @@ -941,7 +941,7 @@ class TestSuppressions : public TestFixture { void suppressionsLine0() const { Suppressions suppressions; - suppressions.addSuppressionLine("syntaxError:*:0"); + ASSERT_EQUALS("", suppressions.addSuppressionLine("syntaxError:*:0")); ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("syntaxError", "test.cpp", 0))); } @@ -949,33 +949,33 @@ class TestSuppressions : public TestFixture { std::istringstream file1("# comment\n" "abc"); Suppressions suppressions1; - suppressions1.parseFile(file1); + ASSERT_EQUALS("", suppressions1.parseFile(file1)); ASSERT_EQUALS(true, suppressions1.isSuppressed(errorMessage("abc", "test.cpp", 123))); std::istringstream file2("// comment\n" "abc"); Suppressions suppressions2; - suppressions2.parseFile(file2); + ASSERT_EQUALS("", suppressions2.parseFile(file2)); ASSERT_EQUALS(true, suppressions2.isSuppressed(errorMessage("abc", "test.cpp", 123))); std::istringstream file3("abc // comment"); Suppressions suppressions3; - suppressions3.parseFile(file3); + ASSERT_EQUALS("", suppressions3.parseFile(file3)); ASSERT_EQUALS(true, suppressions3.isSuppressed(errorMessage("abc", "test.cpp", 123))); std::istringstream file4("abc\t\t # comment"); Suppressions suppressions4; - suppressions4.parseFile(file4); + ASSERT_EQUALS("", suppressions4.parseFile(file4)); ASSERT_EQUALS(true, suppressions4.isSuppressed(errorMessage("abc", "test.cpp", 123))); std::istringstream file5("abc:test.cpp\t\t # comment"); Suppressions suppressions5; - suppressions5.parseFile(file5); + ASSERT_EQUALS("", suppressions5.parseFile(file5)); ASSERT_EQUALS(true, suppressions5.isSuppressed(errorMessage("abc", "test.cpp", 123))); std::istringstream file6("abc:test.cpp:123\t\t # comment with . inside"); Suppressions suppressions6; - suppressions6.parseFile(file6); + ASSERT_EQUALS("", suppressions6.parseFile(file6)); ASSERT_EQUALS(true, suppressions6.isSuppressed(errorMessage("abc", "test.cpp", 123))); } @@ -1193,7 +1193,7 @@ class TestSuppressions : public TestFixture { CppCheck cppCheck(*this, false, nullptr); // <- do not "use global suppressions". pretend this is a thread that just checks a file. Settings& settings = cppCheck.settings(); settings.quiet = true; - settings.nomsg.addSuppressionLine("uninitvar"); + ASSERT_EQUALS("", settings.nomsg.addSuppressionLine("uninitvar")); settings.exitCode = 1; const char code[] = "int f() { int a; return a; }"; @@ -1205,7 +1205,7 @@ class TestSuppressions : public TestFixture { Suppressions suppressions; Suppressions::Suppression suppression("unusedFunction", "test.c", 3); suppression.checked = true; // have to do this because fixes for #5704 - suppressions.addSuppression(std::move(suppression)); + ASSERT_EQUALS("", suppressions.addSuppression(std::move(suppression))); ASSERT_EQUALS(true, !suppressions.getUnmatchedLocalSuppressions("test.c", true).empty()); ASSERT_EQUALS(false, !suppressions.getUnmatchedGlobalSuppressions(true).empty()); ASSERT_EQUALS(false, !suppressions.getUnmatchedLocalSuppressions("test.c", false).empty()); @@ -1214,7 +1214,7 @@ class TestSuppressions : public TestFixture { void globalsuppress_unusedFunction() const { // #4946 - wrong report of "unmatchedSuppression" for "unusedFunction" Suppressions suppressions; - suppressions.addSuppressionLine("unusedFunction:*"); + ASSERT_EQUALS("", suppressions.addSuppressionLine("unusedFunction:*")); ASSERT_EQUALS(false, !suppressions.getUnmatchedLocalSuppressions("test.c", true).empty()); ASSERT_EQUALS(true, !suppressions.getUnmatchedGlobalSuppressions(true).empty()); ASSERT_EQUALS(false, !suppressions.getUnmatchedLocalSuppressions("test.c", false).empty()); @@ -1240,7 +1240,7 @@ class TestSuppressions : public TestFixture { " // cppcheck-suppress unusedStructMember\n" " int y;\n" "};"; - cppCheck.check("/somewhere/test.cpp", code); + ASSERT_EQUALS(0, cppCheck.check("/somewhere/test.cpp", code)); ASSERT_EQUALS("",errout.str()); } @@ -1412,7 +1412,7 @@ class TestSuppressions : public TestFixture { // No unmatched suppression errout.str(""); suppressions.clear(); - Suppressions::reportUnmatchedSuppressions(suppressions, *this); + ASSERT_EQUALS(false, Suppressions::reportUnmatchedSuppressions(suppressions, *this)); ASSERT_EQUALS("", errout.str()); // suppress all unmatchedSuppression @@ -1420,7 +1420,7 @@ class TestSuppressions : public TestFixture { suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "*", Suppressions::Suppression::NO_LINE); - Suppressions::reportUnmatchedSuppressions(suppressions, *this); + ASSERT_EQUALS(false, Suppressions::reportUnmatchedSuppressions(suppressions, *this)); ASSERT_EQUALS("", errout.str()); // suppress all unmatchedSuppression (corresponds to "--suppress=unmatchedSuppression") @@ -1428,7 +1428,7 @@ class TestSuppressions : public TestFixture { suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "", Suppressions::Suppression::NO_LINE); - Suppressions::reportUnmatchedSuppressions(suppressions, *this); + ASSERT_EQUALS(false, Suppressions::reportUnmatchedSuppressions(suppressions, *this)); ASSERT_EQUALS("", errout.str()); // suppress all unmatchedSuppression in a.c @@ -1436,7 +1436,7 @@ class TestSuppressions : public TestFixture { suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "a.c", Suppressions::Suppression::NO_LINE); - Suppressions::reportUnmatchedSuppressions(suppressions, *this); + ASSERT_EQUALS(false, Suppressions::reportUnmatchedSuppressions(suppressions, *this)); ASSERT_EQUALS("", errout.str()); // suppress unmatchedSuppression in a.c at line 10 @@ -1444,7 +1444,7 @@ class TestSuppressions : public TestFixture { suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "a.c", 10U); - Suppressions::reportUnmatchedSuppressions(suppressions, *this); + ASSERT_EQUALS(false, Suppressions::reportUnmatchedSuppressions(suppressions, *this)); ASSERT_EQUALS("", errout.str()); // don't suppress unmatchedSuppression when file is mismatching @@ -1452,7 +1452,7 @@ class TestSuppressions : public TestFixture { suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "b.c", Suppressions::Suppression::NO_LINE); - Suppressions::reportUnmatchedSuppressions(suppressions, *this); + ASSERT_EQUALS(true, Suppressions::reportUnmatchedSuppressions(suppressions, *this)); ASSERT_EQUALS("[a.c:10]: (information) Unmatched suppression: abc\n", errout.str()); // don't suppress unmatchedSuppression when line is mismatching @@ -1460,7 +1460,7 @@ class TestSuppressions : public TestFixture { suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "a.c", 1U); - Suppressions::reportUnmatchedSuppressions(suppressions, *this); + ASSERT_EQUALS(true, Suppressions::reportUnmatchedSuppressions(suppressions, *this)); ASSERT_EQUALS("[a.c:10]: (information) Unmatched suppression: abc\n", errout.str()); } }; From e77da43644bf21f73d22fc1d5b249cb2cd8382da Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Wed, 10 Jan 2024 15:41:01 +0100 Subject: [PATCH 09/12] initializerList: argument name in error message (#5867) --- lib/checkclass.cpp | 10 +++++----- lib/checkclass.h | 2 +- test/testclass.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 4118d31a896..fffb467d585 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -2697,7 +2697,7 @@ void CheckClass::initializerListOrder() // check for use of uninitialized arguments for (const auto& arg : vars[j].initArgs) if (vars[j].var->index() < arg->index()) - initializerListError(vars[j].tok, vars[j].var->nameToken(), scope->className, vars[j].var->name(), /*isArgument*/ true); + initializerListError(vars[j].tok, vars[j].var->nameToken(), scope->className, vars[j].var->name(), arg->name()); // need at least 2 members to have out of order initialization if (j == 0) @@ -2712,12 +2712,12 @@ void CheckClass::initializerListOrder() } } -void CheckClass::initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname, bool isArgument) +void CheckClass::initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname, const std::string& argname) { std::list toks = { tok1, tok2 }; - const std::string msg = isArgument ? - "Member variable '$symbol' uses an uninitialized argument due to the order of declarations." : - "Member variable '$symbol' is in the wrong place in the initializer list."; + const std::string msg = argname.empty() ? + "Member variable '$symbol' is in the wrong place in the initializer list." : + "Member variable '$symbol' uses an uninitialized argument '" + argname + "' due to the order of declarations."; reportError(toks, Severity::style, "initializerList", "$symbol:" + classname + "::" + varname + '\n' + msg + '\n' + diff --git a/lib/checkclass.h b/lib/checkclass.h index 79258491b90..3026fb636b7 100644 --- a/lib/checkclass.h +++ b/lib/checkclass.h @@ -199,7 +199,7 @@ class CPPCHECKLIB CheckClass : public Check { void operatorEqToSelfError(const Token *tok); void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic); void checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic); - void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname, bool isArgument = false); + void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname, const std::string& argname = {}); void suggestInitializationList(const Token *tok, const std::string& varname); void selfInitializationError(const Token* tok, const std::string& varname); void pureVirtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &purefuncname); diff --git a/test/testclass.cpp b/test/testclass.cpp index 317d8956b77..fc1075afc92 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -7611,7 +7611,7 @@ class TestClass : public TestFixture { " B b;\n" " const A a;\n" "};"); - ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (style, inconclusive) Member variable 'C::b' uses an uninitialized argument due to the order of declarations.\n", + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (style, inconclusive) Member variable 'C::b' uses an uninitialized argument 'a' due to the order of declarations.\n", errout.str()); checkInitializerListOrder("struct S {\n" From 2f606e96c3a11ee1c867a54a9a1b6407e8328296 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 11 Jan 2024 20:40:03 +0100 Subject: [PATCH 10/12] Improve support for std::priority_queue, std::tie (#5871) --- cfg/std.cfg | 23 +++++++++++++++++------ test/cfg/std.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/cfg/std.cfg b/cfg/std.cfg index b32b4cae74a..753cd006ae1 100644 --- a/cfg/std.cfg +++ b/cfg/std.cfg @@ -6642,7 +6642,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + false @@ -6678,7 +6678,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + false @@ -6811,10 +6811,10 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + false - + false @@ -8587,6 +8587,17 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init false + + false + + + + + + false + + + malloc calloc @@ -8675,7 +8686,7 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init - + @@ -8684,7 +8695,7 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init - + diff --git a/test/cfg/std.cpp b/test/cfg/std.cpp index 28279b77a6f..1d2c17e8de6 100644 --- a/test/cfg/std.cpp +++ b/test/cfg/std.cpp @@ -25,6 +25,7 @@ #define __STDC_WANT_LIB_EXT1__ 1 #include #include +#include #include #include #ifndef __STDC_NO_THREADS__ @@ -38,7 +39,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -899,6 +903,34 @@ int std_map_find_constref(std::map& m) // #11857 return ++*p; } +void std_queue_front_ignoredReturnValue(const std::queue& q) { + // cppcheck-suppress ignoredReturnValue + q.front(); +} + +void std_priority_queue_top_ignoredReturnValue(const std::priority_queue& pq) { + // cppcheck-suppress ignoredReturnValue + pq.top(); +} + +void std_tie_ignoredReturnValue(int a, int b) +{ + std::set s; + std::set::iterator it; + bool success; + std::tie(it, success) = s.insert(1); + // cppcheck-suppress ignoredReturnValue + std::tie(); + // cppcheck-suppress ignoredReturnValue + std::tie(a, b); +} + +void std_exception_ignoredReturnValue(const std::exception& e) +{ + // cppcheck-suppress ignoredReturnValue + e.what(); +} + void valid_code() { std::vector vecInt{0, 1, 2}; From 3fb85f96dac7c26aa7983bdd4d99656cc879a12e Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Fri, 12 Jan 2024 10:46:10 +0100 Subject: [PATCH 11/12] Fix #12335 FP checkLibraryFunction for incorrect scope (#5868) --- lib/library.cpp | 8 ++++++-- lib/tokenize.cpp | 16 ++++++++++++++-- test/cfg/qt.cpp | 10 ++++++++++ test/cfg/std.cpp | 6 ++++++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/lib/library.cpp b/lib/library.cpp index f1058c7883a..d815b3ac615 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -1010,8 +1010,12 @@ std::string Library::getFunctionName(const Token *ftok) const if (ftok->astParent()) { bool error = false; const Token * tok = ftok->astParent()->isUnaryOp("&") ? ftok->astParent()->astOperand1() : ftok->next()->astOperand1(); - const std::string ret = getFunctionName(tok, error); - return error ? std::string() : ret; + std::string ret = getFunctionName(tok, error); + if (error) + return {}; + if (startsWith(ret, "::")) + ret.erase(0, 2); + return ret; } // Lookup function name without using AST.. diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 03b31da4bcc..70aa0ee17e8 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -9647,6 +9647,18 @@ void Tokenizer::simplifyBitfields() } } +static bool isStdContainerOrIterator(const Token* tok, const Settings* settings) +{ + const Library::Container* ctr = settings->library.detectContainerOrIterator(tok, nullptr, /*withoutStd*/ true); + return ctr && startsWith(ctr->startPattern, "std ::"); +} + +static bool isStdSmartPointer(const Token* tok, const Settings* settings) +{ + const Library::SmartPointer* ptr = settings->library.detectSmartPointer(tok, /*withoutStd*/ true); + return ptr && startsWith(ptr->name, "std::"); +} + // Add std:: in front of std classes, when using namespace std; was given void Tokenizer::simplifyNamespaceStd() { @@ -9677,11 +9689,11 @@ void Tokenizer::simplifyNamespaceStd() if (userFunctions.find(tok->str()) == userFunctions.end() && mSettings->library.matchArguments(tok, "std::" + tok->str())) insert = true; } else if (Token::simpleMatch(tok->next(), "<") && - (mSettings->library.detectContainerOrIterator(tok, nullptr, /*withoutStd*/ true) || mSettings->library.detectSmartPointer(tok, /*withoutStd*/ true))) + (isStdContainerOrIterator(tok, mSettings) || isStdSmartPointer(tok, mSettings))) insert = true; else if (mSettings->library.hasAnyTypeCheck("std::" + tok->str()) || mSettings->library.podtype("std::" + tok->str()) || - mSettings->library.detectContainerOrIterator(tok, nullptr, /*withoutStd*/ true)) + isStdContainerOrIterator(tok, mSettings)) insert = true; if (insert) { diff --git a/test/cfg/qt.cpp b/test/cfg/qt.cpp index 2bef064a983..2c45ba8340c 100644 --- a/test/cfg/qt.cpp +++ b/test/cfg/qt.cpp @@ -71,6 +71,16 @@ bool QString7(QString s, const QString& l) { return l.startsWith(s); } +namespace NTestStd // #12355 +{ + using namespace std; + QString QString_std(QString s) + { + s.replace("abc", "def"); + return s; + } +} + void QByteArray1(QByteArray byteArrayArg) { for (int i = 0; i <= byteArrayArg.size(); ++i) { diff --git a/test/cfg/std.cpp b/test/cfg/std.cpp index 1d2c17e8de6..07f427e6fc1 100644 --- a/test/cfg/std.cpp +++ b/test/cfg/std.cpp @@ -4892,3 +4892,9 @@ void std_vector_data_arithmetic() buf.resize(1); memcpy(buf.data() + 0, "", 1); } + +std::string global_scope_std() // #12355 +{ + ::std::stringstream ss; + return ss.str(); +} \ No newline at end of file From 14e540a2dc6433ef2ba1c20b64a4d0d0b040966d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Fri, 12 Jan 2024 11:30:51 +0100 Subject: [PATCH 12/12] Fix #12341 (If --premium=safety is used then go to "safety mode". do not override this in cppcheck.cfg) (#5872) --- cli/cmdlineparser.cpp | 2 ++ lib/settings.cpp | 2 +- test/testcmdlineparser.cpp | 12 ++++++++++++ test/testsettings.cpp | 29 +++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 0cb85af412b..ba374586faa 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -890,6 +890,8 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a // Special Cppcheck Premium options else if (std::strncmp(argv[i], "--premium=", 10) == 0 && isCppcheckPremium()) { + if (std::strcmp(argv[i], "--premium=safety") == 0) + mSettings.safety = true; if (!mSettings.premiumArgs.empty()) mSettings.premiumArgs += " "; const std::string p(argv[i] + 10); diff --git a/lib/settings.cpp b/lib/settings.cpp index a58e75b2c21..393ce377732 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -125,7 +125,7 @@ std::string Settings::loadCppcheckCfg() const auto& v = it->second; if (!v.is()) return "'safety' is not a bool"; - safety = v.get(); + safety = safety || v.get(); } } diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index e5905cddef6..e0d0f3de65e 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -213,6 +213,7 @@ class TestCmdlineParser : public TestFixture { TEST_CASE(maxConfigsMissingCount); TEST_CASE(maxConfigsInvalid); TEST_CASE(maxConfigsTooSmall); + TEST_CASE(premiumSafety); TEST_CASE(reportProgress1); TEST_CASE(reportProgress2); TEST_CASE(reportProgress3); @@ -1186,6 +1187,17 @@ class TestCmdlineParser : public TestFixture { ASSERT_EQUALS("cppcheck: error: argument to '--max-configs=' must be greater than 0.\n", logger->str()); } + void premiumSafety() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--premium=safety", "file.cpp"}; + settings->safety = false; + settings->cppcheckCfgProductName = "Cppcheck Premium 0.0.0"; + ASSERT_EQUALS(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->safety); + settings->safety = false; + settings->cppcheckCfgProductName.clear(); + } + void reportProgress1() { REDIRECT; const char * const argv[] = {"cppcheck", "--report-progress", "file.cpp"}; diff --git a/test/testsettings.cpp b/test/testsettings.cpp index f09dfed1aa4..6c2b2de2d7c 100644 --- a/test/testsettings.cpp +++ b/test/testsettings.cpp @@ -30,6 +30,7 @@ class TestSettings : public TestFixture { void run() override { TEST_CASE(simpleEnableGroup); TEST_CASE(loadCppcheckCfg); + TEST_CASE(loadCppcheckCfgSafety); } void simpleEnableGroup() const { @@ -209,6 +210,34 @@ class TestSettings : public TestFixture { // TODO: test with FILESDIR } + + void loadCppcheckCfgSafety() const + { + // Test the "safety" flag + { + Settings s; + s.safety = false; + ScopedFile file("cppcheck.cfg", "{}"); + ASSERT_EQUALS("", s.loadCppcheckCfg()); + ASSERT_EQUALS(false, s.safety); + } + + { + Settings s; + s.safety = true; + ScopedFile file("cppcheck.cfg", "{\"safety\": false}"); + ASSERT_EQUALS("", s.loadCppcheckCfg()); + ASSERT_EQUALS(true, s.safety); + } + + { + Settings s; + s.safety = false; + ScopedFile file("cppcheck.cfg", "{\"safety\": true}"); + ASSERT_EQUALS("", s.loadCppcheckCfg()); + ASSERT_EQUALS(true, s.safety); + } + } }; REGISTER_TEST(TestSettings)