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;