diff --git a/Makefile b/Makefile index 38aa65c21..d18c7908c 100644 --- a/Makefile +++ b/Makefile @@ -805,7 +805,7 @@ test/testsimplifytokens.o: test/testsimplifytokens.cpp lib/check.h lib/color.h l test/testsimplifytypedef.o: test/testsimplifytypedef.cpp externals/simplecpp/simplecpp.h lib/check.h 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 test/fixture.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsimplifytypedef.cpp -test/testsimplifyusing.o: test/testsimplifyusing.cpp lib/check.h 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 test/fixture.h +test/testsimplifyusing.o: test/testsimplifyusing.cpp externals/simplecpp/simplecpp.h lib/check.h 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 test/fixture.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsimplifyusing.cpp test/testsingleexecutor.o: test/testsingleexecutor.cpp cli/executor.h cli/singleexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.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/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h test/redirect.h diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index ecebb5001..3985d45de 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -2760,11 +2760,23 @@ static bool scopesMatch(const std::string &scope1, const std::string &scope2, co return false; } +static unsigned int tokDistance(const Token* tok1, const Token* tok2) { + unsigned int dist = 0; + const Token* tok = tok1; + while (tok != tok2) { + ++dist; + tok = tok->next(); + } + return dist; +}; + bool Tokenizer::simplifyUsing() { if (!isCPP() || mSettings->standards.cpp < Standards::CPP11) return false; + const unsigned int maxReplacementTokens = 1000; // limit the number of tokens we replace + bool substitute = false; ScopeInfo3 scopeInfo; ScopeInfo3 *currentScope = &scopeInfo; @@ -3006,6 +3018,12 @@ bool Tokenizer::simplifyUsing() } else if (!usingMatch(nameToken, scope, &tok1, scope1, currentScope1, nullptr)) continue; + const auto nReplace = tokDistance(start, usingEnd); + if (nReplace > maxReplacementTokens) { + simplifyUsingError(usingStart, usingEnd); + continue; + } + // remove the qualification std::string fullScope = scope; std::string removed; @@ -3187,18 +3205,7 @@ bool Tokenizer::simplifyUsing() } } else { skip = true; - if (mSettings->debugwarnings && mErrorLogger) { - std::string str; - for (Token *tok3 = usingStart; tok3 && tok3 != usingEnd; tok3 = tok3->next()) { - if (!str.empty()) - str += ' '; - str += tok3->str(); - } - str += " ;"; - std::list callstack(1, usingStart); - mErrorLogger->reportErr(ErrorMessage(callstack, &list, Severity::debug, "simplifyUsing", - "Failed to parse \'" + str + "\'. The checking continues anyway.", Certainty::normal)); - } + simplifyUsingError(usingStart, usingEnd); } tok1 = after; } @@ -3233,6 +3240,22 @@ bool Tokenizer::simplifyUsing() return substitute; } +void Tokenizer::simplifyUsingError(const Token* usingStart, const Token* usingEnd) +{ + if (mSettings->debugwarnings && mErrorLogger) { + std::string str; + for (const Token *tok = usingStart; tok && tok != usingEnd; tok = tok->next()) { + if (!str.empty()) + str += ' '; + str += tok->str(); + } + str += " ;"; + std::list callstack(1, usingStart); + mErrorLogger->reportErr(ErrorMessage(callstack, &list, Severity::debug, "simplifyUsing", + "Failed to parse \'" + str + "\'. The checking continues anyway.", Certainty::normal)); + } +} + bool Tokenizer::createTokens(std::istream &code, const std::string& FileName) { diff --git a/lib/tokenize.h b/lib/tokenize.h index de05fdfd4..212302a9d 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -280,6 +280,7 @@ public: /** */ bool simplifyUsing(); + void simplifyUsingError(const Token* usingStart, const Token* usingEnd); /** Simplify useless C++ empty namespaces, like: 'namespace %name% { }'*/ void simplifyEmptyNamespaces(); diff --git a/test/testsimplifyusing.cpp b/test/testsimplifyusing.cpp index 45f578cb0..1cc7d1aed 100644 --- a/test/testsimplifyusing.cpp +++ b/test/testsimplifyusing.cpp @@ -24,6 +24,8 @@ #include "token.h" #include "tokenize.h" +#include + #include // IWYU pragma: keep #include @@ -92,13 +94,14 @@ private: TEST_CASE(simplifyUsing10172); TEST_CASE(simplifyUsing10173); TEST_CASE(simplifyUsing10335); + TEST_CASE(simplifyUsing10720); TEST_CASE(scopeInfo1); TEST_CASE(scopeInfo2); } #define tok(...) tok_(__FILE__, __LINE__, __VA_ARGS__) - std::string tok_(const char* file, int line, const char code[], cppcheck::Platform::Type type = cppcheck::Platform::Type::Native, bool debugwarnings = true) { + std::string tok_(const char* file, int line, const char code[], cppcheck::Platform::Type type = cppcheck::Platform::Type::Native, bool debugwarnings = true, bool preprocess = false) { errout.str(""); settings0.certainty.enable(Certainty::inconclusive); @@ -106,6 +109,18 @@ private: PLATFORM(settings0.platform, type); Tokenizer tokenizer(&settings0, this); + if (preprocess) { + std::vector files{ "test.cpp" }; + std::istringstream istr(code); + const simplecpp::TokenList tokens1(istr, files, files[0]); + + simplecpp::TokenList tokens2(files); + std::map filedata; + simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); + + tokenizer.createTokens(std::move(tokens2)); + } + std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); @@ -1346,6 +1361,17 @@ private: ASSERT_EQUALS(exp, tok(code)); } + void simplifyUsing10720() { + const char code[] = "template \n" + "struct S {};\n" + "#define STAMP(thiz, prev) using thiz = S;\n" + "STAMP(A, int);\n" + "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); + } + void scopeInfo1() { const char code[] = "struct A {\n" " enum class Mode { UNKNOWN, ENABLED, NONE, };\n"