diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 1ffae490c..04d20987c 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -1511,7 +1511,7 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func) else if (tok2->isName() && isMemberVar(scope, tok2)) return(false); // TODO: Only bailout if function takes argument as non-const reference } - } else if (Token::Match(tok1, "> (") && (!tok1->link() || !Token::Match(tok1->link()->previous(), "static_cast|const_cast|dynamic_cast|reinterpret_cast"))) { + } else if (Token::simpleMatch(tok1, "> (") && (!tok1->link() || !Token::Match(tok1->link()->previous(), "static_cast|const_cast|dynamic_cast|reinterpret_cast"))) { return(false); } else if (Token::Match(tok1, "%var% . %var% (")) { if (!isMemberVar(scope, tok1)) diff --git a/lib/checkinternal.cpp b/lib/checkinternal.cpp index 82a55d3c8..1fe1977b9 100644 --- a/lib/checkinternal.cpp +++ b/lib/checkinternal.cpp @@ -169,6 +169,51 @@ void CheckInternal::checkMissingPercentCharacter() } } +void CheckInternal::checkUnknownPattern() +{ + static std::set knownPatterns; + if (knownPatterns.empty()) { + knownPatterns.insert("%any%"); + knownPatterns.insert("%var%"); + knownPatterns.insert("%type%"); + knownPatterns.insert("%num%"); + knownPatterns.insert("%bool%"); + knownPatterns.insert("%str%"); + knownPatterns.insert("%varid%"); + knownPatterns.insert("%or%"); + knownPatterns.insert("%oror%"); + knownPatterns.insert("%op%"); + } + + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { + if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch (")) + continue; + + // Get pattern string + const Token *pattern_tok = tok->tokAt(4)->nextArgument(); + if (!pattern_tok || pattern_tok->type() != Token::eString) + continue; + + const std::string pattern = pattern_tok->strValue(); + bool inBrackets = false; + + for (std::string::size_type i = 0; i < pattern.length()-1; i++) { + if (pattern[i] == '[' && (i == 0 || pattern[i-1] == ' ')) + inBrackets = true; + else if (pattern[i] == ']') + inBrackets = false; + else if (pattern[i] == '%' && pattern[i+1] != ' ' && pattern[i+1] != '|' && !inBrackets) { + std::string::size_type end = pattern.find('%', i+1); + if (end != std::string::npos) { + std::string s = pattern.substr(i, end-i+1); + if (knownPatterns.find(s) == knownPatterns.end()) + unknownPatternError(tok, s); + } + } + } + } +} + void CheckInternal::simplePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::warning, "simplePatternError", @@ -190,4 +235,10 @@ void CheckInternal::missingPercentCharacterError(const Token* tok, const std::st ); } +void CheckInternal::unknownPatternError(const Token* tok, const std::string& pattern) +{ + reportError(tok, Severity::error, "unkownPattern", + "Unkown pattern used: \"" + pattern + "\""); +} + #endif // #ifndef NDEBUG diff --git a/lib/checkinternal.h b/lib/checkinternal.h index ac02ab9b7..fb4071131 100644 --- a/lib/checkinternal.h +++ b/lib/checkinternal.h @@ -53,6 +53,7 @@ public: checkInternal.checkTokenMatchPatterns(); checkInternal.checkTokenSimpleMatchPatterns(); checkInternal.checkMissingPercentCharacter(); + checkInternal.checkUnknownPattern(); } /** @brief %Check if a simple pattern is used inside Token::Match or Token::findmatch */ @@ -64,16 +65,21 @@ public: /** @brief %Check for missing % end character in Token::Match pattern */ void checkMissingPercentCharacter(); + /** @brief %Check for for unkown (invalid) complex patterns like "%typ%" */ + void checkUnknownPattern(); + private: void simplePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); void complexPatternError(const Token *tok, const std::string &pattern, const std::string &funcname); void missingPercentCharacterError(const Token *tok, const std::string &pattern, const std::string &funcname); + void unknownPatternError(const Token* tok, const std::string& pattern); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckInternal c(0, settings, errorLogger); c.simplePatternError(0, "class {", "Match"); c.complexPatternError(0, "%type% ( )", "Match"); c.missingPercentCharacterError(0, "%num", "Match"); + c.unknownPatternError(0, "%typ"); } std::string myName() const { diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 8e699f3a5..512c611bf 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -102,7 +102,7 @@ void CheckIO::checkFileUsage() std::size_t varListSize = symbolDatabase->getVariableListSize(); for (std::size_t i = 1; i < varListSize; ++i) { const Variable* var = symbolDatabase->getVariableFromVarId(i); - if (!var || !var->varId() || !Token::Match(var->typeStartToken(), "FILE *")) + if (!var || !var->varId() || !Token::simpleMatch(var->typeStartToken(), "FILE *")) continue; if (var->isLocal()) { @@ -153,7 +153,7 @@ void CheckIO::checkFileUsage() fileTok = tok->tokAt(-2); operation = Filepointer::OPEN; } else if (tok->str() == "rewind" || tok->str() == "fseek" || tok->str() == "fsetpos" || tok->str() == "fflush") { - if (Token::Match(tok, "fflush ( stdin )")) + if (Token::simpleMatch(tok, "fflush ( stdin )")) fflushOnInputStreamError(tok, tok->strAt(2)); else { fileTok = tok->tokAt(2); diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 2c25e02c2..945e16205 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -508,7 +508,7 @@ void CheckOther::checkSizeofForPointerSize() variable = tok->next(); tokVar = tok->tokAt(5)->nextArgument(); - } else if (Token::Match(tok, "memset (")) { + } else if (Token::simpleMatch(tok, "memset (")) { variable = tok->tokAt(2); tokVar = variable->tokAt(2)->nextArgument(); diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index d4735ad40..9f12d68c8 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -1334,12 +1334,12 @@ void CheckStl::uselessCalls() } else if (tok->varId() && Token::Match(tok, "%var% . swap ( %var% )") && tok->varId() == tok->tokAt(4)->varId()) { uselessCallsSwapError(tok, tok->str()); - } else if (Token::Match(tok, ". substr (")) { + } else if (Token::simpleMatch(tok, ". substr (")) { if (Token::Match(tok->tokAt(3), "0| )")) uselessCallsSubstrError(tok, false); else if (tok->strAt(3) == "0" && tok->linkAt(2)->strAt(-1) == "npos") uselessCallsSubstrError(tok, false); - else if (Token::Match(tok->linkAt(2)->tokAt(-2), ", 0 )")) + else if (Token::simpleMatch(tok->linkAt(2)->tokAt(-2), ", 0 )")) uselessCallsSubstrError(tok, true); } } diff --git a/test/testinternal.cpp b/test/testinternal.cpp index 7f0d2c41c..0793ff67f 100644 --- a/test/testinternal.cpp +++ b/test/testinternal.cpp @@ -37,6 +37,7 @@ private: TEST_CASE(simplePatternSquareBrackets) TEST_CASE(simplePatternAlternatives) TEST_CASE(missingPercentCharacter) + TEST_CASE(unknownPattern) TEST_CASE(internalError) } @@ -212,7 +213,8 @@ private: " const Token *tok;\n" " Token::Match(tok, \"foo % %type % bar\");\n" "}"); - ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo % %type % bar\"\n", errout.str()); + ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo % %type % bar\"\n" + "[test.cpp:3]: (error) Unkown pattern used: \"%type %\"\n", errout.str()); // Find missing % also in 'alternatives' pattern check("void f() {\n" @@ -229,6 +231,19 @@ private: ASSERT_EQUALS("", errout.str()); } + void unknownPattern() { + check("void f() {\n" + " Token::Match(tok, \"%typ%\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Unkown pattern used: \"%typ%\"\n", errout.str()); + + // Make sure we don't take %or% for a broken %oror% + check("void f() {\n" + " Token::Match(tok, \"%type%\");\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } + void internalError() { // Make sure cppcheck does not raise an internal error of Token::Match ( Ticket #3727 ) check("class DELPHICLASS X;\n"