diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index f2d2254f833..842d597b0a4 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -461,6 +461,11 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a mSettings.quiet = true; } + // -CC + else if (std::strcmp(argv[i], "-CC") == 0) { + mSettings.keepComments = true; + } + // Include paths else if (std::strncmp(argv[i], "-I", 2) == 0) { std::string path; @@ -1737,6 +1742,12 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a if (mSettings.basePaths.empty() && mSettings.relativePaths) mSettings.basePaths = mPathNames; + if (mSettings.keepComments && !mSettings.preprocessOnly) { + mLogger.printError("-CC may only be used on conjunciton with -E"); + return Result::Fail; + } + + return Result::Success; } @@ -1760,6 +1771,7 @@ void CmdLineParser::printHelp() const " addon json files or through this command line option.\n" " If not present, Cppcheck will try \"python3\" first and\n" " then \"python\".\n" + " -CC Do not remove comments during preprocessing (requires -E).\n" " --cppcheck-build-dir=\n" " Cppcheck work folder. Advantages:\n" " * whole program analysis\n" diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 42af3627f02..2b8e9691cef 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -1024,7 +1024,8 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str // Parse comments and then remove them mLogger->addRemarkComments(preprocessor.getRemarkComments()); preprocessor.inlineSuppressions(mSuppressions.nomsg); - preprocessor.removeComments(); + if (!mSettings.keepComments) + preprocessor.removeComments(); // Get directives std::list directives; @@ -1054,7 +1055,8 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str // Do preprocessing on included file mLogger->addRemarkComments(preprocessor.getRemarkComments(data.tokens)); preprocessor.inlineSuppressions(data.tokens, mSuppressions.nomsg); - Preprocessor::removeComments(data.tokens); + if (!mSettings.keepComments) + Preprocessor::removeComments(data.tokens); Preprocessor::createDirectives(data.tokens, directives); Preprocessor::simplifyPragmaAsm(data.tokens); // Discover new configurations from included file diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 1289e6e6510..325ee4c5c55 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -890,7 +890,8 @@ simplecpp::TokenList Preprocessor::preprocess(const std::string &cfgStr, std::ve mMacroUsage = std::move(macroUsage); mIfCond = std::move(ifCond); - tokens2.removeComments(); + if (!mSettings.keepComments) + tokens2.removeComments(); return tokens2; } @@ -919,6 +920,13 @@ std::string Preprocessor::getcode(const std::string &cfgStr, std::vectormacro.empty()) ret << Preprocessor::macroChar; ret << tok->str(); + + if (tok->comment) + line += std::count_if(tok->str().cbegin(), + tok->str().cend(), + [](char c) { + return c == '\n'; + }); } return ret.str(); diff --git a/lib/settings.h b/lib/settings.h index ce4a32e1690..52f20afb7c4 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -372,6 +372,9 @@ class CPPCHECKLIB WARN_UNUSED Settings { /** @brief Using -E for debugging purposes */ bool preprocessOnly{}; + /** @brief Keep comments in preprocessed output */ + bool keepComments{}; + /** @brief Is --quiet given? */ bool quiet{}; diff --git a/man/cppcheck.1.xml b/man/cppcheck.1.xml index 8579d375bdf..1a63b86e200 100644 --- a/man/cppcheck.1.xml +++ b/man/cppcheck.1.xml @@ -259,6 +259,14 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ + + + + + + Do not remove comments during preprocessing (requires -E). + + @@ -293,6 +301,14 @@ Example: -DDEBUG=1 -D__cplusplus Example: '-UDEBUG' + + + + + + Print preprocessor output on stdout and don't do any further processing. + + diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 326b7ad613d..6f963e07212 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -550,6 +550,8 @@ class TestCmdlineParser : public TestFixture { TEST_CASE(reportTypeMisraCpp2023); TEST_CASE(invalidReportType); TEST_CASE(defaultReportType); + TEST_CASE(keepComments); + TEST_CASE(keepCommentsNoDashE); } void nooptions() { @@ -3831,6 +3833,20 @@ class TestCmdlineParser : public TestFixture { ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv)); ASSERT_EQUALS_ENUM(ReportType::normal, settings->reportType); } + + void keepComments() { + REDIRECT; + const char *const argv[] = { "cppcheck", "-E", "-CC", "file.cpp" }; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv)); + ASSERT(settings->keepComments); + } + + void keepCommentsNoDashE() { + REDIRECT; + const char *const argv[] = { "cppcheck", "-CC", "file.cpp" }; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parseFromArgs(argv)); + ASSERT_EQUALS("cppcheck: error: -CC may only be used on conjunciton with -E\n", logger->str()); + } }; REGISTER_TEST(TestCmdlineParser) diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 4b4285ab159..9aad97b2c99 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -374,6 +374,8 @@ class TestPreprocessor : public TestFixture { TEST_CASE(testMissingIncludeCheckConfig); TEST_CASE(testLazyInclude); + TEST_CASE(testKeepComments); + TEST_CASE(testKeepCommentsMultiline); TEST_CASE(hasInclude); @@ -3092,6 +3094,52 @@ class TestPreprocessor : public TestFixture { ASSERT_EQUALS(1, cache.size()); } + void testKeepComments() { + const char *code = "#include \"header.h\"\n" + "// source file comment\n" + "/* source file comment */\n"; + std::vector files; + simplecpp::TokenList tokens(code, files, "test.c"); + + ScopedFile header("header.h", + "// header comment\n" + "/* header comment */\n"); + + Settings settings; + settings.keepComments = true; + Preprocessor preprocessor(tokens, settings, *this, Standards::Language::CPP); + + simplecpp::OutputList outputList; + simplecpp::TokenList tokens2 = preprocessor.preprocess("", files, outputList); + std::string out = tokens2.stringify(); + + const char *expected = "\n" + "#line 1 \"header.h\"\n" + "// header comment\n" + "/* header comment */\n" + "#line 2 \"test.c\"\n" + "// source file comment\n" + "/* source file comment */"; + ASSERT_EQUALS(expected, out); + } + + void testKeepCommentsMultiline() { + const char *code = "/* multi...\n" + " ...line */\n" + "int x;\n"; + std::vector files; + simplecpp::TokenList tokens(code, files, "test.c"); + + Settings settings; + settings.keepComments = true; + Preprocessor preprocessor(tokens, settings, *this, Standards::Language::C); + + const char *expected = "/* multi...\n" + " ...line */\n" + "int x ;"; + ASSERT_EQUALS(expected, preprocessor.getcode("", files, false)); + } + void hasInclude() { const char code[] = "#if __has_include()\n123\n#endif\n"; Settings settings;