diff --git a/Makefile b/Makefile index d1aa9cc09..c1d8aac3a 100644 --- a/Makefile +++ b/Makefile @@ -618,7 +618,7 @@ $(libcppdir)/symboldatabase.o: lib/symboldatabase.cpp lib/astutils.h lib/color.h $(libcppdir)/templatesimplifier.o: lib/templatesimplifier.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/templatesimplifier.cpp -$(libcppdir)/timer.o: lib/timer.cpp lib/config.h lib/timer.h +$(libcppdir)/timer.o: lib/timer.cpp lib/config.h lib/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/timer.cpp $(libcppdir)/token.o: lib/token.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/tokenrange.h lib/utils.h lib/valueflow.h lib/vfvalue.h diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 54a2e8af7..a40ad42fd 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -1418,5 +1418,5 @@ void CmdLineParser::printHelp() bool CmdLineParser::isCppcheckPremium() const { if (mSettings.cppcheckCfgProductName.empty()) mSettings.loadCppcheckCfg(); - return mSettings.cppcheckCfgProductName.compare(0, 16, "Cppcheck Premium") == 0; + return startsWith(mSettings.cppcheckCfgProductName, "Cppcheck Premium"); } diff --git a/gui/checkthread.cpp b/gui/checkthread.cpp index 0a8fbf346..f59897897 100644 --- a/gui/checkthread.cpp +++ b/gui/checkthread.cpp @@ -27,6 +27,7 @@ #include "settings.h" #include "standards.h" #include "threadresult.h" +#include "utils.h" #include #include @@ -75,7 +76,7 @@ static bool executeCommand(std::string exe, std::vector args, std:: } else output = process.readAllStandardOutput().toStdString(); - if (redirect.compare(0,3,"2> ") == 0) { + if (startsWith(redirect, "2> ")) { std::ofstream fout(redirect.substr(3)); fout << process.readAllStandardError().toStdString(); } @@ -157,7 +158,7 @@ void CheckThread::runAddonsAndTools(const ImportProject::FileSettings *fileSetti if (!fileSettings) continue; - if (!fileSettings->cfg.empty() && fileSettings->cfg.compare(0,5,"Debug") != 0) + if (!fileSettings->cfg.empty() && !startsWith(fileSettings->cfg,"Debug")) continue; QStringList args; diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 82fcb2ecf..462e862b5 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -2407,7 +2407,7 @@ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Setti return false; // not a function => variable not changed if (Token::simpleMatch(tok, "{") && isTrivialConstructor(tok)) return false; - if (tok->isKeyword() && !isCPPCastKeyword(tok) && tok->str().compare(0,8,"operator") != 0) + if (tok->isKeyword() && !isCPPCastKeyword(tok) && !startsWith(tok->str(),"operator")) return false; // A functional cast won't modify the variable if (Token::Match(tok, "%type% (|{") && tok->tokType() == Token::eType && astIsPrimitive(tok->next())) diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index 5589760a2..b2c9d0872 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -32,6 +32,7 @@ #include "symboldatabase.h" #include "token.h" #include "tokenize.h" +#include "utils.h" #include "vfvalue.h" #include @@ -824,7 +825,7 @@ const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const t } else if (rhs->str() == "(" && !mSettings->library.returnValue(rhs->astOperand1()).empty()) { // #9298, assignment through return value of a function const std::string &returnValue = mSettings->library.returnValue(rhs->astOperand1()); - if (returnValue.compare(0, 3, "arg") == 0) { + if (startsWith(returnValue, "arg")) { int argn; const Token *func = getTokenArgumentFunction(tok, argn); if (func) { @@ -850,7 +851,7 @@ const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const t alloc.status = VarInfo::NOALLOC; functionCall(tok, openingPar, varInfo, alloc, nullptr); const std::string &returnValue = mSettings->library.returnValue(tok); - if (returnValue.compare(0, 3, "arg") == 0) + if (startsWith(returnValue, "arg")) // the function returns one of its argument, we need to process a potential assignment return openingPar; return isCPPCast(tok->astParent()) ? openingPar : openingPar->link(); diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index b853785eb..a8ab5b0f5 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -1273,7 +1273,7 @@ void CheckUnusedVar::checkFunctionVariableUsage() (!op1Var->valueType() || op1Var->valueType()->type == ValueType::Type::UNKNOWN_TYPE)) { // Check in the library if we should bailout or not.. std::string typeName = op1Var->getTypeName(); - if (typeName.compare(0, 2, "::") == 0) + if (startsWith(typeName, "::")) typeName.erase(typeName.begin(), typeName.begin() + 2); switch (mSettings->library.getTypeCheck("unusedvar", typeName)) { case Library::TypeCheck::def: diff --git a/lib/clangimport.cpp b/lib/clangimport.cpp index 2742eb5c9..0b7e9d4e9 100644 --- a/lib/clangimport.cpp +++ b/lib/clangimport.cpp @@ -412,9 +412,9 @@ std::string clangimport::AstNode::getSpelling() const return ""; } const std::string &str = mExtTokens[typeIndex - 1]; - if (str.compare(0,4,"col:") == 0) + if (startsWith(str,"col:")) return ""; - if (str.compare(0,8,"(ext.substr(5, ext.find_first_of(",>", 5) - 5)); - else if (ext.compare(0, 6, "(ext.substr(6, ext.find_first_of(":,>", 6) - 6)); const auto pos = ext.find(", col:"); if (pos != std::string::npos) @@ -542,7 +542,7 @@ const ::Type * clangimport::AstNode::addTypeTokens(TokenList *tokenList, const s return addTypeTokens(tokenList, str.substr(0, str.find("\':\'") + 1), scope); } - if (str.compare(0, 16, "'enum (anonymous") == 0) + if (startsWith(str, "'enum (anonymous")) return nullptr; std::string type; @@ -943,7 +943,7 @@ Token *clangimport::AstNode::createTokens(TokenList *tokenList) } if (nodeType == DeclRefExpr) { int addrIndex = mExtTokens.size() - 1; - while (addrIndex > 1 && mExtTokens[addrIndex].compare(0,2,"0x") != 0) + while (addrIndex > 1 && !startsWith(mExtTokens[addrIndex],"0x")) --addrIndex; const std::string addr = mExtTokens[addrIndex]; std::string name = unquote(getSpelling()); @@ -985,7 +985,7 @@ Token *clangimport::AstNode::createTokens(TokenList *tokenList) } if (nodeType == EnumDecl) { int colIndex = mExtTokens.size() - 1; - while (colIndex > 0 && mExtTokens[colIndex].compare(0,4,"col:") != 0 && mExtTokens[colIndex].compare(0,5,"line:") != 0) + while (colIndex > 0 && !startsWith(mExtTokens[colIndex],"col:") && !startsWith(mExtTokens[colIndex],"line:")) --colIndex; if (colIndex == 0) return nullptr; @@ -1131,10 +1131,10 @@ Token *clangimport::AstNode::createTokens(TokenList *tokenList) Token *s = getChild(0)->createTokens(tokenList); Token *dot = addtoken(tokenList, "."); std::string memberName = getSpelling(); - if (memberName.compare(0, 2, "->") == 0) { + if (startsWith(memberName, "->")) { dot->originalName("->"); memberName = memberName.substr(2); - } else if (memberName.compare(0, 1, ".") == 0) { + } else if (startsWith(memberName, ".")) { memberName = memberName.substr(1); } if (memberName.empty()) @@ -1150,7 +1150,7 @@ Token *clangimport::AstNode::createTokens(TokenList *tokenList) return nullptr; const Token *defToken = addtoken(tokenList, "namespace"); const std::string &s = mExtTokens[mExtTokens.size() - 2]; - const Token* nameToken = (s.compare(0, 4, "col:") == 0 || s.compare(0, 5, "line:") == 0) ? + const Token* nameToken = (startsWith(s, "col:") || startsWith(s, "line:")) ? addtoken(tokenList, mExtTokens.back()) : nullptr; Scope *scope = createScope(tokenList, Scope::ScopeType::eNamespace, children, defToken); if (nameToken) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 481617d34..0feb733bf 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -360,7 +360,7 @@ static std::string executeAddon(const AddonInfo &addonInfo, break; } #endif - if (executeCommand(py_exe, split("--version"), redirect, out) && out.compare(0, 7, "Python ") == 0 && std::isdigit(out[7])) { + if (executeCommand(py_exe, split("--version"), redirect, out) && startsWith(out, "Python ") && std::isdigit(out[7])) { pythonExe = py_exe; break; } @@ -393,7 +393,7 @@ static std::string executeAddon(const AddonInfo &addonInfo, std::istringstream istr(result); std::string line; while (std::getline(istr, line)) { - if (line.compare(0,9,"Checking ", 0, 9) != 0 && !line.empty() && line[0] != '{') { + if (!startsWith(line,"Checking ") && !line.empty() && line[0] != '{') { result.erase(result.find_last_not_of('\n') + 1, std::string::npos); // Remove trailing newlines throw InternalError(nullptr, "Failed to execute '" + pythonExe + " " + args + "'. " + result); } @@ -834,7 +834,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string std::string code; const std::list &directives = preprocessor.getDirectives(); for (const Directive &dir : directives) { - if (dir.str.compare(0,8,"#define ") == 0 || dir.str.compare(0,9,"#include ") == 0) + if (startsWith(dir.str,"#define ") || startsWith(dir.str,"#include ")) code += "#line " + std::to_string(dir.linenr) + " \"" + dir.file + "\"\n" + dir.str + '\n'; } Tokenizer tokenizer2(&mSettings, this); @@ -883,7 +883,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string std::string codeWithoutCfg = preprocessor.getcode(tokens1, mCurrentConfig, files, true); t.stop(); - if (codeWithoutCfg.compare(0,5,"#file") == 0) + if (startsWith(codeWithoutCfg,"#file")) codeWithoutCfg.insert(0U, "//"); std::string::size_type pos = 0; while ((pos = codeWithoutCfg.find("\n#file",pos)) != std::string::npos) @@ -1456,7 +1456,7 @@ void CppCheck::executeAddons(const std::vector& files) const bool misraC2023 = mSettings.premiumArgs.find("--misra-c-2023") != std::string::npos; while (std::getline(istr, line)) { - if (line.compare(0,1,"{") != 0) + if (!startsWith(line,"{")) continue; picojson::value res; @@ -1486,7 +1486,7 @@ void CppCheck::executeAddons(const std::vector& files) } errmsg.id = obj["addon"].get() + "-" + obj["errorId"].get(); - if (misraC2023 && errmsg.id.compare(0, 12, "misra-c2012-") == 0) + if (misraC2023 && startsWith(errmsg.id, "misra-c2012-")) errmsg.id = "misra-c2023-" + errmsg.id.substr(12); const std::string text = obj["message"].get(); errmsg.setmsg(text); diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index af322cb02..27f7765c9 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -135,7 +135,7 @@ ErrorMessage::ErrorMessage(const ErrorPath &errorPath, const TokenList *tokenLis std::string info = e.second; - if (info.compare(0,8,"$symbol:") == 0 && info.find('\n') < info.size()) { + if (startsWith(info,"$symbol:") && info.find('\n') < info.size()) { const std::string::size_type pos = info.find('\n'); const std::string &symbolName = info.substr(8, pos - 8); info = replaceStr(info.substr(pos+1), "$symbol", symbolName); @@ -215,7 +215,7 @@ void ErrorMessage::setmsg(const std::string &msg) if (pos == std::string::npos) { mShortMessage = replaceStr(msg, "$symbol", symbolName); mVerboseMessage = replaceStr(msg, "$symbol", symbolName); - } else if (msg.compare(0,8,"$symbol:") == 0) { + } else if (startsWith(msg,"$symbol:")) { mSymbolNames += msg.substr(8, pos-7); setmsg(msg.substr(pos + 1)); } else { diff --git a/lib/importproject.cpp b/lib/importproject.cpp index a27b3a493..f7d20d132 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -156,7 +156,7 @@ void ImportProject::FileSettings::setIncludePaths(const std::string &basepath, c for (const std::string &ipath : copyIn) { if (ipath.empty()) continue; - if (ipath.compare(0,2,"%(")==0) + if (startsWith(ipath,"%(")) continue; std::string s(Path::fromNativeSeparators(ipath)); if (!found.insert(s).second) @@ -298,9 +298,9 @@ void ImportProject::FileSettings::parseCommand(const std::string& command) if (F=='D') { std::string defval = readUntil(command, &pos, " "); defs += fval; - if (defval.size() >= 3 && defval.compare(0,2,"=\"")==0 && defval.back()=='\"') + if (defval.size() >= 3 && startsWith(defval,"=\"") && defval.back()=='\"') defval = "=" + unescape(defval.substr(2, defval.size() - 3)); - else if (defval.size() >= 5 && defval.compare(0, 3, "=\\\"") == 0 && endsWith(defval, "\\\"")) + else if (defval.size() >= 5 && startsWith(defval, "=\\\"") && endsWith(defval, "\\\"")) defval = "=\"" + unescape(defval.substr(3, defval.size() - 5)) + "\""; if (!defval.empty()) defs += defval; @@ -313,32 +313,9 @@ void ImportProject::FileSettings::parseCommand(const std::string& command) i = unescape(i.substr(1, i.size() - 2)); if (std::find(includePaths.cbegin(), includePaths.cend(), i) == includePaths.cend()) includePaths.push_back(std::move(i)); - } else if (F=='s' && fval.compare(0,2,"td") == 0) { + } else if (F=='s' && startsWith(fval,"td")) { ++pos; - const std::string stdval = readUntil(command, &pos, " "); - standard = stdval; - // TODO: use simplecpp::DUI::std instead of specifying it manually - if (standard.compare(0, 3, "c++") || standard.compare(0, 5, "gnu++")) { - const std::string stddef = simplecpp::getCppStdString(standard); - if (stddef.empty()) { - // TODO: log error - continue; - } - - defs += "__cplusplus="; - defs += stddef; - defs += ";"; - } else if (standard.compare(0, 1, "c") || standard.compare(0, 3, "gnu")) { - const std::string stddef = simplecpp::getCStdString(standard); - if (stddef.empty()) { - // TODO: log error - continue; - } - - defs += "__STDC_VERSION__="; - defs += stddef; - defs += ";"; - } + standard = readUntil(command, &pos, " "); } else if (F == 'i' && fval == "system") { ++pos; std::string isystem = readUntil(command, &pos, " "); @@ -459,9 +436,9 @@ bool ImportProject::importSln(std::istream &istr, const std::string &path, const return false; } - if (line.find("Microsoft Visual Studio Solution File") != 0) { + if (!startsWith(line, "Microsoft Visual Studio Solution File")) { // Skip BOM - if (!std::getline(istr, line) || line.find("Microsoft Visual Studio Solution File") != 0) { + if (!std::getline(istr, line) || !startsWith(line, "Microsoft Visual Studio Solution File")) { printError("Visual Studio solution file header not found"); return false; } @@ -473,7 +450,7 @@ bool ImportProject::importSln(std::istream &istr, const std::string &path, const bool found = false; while (std::getline(istr,line)) { - if (line.compare(0,8,"Project(")!=0) + if (!startsWith(line,"Project(")) continue; const std::string::size_type pos = line.find(".vcxproj"); if (pos == std::string::npos) @@ -1302,7 +1279,7 @@ void ImportProject::selectOneVsConfig(cppcheck::Platform::Type platform) } const ImportProject::FileSettings &fs = *it; bool remove = false; - if (fs.cfg.compare(0,5,"Debug") != 0) + if (!startsWith(fs.cfg,"Debug")) remove = true; if (platform == cppcheck::Platform::Type::Win64 && fs.platformType != platform) remove = true; diff --git a/lib/library.cpp b/lib/library.cpp index 15d649171..4c886f699 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -1174,7 +1174,7 @@ const Library::Container* Library::detectContainerInternal(const Token* const ty if (container.startPattern.empty()) continue; - const int offset = (withoutStd && container.startPattern2.find("std :: ") == 0) ? 7 : 0; + const int offset = (withoutStd && startsWith(container.startPattern2, "std :: ")) ? 7 : 0; // If endPattern is undefined, it will always match, but itEndPattern has to be defined. if (detect != IteratorOnly && container.endPattern.empty()) { @@ -1754,7 +1754,7 @@ std::shared_ptr createTokenFromExpression(const std::string& returnValue, // set varids for (Token* tok2 = tokenList->front(); tok2; tok2 = tok2->next()) { - if (tok2->str().compare(0, 3, "arg") != 0) + if (!startsWith(tok2->str(), "arg")) continue; nonneg int const id = strToInt(tok2->str().c_str() + 3); tok2->varId(id); diff --git a/lib/path.cpp b/lib/path.cpp index 078c6997d..74e6f3f46 100644 --- a/lib/path.cpp +++ b/lib/path.cpp @@ -171,7 +171,7 @@ bool Path::isAbsolute(const std::string& path) return false; // On Windows, 'C:\foo\bar' is an absolute path, while 'C:foo\bar' is not - return nativePath.compare(0, 2, "\\\\") == 0 || (std::isalpha(nativePath[0]) != 0 && nativePath.compare(1, 2, ":\\") == 0); + return startsWith(nativePath, "\\\\") || (std::isalpha(nativePath[0]) != 0 && nativePath.compare(1, 2, ":\\") == 0); #else return !nativePath.empty() && nativePath[0] == '/'; #endif @@ -227,7 +227,7 @@ bool Path::acceptFile(const std::string &path, const std::set &extr bool Path::isHeader(const std::string &path) { const std::string extension = getFilenameExtensionInLowerCase(path); - return (extension.compare(0, 2, ".h") == 0); + return startsWith(extension, ".h"); } std::string Path::getAbsoluteFilePath(const std::string& filePath) diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index a2058de4b..aa572cb5c 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -27,6 +27,7 @@ #include "settings.h" #include "standards.h" #include "suppressions.h" +#include "utils.h" #include #include @@ -370,7 +371,7 @@ static const simplecpp::Token *gotoEndIf(const simplecpp::Token *cmdtok) int level = 0; while (nullptr != (cmdtok = cmdtok->next)) { if (cmdtok->op == '#' && !sameline(cmdtok->previous,cmdtok) && sameline(cmdtok, cmdtok->next)) { - if (cmdtok->next->str().compare(0,2,"if")==0) + if (startsWith(cmdtok->next->str(),"if")) ++level; else if (cmdtok->next->str() == "endif") { --level; @@ -755,7 +756,7 @@ void Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool sh for (const simplecpp::Output &out : outputList) { switch (out.type) { case simplecpp::Output::ERROR: - if (out.msg.compare(0,6,"#error")!=0 || showerror) + if (!startsWith(out.msg,"#error") || showerror) error(out.location.file(), out.location.line, out.msg); break; case simplecpp::Output::WARNING: diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index eac6ae6cf..10b5fa7a0 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -172,7 +172,7 @@ std::vector Suppressions::parseMultiSuppressComment(c break; if (word.find_first_not_of("+-*/%#;") == std::string::npos) break; - if (word.compare(0, 11, "symbolName=") == 0) { + if (startsWith(word, "symbolName=")) { s.symbolName = word.substr(11); } else { if (errorMessage && errorMessage->empty()) @@ -316,7 +316,7 @@ bool Suppressions::Suppression::parseComment(std::string comment, std::string *e break; if (word.find_first_not_of("+-*/%#;") == std::string::npos) break; - if (word.compare(0,11,"symbolName=")==0) + if (startsWith(word,"symbolName=")) symbolName = word.substr(11); else if (errorMessage && errorMessage->empty()) *errorMessage = "Bad suppression attribute '" + word + "'. You can write comments in the comment after a ; or //. Valid suppression attributes; symbolName=sym"; @@ -377,7 +377,7 @@ std::string Suppressions::Suppression::getText() const ret += " symbolName=" + symbolName; if (hash > 0) ret += " hash=" + std::to_string(hash); - if (ret.compare(0,1," ")==0) + if (startsWith(ret," ")) return ret.substr(1); return ret; } diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 9628dc0c6..3225ef2cf 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -2389,7 +2389,7 @@ static bool isOperator(const Token *tokenDef) if (tokenDef->isOperatorKeyword()) return true; const std::string &name = tokenDef->str(); - return name.size() > 8 && name.compare(0,8,"operator")==0 && std::strchr("+-*/%&|~^<>!=[(", name[8]); + return name.size() > 8 && startsWith(name,"operator") && std::strchr("+-*/%&|~^<>!=[(", name[8]); } Function::Function(const Tokenizer *mTokenizer, diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index fc7554a70..b620313c7 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -27,6 +27,7 @@ #include "mathlib.h" #include "sourcelocation.h" #include "token.h" +#include "utils.h" #include #include @@ -1061,7 +1062,7 @@ public: bool isAnonymous() const { // TODO: Check if class/struct is anonymous - return className.size() > 9 && className.compare(0,9,"Anonymous") == 0 && std::isdigit(className[9]); + return className.size() > 9 && startsWith(className,"Anonymous") && std::isdigit(className[9]); } const Enumerator * findEnumerator(const std::string & name) const { diff --git a/lib/timer.cpp b/lib/timer.cpp index 4e04b5819..73bb3f024 100644 --- a/lib/timer.cpp +++ b/lib/timer.cpp @@ -18,6 +18,8 @@ #include "timer.h" +#include "utils.h" + #include #include #include @@ -61,7 +63,7 @@ void TimerResults::showResults(SHOWTIME_MODES mode) const bool hasParent = false; { // Do not use valueFlow.. in "Overall time" because those are included in Tokenizer already - if (iter->first.compare(0,9,"valueFlow") == 0) + if (startsWith(iter->first,"valueFlow")) hasParent = true; // Do not use inner timers in "Overall time" diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index cc4f3cfa0..aaa7d599a 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -246,7 +246,7 @@ bool Tokenizer::duplicateTypedef(Token **tokPtr, const Token *name, const Token if (end) end = end->next(); } else if (end->str() == "(") { - if (tok->previous()->str().compare(0, 8, "operator") == 0) + if (startsWith(tok->previous()->str(), "operator")) // conversion operator return false; if (tok->previous()->str() == "typedef") @@ -8124,7 +8124,7 @@ static bool isNonMacro(const Token* tok) return true; if (cAlternativeTokens.count(tok->str()) > 0) return true; - if (tok->str().compare(0, 2, "__") == 0) // attribute/annotation + if (startsWith(tok->str(), "__")) // attribute/annotation return true; if (Token::simpleMatch(tok, "alignas (")) return true; @@ -8236,7 +8236,7 @@ void Tokenizer::reportUnknownMacros() const continue; if (cAlternativeTokens.count(tok->linkAt(2)->next()->str()) > 0) continue; - if (tok->next()->str().compare(0, 2, "__") == 0) // attribute/annotation + if (startsWith(tok->next()->str(), "__")) // attribute/annotation continue; unknownMacroError(tok->next()); } @@ -8990,7 +8990,7 @@ void Tokenizer::simplifyCppcheckAttribute() if (!tok->previous()) continue; const std::string &attr = tok->previous()->str(); - if (attr.compare(0, 11, "__cppcheck_") != 0) // TODO: starts_with("__cppcheck_") + if (!startsWith(attr, "__cppcheck_")) continue; if (attr.compare(attr.size()-2, 2, "__") != 0) // TODO: ends_with("__") continue; @@ -8998,7 +8998,7 @@ void Tokenizer::simplifyCppcheckAttribute() Token *vartok = tok->link(); while (Token::Match(vartok->next(), "%name%|*|&|::")) { vartok = vartok->next(); - if (Token::Match(vartok, "%name% (") && vartok->str().compare(0,11,"__cppcheck_") == 0) + if (Token::Match(vartok, "%name% (") && startsWith(vartok->str(),"__cppcheck_")) vartok = vartok->linkAt(1); } @@ -10471,7 +10471,7 @@ bool Tokenizer::hasIfdef(const Token *start, const Token *end) const assert(mPreprocessor); return std::any_of(mPreprocessor->getDirectives().cbegin(), mPreprocessor->getDirectives().cend(), [&](const Directive& d) { - return d.str.compare(0, 3, "#if") == 0 && + return startsWith(d.str, "#if") && d.linenr >= start->linenr() && d.linenr <= end->linenr() && start->fileIndex() < list.getFiles().size() && diff --git a/lib/utils.h b/lib/utils.h index 46ee7d698..690a47c10 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -80,6 +80,17 @@ struct EnumClassHash { } }; +inline bool startsWith(const std::string& str, const char start[], std::size_t startlen) +{ + return str.compare(0, startlen, start) == 0; +} + +template +bool startsWith(const std::string& str, const char (&start)[N]) +{ + return startsWith(str, start, N - 1); +} + inline bool endsWith(const std::string &str, char c) { return !str.empty() && str.back() == c; diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 3036bdc42..a6e5311dc 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -4494,7 +4494,7 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList &tokenlist, ErrorLog valueFlowForwardLifetime(tok->next(), tokenlist, errorLogger, settings); } else { const std::string& retVal = settings->library.returnValue(tok); - if (retVal.compare(0, 3, "arg") == 0) { + if (startsWith(retVal, "arg")) { std::size_t iArg{}; try { iArg = strToInt(retVal.substr(3)); diff --git a/test/cli/test-more-projects.py b/test/cli/test-more-projects.py index 6269d64a5..b2945df77 100644 --- a/test/cli/test-more-projects.py +++ b/test/cli/test-more-projects.py @@ -224,4 +224,31 @@ def test_project_missing_subproject(tmpdir): ret, stdout, stderr = cppcheck(['--project=' + project_file, '--template=cppcheck1']) assert ret == 1, stdout assert stdout == "cppcheck: error: failed to open project '{}/dummy.json'. The file does not exist.\n".format(str(tmpdir).replace('\\', '/')) - assert stderr == '' \ No newline at end of file + assert stderr == '' + + +def test_project_std(tmpdir): + with open(os.path.join(tmpdir, 'bug1.cpp'), 'wt') as f: + f.write(""" + #if __cplusplus == 201402L + int x = 123 / 0; + #endif + """) + + compile_commands = os.path.join(tmpdir, 'compile_commands.json') + + compilation_db = [ + { + "directory": str(tmpdir), + "command": "c++ -o bug1.o -c bug1.cpp -std=c++14", + "file": "bug1.cpp", + "output": "bug1.o" + } + ] + + with open(compile_commands, 'wt') as f: + f.write(json.dumps(compilation_db)) + + ret, stdout, stderr = cppcheck(['--project=' + compile_commands, '--enable=all', '-rp=' + str(tmpdir), '--template=cppcheck1']) + assert ret == 0, stdout + assert (stderr == '[bug1.cpp:3]: (error) Division by zero.\n') \ No newline at end of file diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 1eede5650..d1f41d455 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -28,6 +28,7 @@ #include "suppressions.h" #include "fixture.h" #include "timer.h" +#include "utils.h" #include #include @@ -272,7 +273,7 @@ private: const char * const argv[] = {"cppcheck"}; ASSERT(parser->parseFromArgs(1, argv)); ASSERT_EQUALS(true, parser->getShowHelp()); - ASSERT(GET_REDIRECT_OUTPUT.find("Cppcheck - A tool for static C/C++ code analysis") == 0); + ASSERT(startsWith(GET_REDIRECT_OUTPUT, "Cppcheck - A tool for static C/C++ code analysis")); } void helpshort() { @@ -280,7 +281,7 @@ private: const char * const argv[] = {"cppcheck", "-h"}; ASSERT(parser->parseFromArgs(2, argv)); ASSERT_EQUALS(true, parser->getShowHelp()); - ASSERT(GET_REDIRECT_OUTPUT.find("Cppcheck - A tool for static C/C++ code analysis") == 0); + ASSERT(startsWith(GET_REDIRECT_OUTPUT, "Cppcheck - A tool for static C/C++ code analysis")); } void helplong() { @@ -288,7 +289,7 @@ private: const char * const argv[] = {"cppcheck", "--help"}; ASSERT(parser->parseFromArgs(2, argv)); ASSERT_EQUALS(true, parser->getShowHelp()); - ASSERT(GET_REDIRECT_OUTPUT.find("Cppcheck - A tool for static C/C++ code analysis") == 0); + ASSERT(startsWith(GET_REDIRECT_OUTPUT, "Cppcheck - A tool for static C/C++ code analysis")); } void showversion() { @@ -1594,7 +1595,7 @@ private: const char * const argv[] = {"cppcheck", "--doc"}; ASSERT(parser->parseFromArgs(2, argv)); ASSERT(parser->exitAfterPrinting()); - ASSERT(GET_REDIRECT_OUTPUT.find("## ") == 0); + ASSERT(startsWith(GET_REDIRECT_OUTPUT, "## ")); } void showtime() { diff --git a/test/testsimplifyusing.cpp b/test/testsimplifyusing.cpp index 32287681a..02cc14aaf 100644 --- a/test/testsimplifyusing.cpp +++ b/test/testsimplifyusing.cpp @@ -23,6 +23,7 @@ #include "fixture.h" #include "token.h" #include "tokenize.h" +#include "utils.h" #include @@ -1391,7 +1392,7 @@ private: "STAMP(B, A);\n" "STAMP(C, B);\n"; tok(code, cppcheck::Platform::Type::Native, /*debugwarnings*/ true, /*preprocess*/ true); - ASSERT_EQUALS(errout.str().compare(0, 64, "[test.cpp:6]: (debug) Failed to parse 'using C = S < S < S < int"), 0); + ASSERT(startsWith(errout.str(), "[test.cpp:6]: (debug) Failed to parse 'using C = S < S < S < int")); } void scopeInfo1() { diff --git a/test/testutils.cpp b/test/testutils.cpp index bdc095f54..3c2a7a960 100644 --- a/test/testutils.cpp +++ b/test/testutils.cpp @@ -39,6 +39,7 @@ private: TEST_CASE(isCharLiteral); TEST_CASE(strToInt); TEST_CASE(id_string); + TEST_CASE(startsWith); } void isValidGlobPattern() const { @@ -345,6 +346,19 @@ private: ASSERT_EQUALS(std::string(16,'f'), id_string_i(~0ULL)); } } + + void startsWith() const + { + ASSERT(::startsWith("test", "test")); + ASSERT(::startsWith("test2", "test")); + ASSERT(::startsWith("test test", "test")); + ASSERT(::startsWith("test", "t")); + ASSERT(!::startsWith("2test", "test")); + ASSERT(!::startsWith("", "test")); + ASSERT(!::startsWith("tes", "test")); + ASSERT(!::startsWith("2test", "t")); + ASSERT(!::startsWith("t", "test")); + } }; REGISTER_TEST(TestUtils) diff --git a/tools/dmake.cpp b/tools/dmake.cpp index bc0c7a064..388efd50d 100644 --- a/tools/dmake.cpp +++ b/tools/dmake.cpp @@ -38,7 +38,7 @@ static std::string builddir(std::string filename) { - if (filename.compare(0,4,"lib/") == 0) + if (startsWith(filename,"lib/")) filename = "$(libcppdir)" + filename.substr(3); return filename; } @@ -77,13 +77,13 @@ static void getDeps(const std::string &filename, std::vector &depfi * Files are searched according to the following priority: * [test, tools] -> cli -> lib -> externals */ - if (filename.compare(0, 4, "cli/") == 0) + if (startsWith(filename, "cli/")) getDeps("lib" + filename.substr(filename.find('/')), depfiles); - else if (filename.compare(0, 5, "test/") == 0) + else if (startsWith(filename, "test/")) getDeps("cli" + filename.substr(filename.find('/')), depfiles); - else if (filename.compare(0, 6, "tools/") == 0) + else if (startsWith(filename, "tools/")) getDeps("cli" + filename.substr(filename.find('/')), depfiles); - else if (filename.compare(0, 4, "lib/") == 0) { + else if (startsWith(filename, "lib/")) { for (const std::string & external : externalfolders) getDeps(external + filename.substr(filename.find('/')), depfiles); } @@ -126,7 +126,7 @@ static void getDeps(const std::string &filename, std::vector &depfi static void compilefiles(std::ostream &fout, const std::vector &files, const std::string &args) { for (const std::string &file : files) { - const bool external(file.compare(0,10,"externals/") == 0); + const bool external(startsWith(file,"externals/")); fout << objfile(file) << ": " << file; std::vector depfiles; getDeps(file, depfiles);