diff --git a/lib/check.h b/lib/check.h index ce128ee7d25..15d0611a908 100644 --- a/lib/check.h +++ b/lib/check.h @@ -102,11 +102,12 @@ class CPPCHECKLIB Check { /** Base class used for whole-program analysis */ class CPPCHECKLIB FileInfo { public: - FileInfo() = default; + explicit FileInfo(std::string f0 = {}) : file0(std::move(f0)) {} virtual ~FileInfo() = default; virtual std::string toString() const { return std::string(); } + std::string file0; }; virtual FileInfo * getFileInfo(const Tokenizer& /*tokenizer*/, const Settings& /*settings*/) const { diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index a0326a9db1f..e15389cae0a 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -893,6 +893,7 @@ namespace /** data for multifile checking */ class MyFileInfo : public Check::FileInfo { public: + using Check::FileInfo::FileInfo; /** unsafe array index usage */ std::list unsafeArrayIndex; @@ -951,7 +952,7 @@ Check::FileInfo *CheckBufferOverrun::getFileInfo(const Tokenizer &tokenizer, con if (unsafeArrayIndex.empty() && unsafePointerArith.empty()) { return nullptr; } - auto *fileInfo = new MyFileInfo; + auto *fileInfo = new MyFileInfo(tokenizer.list.getFiles()[0]); fileInfo->unsafeArrayIndex = unsafeArrayIndex; fileInfo->unsafePointerArith = unsafePointerArith; return fileInfo; @@ -998,14 +999,15 @@ bool CheckBufferOverrun::analyseWholeProgram(const CTU::FileInfo *ctu, const std if (!fi) continue; for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeArrayIndex) - foundErrors |= analyseWholeProgram1(callsMap, unsafeUsage, 1, errorLogger, settings.maxCtuDepth); + foundErrors |= analyseWholeProgram1(callsMap, unsafeUsage, 1, errorLogger, settings.maxCtuDepth, fi->file0); for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafePointerArith) - foundErrors |= analyseWholeProgram1(callsMap, unsafeUsage, 2, errorLogger, settings.maxCtuDepth); + foundErrors |= analyseWholeProgram1(callsMap, unsafeUsage, 2, errorLogger, settings.maxCtuDepth, fi->file0); } return foundErrors; } -bool CheckBufferOverrun::analyseWholeProgram1(const std::map> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, int type, ErrorLogger &errorLogger, int maxCtuDepth) +bool CheckBufferOverrun::analyseWholeProgram1(const std::map> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, + int type, ErrorLogger &errorLogger, int maxCtuDepth, const std::string& file0) { const CTU::FileInfo::FunctionCall *functionCall = nullptr; @@ -1038,7 +1040,7 @@ bool CheckBufferOverrun::analyseWholeProgram1(const std::map> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, int type, ErrorLogger &errorLogger, int maxCtuDepth); + static bool analyseWholeProgram1(const std::map> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, + int type, ErrorLogger &errorLogger, int maxCtuDepth, const std::string& file0); static std::string myName() { diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index a7c775166ac..69be9cbaa8d 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -3566,6 +3566,7 @@ namespace /* multifile checking; one definition rule violations */ class MyFileInfo : public Check::FileInfo { public: + using Check::FileInfo::FileInfo; struct NameLoc { std::string className; std::string fileName; @@ -3662,7 +3663,7 @@ Check::FileInfo *CheckClass::getFileInfo(const Tokenizer &tokenizer, const Setti if (classDefinitions.empty()) return nullptr; - auto *fileInfo = new MyFileInfo; + auto *fileInfo = new MyFileInfo(tokenizer.list.getFiles()[0]); fileInfo->classDefinitions.swap(classDefinitions); return fileInfo; } @@ -3728,7 +3729,7 @@ bool CheckClass::analyseWholeProgram(const CTU::FileInfo *ctu, const std::listsecond.fileName, it->second.lineNumber, it->second.column); const ErrorMessage errmsg(std::move(locationList), - emptyString, + fi->file0, Severity::error, "$symbol:" + nameLoc.className + "\nThe one definition rule is violated, different classes/structs have the same name '$symbol'", diff --git a/lib/checknullpointer.cpp b/lib/checknullpointer.cpp index 1a19acd4878..dfdafbb5a57 100644 --- a/lib/checknullpointer.cpp +++ b/lib/checknullpointer.cpp @@ -600,6 +600,7 @@ namespace /* data for multifile checking */ class MyFileInfo : public Check::FileInfo { public: + using Check::FileInfo::FileInfo; /** function arguments that are dereferenced without checking if they are null */ std::list unsafeUsage; @@ -617,7 +618,7 @@ Check::FileInfo *CheckNullPointer::getFileInfo(const Tokenizer &tokenizer, const if (unsafeUsage.empty()) return nullptr; - auto *fileInfo = new MyFileInfo; + auto *fileInfo = new MyFileInfo(tokenizer.list.getFiles()[0]); fileInfo->unsafeUsage = unsafeUsage; return fileInfo; } @@ -667,7 +668,7 @@ bool CheckNullPointer::analyseWholeProgram(const CTU::FileInfo *ctu, const std:: continue; const ErrorMessage errmsg(locationList, - emptyString, + fi->file0, warning ? Severity::warning : Severity::error, "Null pointer dereference: " + unsafeUsage.myArgumentName, "ctunullpointer", diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index 541a46c071f..d1587142554 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -1708,6 +1708,7 @@ namespace /* data for multifile checking */ class MyFileInfo : public Check::FileInfo { public: + using Check::FileInfo::FileInfo; /** function arguments that data are unconditionally read */ std::list unsafeUsage; @@ -1725,7 +1726,7 @@ Check::FileInfo *CheckUninitVar::getFileInfo(const Tokenizer &tokenizer, const S if (unsafeUsage.empty()) return nullptr; - auto *fileInfo = new MyFileInfo; + auto *fileInfo = new MyFileInfo(tokenizer.list.getFiles()[0]); fileInfo->unsafeUsage = unsafeUsage; return fileInfo; } @@ -1769,7 +1770,7 @@ bool CheckUninitVar::analyseWholeProgram(const CTU::FileInfo *ctu, const std::li continue; const ErrorMessage errmsg(locationList, - emptyString, + fi->file0, Severity::error, "Using argument " + unsafeUsage.myArgumentName + " that points at uninitialized variable " + functionCall->callArgumentExpression, "ctuuninitvar", diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index a4b9a88445e..3feb0b7debc 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -2041,8 +2041,11 @@ unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const st } // cppcheck-suppress shadowFunction - TODO: fix this for (const Check *check : Check::instances()) { - if (checkClassAttr == check->name()) - fileInfoList.push_back(check->loadFileInfoFromXml(e)); + if (checkClassAttr == check->name()) { + Check::FileInfo* fi = check->loadFileInfoFromXml(e); + fi->file0 = filesTxtLine.substr(firstColon + 2); + fileInfoList.push_back(fi); + } } } } diff --git a/test/cli/whole-program/nullpointer1.cpp b/test/cli/whole-program/nullpointer1.cpp new file mode 100644 index 00000000000..d323e3ad1d3 --- /dev/null +++ b/test/cli/whole-program/nullpointer1.cpp @@ -0,0 +1 @@ +#include "nullpointer1.h" diff --git a/test/cli/whole-program/nullpointer1.h b/test/cli/whole-program/nullpointer1.h new file mode 100644 index 00000000000..56fa6c49d84 --- /dev/null +++ b/test/cli/whole-program/nullpointer1.h @@ -0,0 +1,8 @@ +#include "nullpointer1_1.h" + +template +void f(T* p) { + if (sizeof(T) == 4) + p = nullptr; + g(p); +} diff --git a/test/cli/whole-program/nullpointer1_1.h b/test/cli/whole-program/nullpointer1_1.h new file mode 100644 index 00000000000..cbbc478cf2c --- /dev/null +++ b/test/cli/whole-program/nullpointer1_1.h @@ -0,0 +1,4 @@ +template +void g(T* p) { + *p = 0; +}; diff --git a/test/cli/whole-program_test.py b/test/cli/whole-program_test.py index c8115f06d19..f45966e9389 100644 --- a/test/cli/whole-program_test.py +++ b/test/cli/whole-program_test.py @@ -4,6 +4,7 @@ import json import shutil from testutils import cppcheck +import xml.etree.ElementTree as ET __script_dir = os.path.dirname(os.path.abspath(__file__)) @@ -358,3 +359,35 @@ def test_checkclass_project_builddir_j(tmpdir): build_dir = os.path.join(tmpdir, 'b1') os.mkdir(build_dir) __test_checkclass_project(tmpdir, ['-j2', '--cppcheck-build-dir={}'.format(build_dir)]) + +def __test_nullpointer_file0(extra_args): + args = [ + '-q', + '--xml', + '--error-exitcode=1', + 'whole-program/nullpointer1.cpp' + ] + + args += extra_args + + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + results = ET.fromstring(stderr) + file0 = '' + for e in results.findall('errors/error[@id="ctunullpointer"]'): + file0 = e.attrib['file0'] + + assert ret == 1, stdout if stdout else stderr + assert stdout == '' + assert file0 == 'whole-program/nullpointer1.cpp', stderr + +def test_nullpointer_file0(): + __test_nullpointer_file0(['-j1']) + +@pytest.mark.xfail(strict=True) # no CTU without builddir +def test_nullpointer_file0_j(): + __test_nullpointer_file0(['-j2', '--no-cppcheck-build-dir']) + +def test_nullpointer_file0_builddir_j(tmpdir): + build_dir = os.path.join(tmpdir, 'b1') + os.mkdir(build_dir) + __test_nullpointer_file0(['-j2', '--cppcheck-build-dir={}'.format(build_dir)])