diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp
index 0ea355358d6..cbcc456cbc6 100644
--- a/cli/cmdlineparser.cpp
+++ b/cli/cmdlineparser.cpp
@@ -647,6 +647,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
mSettings.cppHeaderProbe = true;
}
+ else if (std::strcmp(argv[i], "--debug-ast") == 0)
+ mSettings.debugast = true;
+
// Show debug warnings for lookup for configuration files
else if (std::strcmp(argv[i], "--debug-clang-output") == 0)
mSettings.debugClangOutput = true;
@@ -687,10 +690,16 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
else if (std::strcmp(argv[i], "--debug-simplified") == 0)
mSettings.debugSimplified = true;
+ else if (std::strcmp(argv[i], "--debug-symdb") == 0)
+ mSettings.debugsymdb = true;
+
// Show template information
else if (std::strcmp(argv[i], "--debug-template") == 0)
mSettings.debugtemplate = true;
+ else if (std::strcmp(argv[i], "--debug-valueflow") == 0)
+ mSettings.debugvalueflow = true;
+
// Show debug warnings
else if (std::strcmp(argv[i], "--debug-warnings") == 0)
mSettings.debugwarnings = true;
diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp
index 5dd0576db71..65f9e7ef1ae 100644
--- a/lib/cppcheck.cpp
+++ b/lib/cppcheck.cpp
@@ -725,8 +725,7 @@ unsigned int CppCheck::checkClang(const FileWithDetails &file)
mErrorLogger,
mSettings,
&s_timerResults);
- if (mSettings.debugnormal)
- tokenizer.printDebugOutput(1, std::cout);
+ tokenizer.printDebugOutput(std::cout);
checkNormalTokens(tokenizer, nullptr); // TODO: provide analyzer information
// create dumpfile
diff --git a/lib/settings.h b/lib/settings.h
index 64e007b46ce..7e9be6c6a40 100644
--- a/lib/settings.h
+++ b/lib/settings.h
@@ -188,6 +188,9 @@ class CPPCHECKLIB WARN_UNUSED Settings {
/** @brief Are we running from DACA script? */
bool daca{};
+ /** @brief Is --debug-ast given? */
+ bool debugast{};
+
/** @brief Is --debug-clang-output given? */
bool debugClangOutput{};
@@ -215,9 +218,15 @@ class CPPCHECKLIB WARN_UNUSED Settings {
/** @brief Is --debug-simplified given? */
bool debugSimplified{};
+ /** @brief Is --debug-symdb given? */
+ bool debugsymdb{};
+
/** @brief Is --debug-template given? */
bool debugtemplate{};
+ /** @brief Is --debug-valueflow given? */
+ bool debugvalueflow{};
+
/** @brief Is --debug-warnings given? */
bool debugwarnings{};
diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp
index 38c1d207565..8123d5f4e85 100644
--- a/lib/tokenize.cpp
+++ b/lib/tokenize.cpp
@@ -3441,7 +3441,7 @@ bool Tokenizer::simplifyTokens1(const std::string &configuration)
mSymbolDatabase->setArrayDimensionsUsingValueFlow();
}
- printDebugOutput(1, std::cout);
+ printDebugOutput(std::cout);
return true;
}
@@ -5890,39 +5890,39 @@ bool Tokenizer::simplifyTokenList1(const char FileName[])
}
//---------------------------------------------------------------------------
-void Tokenizer::printDebugOutput(int simplification, std::ostream &out) const
+// TODO: do not depend on --verbose
+void Tokenizer::printDebugOutput(std::ostream &out) const
{
- const bool debug = (simplification != 1U && mSettings.debugSimplified) ||
- (simplification != 2U && mSettings.debugnormal);
+ if (!list.front())
+ return;
- if (debug && list.front()) {
- const bool xml = (mSettings.outputFormat == Settings::OutputFormat::xml);
+ const bool debug = mSettings.debugSimplified || mSettings.debugnormal || mSettings.debugsymdb || mSettings.debugast || mSettings.debugvalueflow;
+ if (!debug)
+ return;
- if (!xml)
- list.front()->printOut(out, xml, nullptr, list.getFiles());
+ const bool xml = (mSettings.outputFormat == Settings::OutputFormat::xml);
- if (xml)
- {
- out << "" << std::endl;
- list.front()->printOut(out, xml, nullptr, list.getFiles());
- }
+ if (xml)
+ out << "" << std::endl;
- if (mSymbolDatabase) {
- if (xml)
- mSymbolDatabase->printXml(out);
- else if (mSettings.verbose) {
- mSymbolDatabase->printOut("Symbol database");
- }
- }
+ if (mSettings.debugSimplified || mSettings.debugnormal)
+ list.front()->printOut(out, xml, nullptr, list.getFiles());
+
+ if (mSymbolDatabase) {
+ if (xml)
+ mSymbolDatabase->printXml(out);
+ else if (mSettings.debugsymdb || (mSettings.debugnormal && mSettings.verbose))
+ mSymbolDatabase->printOut("Symbol database");
+ }
- if (mSettings.verbose)
- list.front()->printAst(mSettings.verbose, xml, list.getFiles(), out);
+ if (mSettings.debugast || (mSettings.debugnormal && mSettings.verbose))
+ list.front()->printAst(mSettings.verbose, xml, list.getFiles(), out);
+ if (mSettings.debugnormal || mSettings.debugvalueflow)
list.front()->printValueFlow(list.getFiles(), xml, out);
- if (xml)
- out << "" << std::endl;
- }
+ if (xml)
+ out << "" << std::endl;
}
void Tokenizer::dump(std::ostream &out) const
@@ -8077,13 +8077,15 @@ bool Tokenizer::isScopeNoReturn(const Token *endScopeToken, bool *unknown) const
void Tokenizer::syntaxError(const Token *tok, const std::string &code) const
{
- printDebugOutput(0, std::cout);
+ if (mSettings.debugSimplified || mSettings.debugnormal)
+ printDebugOutput(std::cout);
throw InternalError(tok, code.empty() ? "syntax error" : "syntax error: " + code, InternalError::SYNTAX);
}
void Tokenizer::unmatchedToken(const Token *tok) const
{
- printDebugOutput(0, std::cout);
+ if (mSettings.debugSimplified || mSettings.debugnormal)
+ printDebugOutput(std::cout);
throw InternalError(tok,
"Unmatched '" + tok->str() + "'. Configuration: '" + mConfiguration + "'.",
InternalError::SYNTAX);
@@ -8091,13 +8093,15 @@ void Tokenizer::unmatchedToken(const Token *tok) const
void Tokenizer::syntaxErrorC(const Token *tok, const std::string &what) const
{
- printDebugOutput(0, std::cout);
+ if (mSettings.debugSimplified || mSettings.debugnormal)
+ printDebugOutput(std::cout);
throw InternalError(tok, "Code '"+what+"' is invalid C code.", "Use --std, -x or --language to enforce C++. Or --cpp-header-probe to identify C++ headers via the Emacs marker.", InternalError::SYNTAX);
}
void Tokenizer::unknownMacroError(const Token *tok1) const
{
- printDebugOutput(0, std::cout);
+ if (mSettings.debugSimplified || mSettings.debugnormal)
+ printDebugOutput(std::cout);
throw InternalError(tok1, "There is an unknown macro here somewhere. Configuration is required. If " + tok1->str() + " is a macro then please configure it.", InternalError::UNKNOWN_MACRO);
}
@@ -8131,7 +8135,7 @@ void Tokenizer::invalidConstFunctionTypeError(const Token *tok) const
void Tokenizer::cppcheckError(const Token *tok) const
{
- printDebugOutput(0, std::cout);
+ printDebugOutput(std::cout);
throw InternalError(tok, "Analysis failed. If the code is valid then please report this failure.", InternalError::INTERNAL);
}
diff --git a/lib/tokenize.h b/lib/tokenize.h
index 0f0363354e9..a5b34fa091f 100644
--- a/lib/tokenize.h
+++ b/lib/tokenize.h
@@ -558,11 +558,8 @@ class CPPCHECKLIB Tokenizer {
void createSymbolDatabase();
/** print --debug output if debug flags match the simplification:
- * 0=unknown/both simplifications
- * 1=1st simplifications
- * 2=2nd simplifications
*/
- void printDebugOutput(int simplification, std::ostream &out) const;
+ void printDebugOutput(std::ostream &out) const;
void dump(std::ostream &out) const;
diff --git a/test/cli/clang-import_test.py b/test/cli/clang-import_test.py
index 74136453044..f55f80f0837 100644
--- a/test/cli/clang-import_test.py
+++ b/test/cli/clang-import_test.py
@@ -47,8 +47,8 @@ def __check_symbol_database(tmpdir, code):
testfile = os.path.join(tmpdir, 'test.cpp')
with open(testfile, 'w+t') as f:
f.write(code)
- ret1, stdout1, _ = cppcheck(['--clang', '--debug', '-v', testfile])
- ret2, stdout2, _ = cppcheck(['--debug', '-v', testfile])
+ ret1, stdout1, _ = cppcheck(['--clang', '--debug-symdb', testfile])
+ ret2, stdout2, _ = cppcheck(['--debug-symdb', testfile])
assert 0 == ret1, stdout1
assert 0 == ret2, stdout2
assert __get_debug_section('### Symbol database', stdout1) == __get_debug_section('### Symbol database', stdout2)
@@ -58,8 +58,8 @@ def __check_ast(tmpdir, code):
testfile = os.path.join(tmpdir, 'test.cpp')
with open(testfile, 'w+t') as f:
f.write(code)
- ret1, stdout1, _ = cppcheck(['--clang', '--debug', '-v', testfile])
- ret2, stdout2, _ = cppcheck(['--debug', '-v', testfile])
+ ret1, stdout1, _ = cppcheck(['--clang', '--debug-ast', testfile])
+ ret2, stdout2, _ = cppcheck(['--debug-ast', testfile])
assert 0 == ret1, stdout1
assert 0 == ret2, stdout1
assert __get_debug_section('##AST', stdout1) == __get_debug_section('##AST', stdout2)
diff --git a/test/cli/other_test.py b/test/cli/other_test.py
index ac1c303a804..0d792b90b98 100644
--- a/test/cli/other_test.py
+++ b/test/cli/other_test.py
@@ -3033,32 +3033,47 @@ def test_debug_verbose_xml(tmp_path):
# TODO: test with --xml
-def test_debug_template(tmp_path):
+def __test_debug_template(tmp_path, verbose):
test_file = tmp_path / 'test.cpp'
with open(test_file, "w") as f:
f.write(
"""template class TemplCl;
-void f
+void f()
{
- (void)*((int*)0);
+ (void)*((int*)nullptr);
}
""")
args = [
'-q',
- '--debug', # TODO: remove depdency on this
+ '--template=simple',
'--debug-template',
str(test_file)
]
+ if verbose:
+ args += ['--verbose']
+
exitcode, stdout, stderr = cppcheck(args)
assert exitcode == 0, stdout
- assert stdout.find('##file ') != -1
- assert stdout.find('##Value flow') != -1
+ assert stdout.find('##file ') == -1
+ assert stdout.find('##Value flow') == -1
assert stdout.find('### Symbol database ###') == -1
assert stdout.find('##AST') == -1
assert stdout.find('### Template Simplifier pass ') != -1
- assert stderr.splitlines() == []
+ assert stderr.splitlines() == [
+ '{}:4:13: error: Null pointer dereference: (int*)nullptr [nullPointer]'.format(test_file)
+ ]
+ return stdout
+
+
+def test_debug_template(tmp_path):
+ __test_debug_template(tmp_path, False)
+
+
+def test_debug_template_verbose_nodiff(tmp_path):
+ # make sure --verbose does not change the output
+ assert __test_debug_template(tmp_path, False) == __test_debug_template(tmp_path, True)
def test_file_ignore_2(tmp_path): # #13570
@@ -3087,7 +3102,7 @@ def test_file_ignore_2(tmp_path): # #13570
assert stderr.splitlines() == []
-def test_debug_valueflow(tmp_path):
+def test_debug_valueflow_data(tmp_path):
test_file = tmp_path / 'test.c'
with open(test_file, "w") as f:
f.write(
@@ -3100,7 +3115,7 @@ def test_debug_valueflow(tmp_path):
args = [
'-q',
- '--debug', # TODO: limit to valueflow output
+ '--debug-valueflow',
str(test_file)
]
@@ -3108,7 +3123,7 @@ def test_debug_valueflow(tmp_path):
assert exitcode == 0, stdout
# check sections in output
- assert stdout.find('##file ') != -1
+ assert stdout.find('##file ') == -1
assert stdout.find('##Value flow') != -1
assert stdout.find('### Symbol database ###') == -1
assert stdout.find('##AST') == -1
@@ -3130,7 +3145,7 @@ def test_debug_valueflow(tmp_path):
]
-def test_debug_valueflow_xml(tmp_path): # #13606
+def test_debug_valueflow_data_xml(tmp_path): # #13606
test_file = tmp_path / 'test.c'
with open(test_file, "w") as f:
f.write(
@@ -3143,7 +3158,7 @@ def test_debug_valueflow_xml(tmp_path): # #13606
args = [
'-q',
- '--debug', # TODO: limit to valueflow output
+ '--debug-valueflow',
'--xml',
str(test_file)
]
@@ -3155,7 +3170,7 @@ def test_debug_valueflow_xml(tmp_path): # #13606
assert ElementTree.fromstring(stderr) is not None
# check sections in output
- assert stdout.find('##file ') != -1 # also exists in CDATA
+ assert stdout.find('##file ') == -1
assert stdout.find('##Value flow') == -1
assert stdout.find('### Symbol database ###') == -1
assert stdout.find('##AST') == -1
@@ -3165,8 +3180,6 @@ def test_debug_valueflow_xml(tmp_path): # #13606
debug_xml = ElementTree.fromstring(stdout)
assert debug_xml is not None
assert debug_xml.tag == 'debug'
- file_elem = debug_xml.findall('file')
- assert len(file_elem) == 1
valueflow_elem = debug_xml.findall('valueflow')
assert len(valueflow_elem) == 1
scopes_elem = debug_xml.findall('scopes')
@@ -3608,3 +3621,258 @@ def test_preprocess_enforced_cpp(tmp_path): # #10989
assert stderr.splitlines() == [
'{}:2:2: error: #error "err" [preprocessorErrorDirective]'.format(test_file)
]
+
+
+# TODO: test with --xml
+def __test_debug_normal(tmp_path, verbose):
+ test_file = tmp_path / 'test.c'
+ with open(test_file, "w") as f:
+ f.write(
+"""void f()
+{
+ (void)*((int*)0);
+}
+""")
+
+ args = [
+ '-q',
+ '--template=simple',
+ '--debug-normal',
+ str(test_file)
+ ]
+
+ if verbose:
+ args += ['--verbose']
+
+ exitcode, stdout, stderr = cppcheck(args)
+ assert exitcode == 0, stdout
+ assert stdout.find('##file ') != -1
+ assert stdout.find('##Value flow') != -1
+ if verbose:
+ assert stdout.find('### Symbol database ###') != -1
+ else:
+ assert stdout.find('### Symbol database ###') == -1
+ if verbose:
+ assert stdout.find('##AST') != -1
+ else:
+ assert stdout.find('##AST') == -1
+ assert stdout.find('### Template Simplifier pass ') == -1
+ assert stderr.splitlines() == [
+ '{}:3:13: error: Null pointer dereference: (int*)0 [nullPointer]'.format(test_file)
+ ]
+ return stdout
+
+
+def test_debug_normal(tmp_path):
+ __test_debug_normal(tmp_path, False)
+
+
+@pytest.mark.xfail(strict=True) # TODO: remove dependency on --verbose
+def test_debug_normal_verbose_nodiff(tmp_path):
+ # make sure --verbose does not change the output
+ assert __test_debug_normal(tmp_path, False) == __test_debug_normal(tmp_path, True)
+
+
+# TODO: test with --xml
+def __test_debug_simplified(tmp_path, verbose):
+ test_file = tmp_path / 'test.c'
+ with open(test_file, "w") as f:
+ f.write(
+"""void f()
+{
+ (void)*((int*)0);
+}
+""")
+
+ args = [
+ '-q',
+ '--template=simple',
+ '--debug-simplified',
+ str(test_file)
+ ]
+
+ if verbose:
+ args += ['--verbose']
+
+ exitcode, stdout, stderr = cppcheck(args)
+ assert exitcode == 0, stdout
+ assert stdout.find('##file ') != -1
+ assert stdout.find('##Value flow') == -1
+ assert stdout.find('### Symbol database ###') == -1
+ assert stdout.find('##AST') == -1
+ assert stdout.find('### Template Simplifier pass ') == -1
+ assert stderr.splitlines() == [
+ '{}:3:13: error: Null pointer dereference: (int*)0 [nullPointer]'.format(test_file)
+ ]
+ return stdout
+
+
+def test_debug_simplified(tmp_path):
+ __test_debug_simplified(tmp_path, False)
+
+
+def test_debug_simplified_verbose_nodiff(tmp_path):
+ # make sure --verbose does not change the output
+ assert __test_debug_simplified(tmp_path, False) == __test_debug_simplified(tmp_path, True)
+
+
+# TODO: test with --xml
+def __test_debug_symdb(tmp_path, verbose):
+ test_file = tmp_path / 'test.c'
+ with open(test_file, "w") as f:
+ f.write(
+"""void f()
+{
+ (void)*((int*)0);
+}
+""")
+
+ args = [
+ '-q',
+ '--template=simple',
+ '--debug-symdb',
+ str(test_file)
+ ]
+
+ if verbose:
+ args += ['--verbose']
+
+ exitcode, stdout, stderr = cppcheck(args)
+ assert exitcode == 0, stdout
+ assert stdout.find('##file ') == -1
+ assert stdout.find('##Value flow') == -1
+ assert stdout.find('### Symbol database ###') != -1
+ assert stdout.find('##AST') == -1
+ assert stdout.find('### Template Simplifier pass ') == -1
+ assert stderr.splitlines() == [
+ '{}:3:13: error: Null pointer dereference: (int*)0 [nullPointer]'.format(test_file)
+ ]
+ return stdout
+
+
+def test_debug_symdb(tmp_path):
+ __test_debug_symdb(tmp_path, False)
+
+
+@pytest.mark.skip # TODO: this contains memory addresses the output will always differ - would require stable identifier
+def test_debug_symdb_verbose_nodiff(tmp_path):
+ # make sure --verbose does not change the output
+ assert __test_debug_symdb(tmp_path, False) == __test_debug_symdb(tmp_path, True)
+
+
+# TODO: test with --xml
+def __test_debug_ast(tmp_path, verbose):
+ test_file = tmp_path / 'test.c'
+ with open(test_file, "w") as f:
+ f.write(
+"""void f()
+{
+ (void)*((int*)0);
+}
+""")
+
+ args = [
+ '-q',
+ '--template=simple',
+ '--debug-ast',
+ str(test_file)
+ ]
+
+ if verbose:
+ args += ['--verbose']
+
+ exitcode, stdout, stderr = cppcheck(args)
+ assert exitcode == 0, stdout
+ assert stdout.find('##file ') == -1
+ assert stdout.find('##Value flow') == -1
+ assert stdout.find('### Symbol database ###') == -1
+ assert stdout.find('##AST') != -1
+ assert stdout.find('### Template Simplifier pass ') == -1
+ assert stderr.splitlines() == [
+ '{}:3:13: error: Null pointer dereference: (int*)0 [nullPointer]'.format(test_file)
+ ]
+ return stdout
+
+
+def test_debug_ast(tmp_path):
+ __test_debug_ast(tmp_path, False)
+
+
+@pytest.mark.xfail(strict=True) # TODO: remove dependency on --verbose
+def test_debug_ast_verbose_nodiff(tmp_path):
+ # make sure --verbose does not change the output
+ assert __test_debug_ast(tmp_path, False) == __test_debug_ast(tmp_path, True)
+
+
+# TODO: test with --xml
+def __test_debug_valueflow(tmp_path, verbose):
+ test_file = tmp_path / 'test.c'
+ with open(test_file, "w") as f:
+ f.write(
+"""void f()
+{
+ (void)*((int*)0);
+}
+""")
+
+ args = [
+ '-q',
+ '--template=simple',
+ '--debug-valueflow',
+ str(test_file)
+ ]
+
+ if verbose:
+ args += ['--verbose']
+
+ exitcode, stdout, stderr = cppcheck(args)
+ assert exitcode == 0, stdout
+ assert stdout.find('##file ') == -1
+ assert stdout.find('##Value flow') != -1
+ assert stdout.find('### Symbol database ###') == -1
+ assert stdout.find('##AST') == -1
+ assert stdout.find('### Template Simplifier pass ') == -1
+ assert stderr.splitlines() == [
+ '{}:3:13: error: Null pointer dereference: (int*)0 [nullPointer]'.format(test_file)
+ ]
+ return stdout
+
+
+def test_debug_valueflow(tmp_path):
+ __test_debug_valueflow(tmp_path, False)
+
+
+def test_debug_valueflow_verbose_nodiff(tmp_path):
+ # make sure --verbose does not change the output
+ assert __test_debug_valueflow(tmp_path, False) == __test_debug_valueflow(tmp_path, True)
+
+
+def test_debug_syntaxerror_c(tmp_path):
+ test_file = tmp_path / 'test.c'
+ with open(test_file, "w") as f:
+ f.write(
+"""
+template class TemplCl;
+void f()
+{
+ (void)*((int*)0);
+}
+""")
+
+ args = [
+ '-q',
+ '--template=simple',
+ '--debug-normal',
+ str(test_file)
+ ]
+
+ exitcode, stdout, stderr = cppcheck(args)
+ assert exitcode == 0, stdout
+ assert stdout.find('##file ') != -1
+ assert stdout.find('##Value flow') != -1
+ assert stdout.find('### Symbol database ###') == -1
+ assert stdout.find('##AST') == -1
+ assert stdout.find('### Template Simplifier pass ') == -1
+ assert stderr.splitlines() == [
+ "{}:2:1: error: Code 'template<...' is invalid C code. [syntaxError]".format(test_file)
+ ]
diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp
index fab73b838bb..55c4ba8562f 100644
--- a/test/testcmdlineparser.cpp
+++ b/test/testcmdlineparser.cpp
@@ -455,6 +455,9 @@ class TestCmdlineParser : public TestFixture {
TEST_CASE(analyzeAllVsConfigs);
TEST_CASE(noAnalyzeAllVsConfigs);
TEST_CASE(noAnalyzeAllVsConfigs2);
+ TEST_CASE(debugSymdb);
+ TEST_CASE(debugAst);
+ TEST_CASE(debugValueflow);
TEST_CASE(ignorepaths1);
TEST_CASE(ignorepaths2);
@@ -3106,6 +3109,27 @@ class TestCmdlineParser : public TestFixture {
ASSERT_EQUALS("cppcheck: error: --no-analyze-all-vs-configs has no effect - no Visual Studio project provided.\n", logger->str());
}
+ void debugSymdb() {
+ REDIRECT;
+ const char * const argv[] = {"cppcheck", "--debug-symdb", "file.cpp"};
+ ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv));
+ ASSERT_EQUALS(true, settings->debugsymdb);
+ }
+
+ void debugAst() {
+ REDIRECT;
+ const char * const argv[] = {"cppcheck", "--debug-ast", "file.cpp"};
+ ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv));
+ ASSERT_EQUALS(true, settings->debugast);
+ }
+
+ void debugValueflow() {
+ REDIRECT;
+ const char * const argv[] = {"cppcheck", "--debug-valueflow", "file.cpp"};
+ ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv));
+ ASSERT_EQUALS(true, settings->debugvalueflow);
+ }
+
void ignorepaths1() {
REDIRECT;
const char * const argv[] = {"cppcheck", "-isrc", "file.cpp"};