diff --git a/Makefile b/Makefile index a7f3feefba6..5810fcc9aa3 100644 --- a/Makefile +++ b/Makefile @@ -792,7 +792,7 @@ test/testcondition.o: test/testcondition.cpp lib/check.h lib/checkcondition.h li test/testconstructors.o: test/testconstructors.cpp lib/check.h lib/checkclass.h lib/checkers.h lib/checkimpl.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h $(CXX) ${INCLUDE_FOR_TEST} ${CFLAGS_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testconstructors.cpp -test/testcppcheck.o: test/testcppcheck.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h test/redirect.h +test/testcppcheck.o: test/testcppcheck.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} ${CFLAGS_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testcppcheck.cpp test/testerrorlogger.o: test/testerrorlogger.cpp externals/tinyxml2/tinyxml2.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/xml.h test/fixture.h test/helpers.h diff --git a/lib/analyzerinfo.cpp b/lib/analyzerinfo.cpp index 36803b897b1..aa8d82c27fa 100644 --- a/lib/analyzerinfo.cpp +++ b/lib/analyzerinfo.cpp @@ -58,6 +58,68 @@ void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std:: fout << getFilesTxt(sourcefiles, fileSettings); } +void AnalyzerInformation::writeIncludes(const std::set &files) +{ + if (!files.empty() && mOutputStream.is_open()) { + mOutputStream << " \n"; + for (const std::string &file : files) { + mOutputStream << " " << file << "\n"; + } + mOutputStream << " \n"; + } +} + +std::set AnalyzerInformation::getIncludes(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t fsFileId) const +{ + if (mOutputStream.is_open()) + throw std::runtime_error("analyzer information file is already open"); + + std::set files; + + if (buildDir.empty() || sourcefile.empty()) + return files; + + const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir, sourcefile, cfg, fsFileId); + + tinyxml2::XMLDocument analyzerInfoDoc; + if (analyzerInfoDoc.LoadFile(analyzerInfoFile.c_str()) != tinyxml2::XML_SUCCESS) + return files; + + const tinyxml2::XMLElement *const rootNode = analyzerInfoDoc.FirstChildElement(); + if (rootNode == nullptr) + return files; + + if (strcmp(rootNode->Name(), "analyzerinfo") != 0) + return files; + + const tinyxml2::XMLElement *includesNode = nullptr; + for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (strcmp(e->Name(), "includes") == 0) { + includesNode = e; + break; + } + } + + if (includesNode == nullptr) + return files; + + for (const tinyxml2::XMLElement *e = includesNode->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (strcmp(e->Name(), "filename") != 0) + continue; + + files.insert(e->GetText()); + } + + return files; +} + +void AnalyzerInformation::writeHash(std::size_t hash) +{ + if (mOutputStream.is_open()) { + mOutputStream << " " << hash << "\n"; + } +} + std::string AnalyzerInformation::getFilesTxt(const std::list &sourcefiles, const std::list &fileSettings) { std::ostringstream ret; @@ -94,10 +156,17 @@ std::string AnalyzerInformation::skipAnalysis(const tinyxml2::XMLDocument &analy if (strcmp(rootNode->Name(), "analyzerinfo") != 0) return "unexpected root node"; - const char * const attr = rootNode->Attribute("hash"); - if (!attr) - return "no 'hash' attribute found"; - if (attr != std::to_string(hash)) + const tinyxml2::XMLElement *hashNode = nullptr; + for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (strcmp(e->Name(), "hash") == 0) { + hashNode = e; + break; + } + } + + if (!hashNode) + return "no 'hash' node found"; + if (hashNode->GetText() != std::to_string(hash)) return "hash mismatch"; for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { @@ -194,7 +263,7 @@ bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::st if (!mOutputStream.is_open()) throw std::runtime_error("failed to open '" + analyzerInfoFile + "'"); mOutputStream << "\n"; - mOutputStream << "\n"; + mOutputStream << "\n"; return true; } diff --git a/lib/analyzerinfo.h b/lib/analyzerinfo.h index 75674a22f82..75ff519db84 100644 --- a/lib/analyzerinfo.h +++ b/lib/analyzerinfo.h @@ -27,6 +27,7 @@ #include #include #include +#include #include class ErrorMessage; @@ -67,6 +68,9 @@ class CPPCHECKLIB AnalyzerInformation { bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t fsFileId, std::size_t hash, std::list &errors, bool debug = false); void reportErr(const ErrorMessage &msg); void setFileInfo(const std::string &check, const std::string &fileInfo); + void writeIncludes(const std::set &files); + std::set getIncludes(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t fsFileId) const; + void writeHash(std::size_t hash); static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t fsFileId); void reopen(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t fsFileId); diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 3c96bec64a3..42af3627f02 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -1026,25 +1026,6 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str preprocessor.inlineSuppressions(mSuppressions.nomsg); preprocessor.removeComments(); - if (!mSettings.buildDir.empty()) { - analyzerInformation.reset(new AnalyzerInformation); - mLogger->setAnalyzerInfo(analyzerInformation.get()); - } - - if (analyzerInformation) { - // Calculate hash so it can be compared with old hash / future hashes - const std::size_t hash = calculateHash(preprocessor, file.spath()); - std::list errors; - if (!analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, file.fsFileId(), hash, errors, mSettings.debugainfo)) { - while (!errors.empty()) { - mErrorLogger.reportErr(errors.front()); - errors.pop_front(); - } - mLogger->setAnalyzerInfo(nullptr); - return mLogger->exitcode(); // known results => no need to reanalyze file - } - } - // Get directives std::list directives; preprocessor.createDirectives(directives); @@ -1062,7 +1043,13 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str std::inserter(configDefines, configDefines.end()), getDefineName); + // Keep track of all included files when using build dir + std::set includedFiles; + preprocessor.setLoadCallback([&](simplecpp::FileData &data, bool loaded) { + if (analyzerInformation) { + includedFiles.insert(data.filename); + } if (loaded) { // Do preprocessing on included file mLogger->addRemarkComments(preprocessor.getRemarkComments(data.tokens)); @@ -1078,12 +1065,37 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str preprocessor.setPlatformInfo(); + if (!mSettings.buildDir.empty()) { + analyzerInformation.reset(new AnalyzerInformation); + mLogger->setAnalyzerInfo(analyzerInformation.get()); + } + + if (analyzerInformation) { + // Load all included files to get correct hashes and suppressions + for (const std::string &filename : analyzerInformation->getIncludes(mSettings.buildDir, file.spath(), cfgname, file.fsFileId())) + preprocessor.loadFile(files, filename); + // Calculate hash so it can be compared with old hash / future hashes + const std::size_t hash = calculateHash(preprocessor, file.spath()); + std::list errors; + if (!analyzerInformation->analyzeFile(mSettings.buildDir, file.spath(), cfgname, file.fsFileId(), hash, errors, mSettings.debugainfo)) { + while (!errors.empty()) { + mErrorLogger.reportErr(errors.front()); + errors.pop_front(); + } + mLogger->setAnalyzerInfo(nullptr); + return mLogger->exitcode(); // known results => no need to reanalyze file + } + // Clear included file list; we don't want to keep includes that have been removed from the source + // Any includes that are still present will be readded + includedFiles.clear(); + } + // Get configurations.. if (maxConfigs > 1) { Timer::run("Preprocessor::getConfigs", mTimerResults, [&]() { configurations = { "" }; preprocessor.getConfigs(configDefines, configurations); - preprocessor.loadFiles(files); + preprocessor.loadAllIncludes(files); }); } else { configurations = { mSettings.userDefines }; @@ -1306,6 +1318,11 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str mLogger->setPlistFilenames(std::move(files)); } + if (analyzerInformation) { + analyzerInformation->writeIncludes(includedFiles); + analyzerInformation->writeHash(calculateHash(preprocessor, file.spath())); + } + executeAddons(dumpFile, file); } catch (const TerminateException &) { // Analysis is terminated diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 11bf0c376a8..1289e6e6510 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -833,7 +833,7 @@ const simplecpp::Output* Preprocessor::handleErrors(const simplecpp::OutputList& return reportOutput(outputList, showerror); } -bool Preprocessor::loadFiles(std::vector &files) +bool Preprocessor::loadAllIncludes(std::vector &files) { const simplecpp::DUI dui = createDUI(mSettings, "", mLang); @@ -842,6 +842,13 @@ bool Preprocessor::loadFiles(std::vector &files) return !handleErrors(outputList); } +simplecpp::FileData *Preprocessor::loadFile(std::vector &files, const std::string &file) +{ + const simplecpp::DUI dui = createDUI(mSettings, "", mLang); + + return mFileCache.get("", file, dui, false, files, nullptr).first; +} + void Preprocessor::removeComments() { removeComments(mTokens); diff --git a/lib/preprocessor.h b/lib/preprocessor.h index 3a5a8393a3e..91a0b7e7d37 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -122,7 +122,9 @@ class CPPCHECKLIB WARN_UNUSED Preprocessor { std::vector getRemarkComments(const simplecpp::TokenList &tokens) const; - bool loadFiles(std::vector &files); + bool loadAllIncludes(std::vector &files); + + simplecpp::FileData *loadFile(std::vector &files, const std::string &file); void removeComments(); diff --git a/test/cli/inline-suppress_test.py b/test/cli/inline-suppress_test.py index ac3dc643485..065a59f5067 100644 --- a/test/cli/inline-suppress_test.py +++ b/test/cli/inline-suppress_test.py @@ -248,6 +248,30 @@ def test_build_dir(tmpdir): assert stdout == '' assert ret == 0, stdout + +def test_build_dir_include(tmpdir): + args = [ + '-q', + '--template=simple', + '--cppcheck-build-dir={}'.format(tmpdir), + '--enable=all', + '--inline-suppr', + '{}5.cpp'.format(__proj_inline_suppres_path) + ] + + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + lines = stderr.splitlines() + assert lines == [] + assert stdout == '' + assert ret == 0, stdout + + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + lines = stderr.splitlines() + assert lines == [] + assert stdout == '' + assert ret == 0, stdout + + def test_build_dir_jobs_suppressions(tmpdir): #14064 args = [ '-q', diff --git a/test/cli/other_test.py b/test/cli/other_test.py index 4d4dbec7b0a..567b214ede1 100644 --- a/test/cli/other_test.py +++ b/test/cli/other_test.py @@ -2320,7 +2320,7 @@ def test_builddir_hash_check_level(tmp_path): # #13376 cache_file = (build_dir / 'test.a1') root = ElementTree.fromstring(cache_file.read_text()) - hash_1 = root.get('hash') + hash_1 = root.findtext('hash') args += ['--check-level=exhaustive'] @@ -2329,7 +2329,7 @@ def test_builddir_hash_check_level(tmp_path): # #13376 assert stderr == '' root = ElementTree.fromstring(cache_file.read_text()) - hash_2 = root.get('hash') + hash_2 = root.findtext('hash') assert hash_1 != hash_2 @@ -4531,17 +4531,17 @@ def run_and_assert_cppcheck(stdout_exp): "discarding cached result from '{}' for '{}' - unexpected root node".format(test_a1_file_s, test_file_s) ]) - # missing 'hash' attribute + # missing 'hash' node with open(test_a1_file, 'w') as f: f.write('') run_and_assert_cppcheck([ - "discarding cached result from '{}' for '{}' - no 'hash' attribute found".format(test_a1_file_s, test_file_s) + "discarding cached result from '{}' for '{}' - no 'hash' node found".format(test_a1_file_s, test_file_s) ]) - # invalid 'hash' attribute + # invalid 'hash' node with open(test_a1_file, 'w') as f: - f.write('') + f.write('hash') run_and_assert_cppcheck([ "discarding cached result from '{}' for '{}' - hash mismatch".format(test_a1_file_s, test_file_s) diff --git a/test/cli/premium_test.py b/test/cli/premium_test.py index ddf7b1e2fbe..820f7b680e9 100644 --- a/test/cli/premium_test.py +++ b/test/cli/premium_test.py @@ -88,11 +88,11 @@ def test_build_dir_hash_cppcheck_product(tmpdir): assert exitcode == 0 def _get_hash(s:str): - i = s.find(' hash="') + i = s.find('') if i <= -1: return '' i += 7 - return s[i:s.find('"', i)] + return s[i:s.find('', i)] with open(build_dir.join('test.a1'), 'rt') as f: f1 = f.read() diff --git a/test/cli/proj-inline-suppress/5.cpp b/test/cli/proj-inline-suppress/5.cpp new file mode 100644 index 00000000000..c2c717ba8b3 --- /dev/null +++ b/test/cli/proj-inline-suppress/5.cpp @@ -0,0 +1 @@ +#include "5.h" diff --git a/test/cli/proj-inline-suppress/5.h b/test/cli/proj-inline-suppress/5.h new file mode 100644 index 00000000000..63967f45e28 --- /dev/null +++ b/test/cli/proj-inline-suppress/5.h @@ -0,0 +1,5 @@ +struct expected +{ + // cppcheck-suppress noExplicitConstructor + expected(int){} +}; diff --git a/test/helpers.cpp b/test/helpers.cpp index 95a2dfae24b..06ad0c4c298 100644 --- a/test/helpers.cpp +++ b/test/helpers.cpp @@ -121,7 +121,7 @@ void SimpleTokenizer2::preprocess(const char* code, std::size_t size, std::vecto simplecpp::TokenList tokens1({code, size}, files, file0, dui, &outputList); Preprocessor preprocessor(tokens1, tokenizer.getSettings(), errorlogger, Path::identify(tokens1.getFiles()[0], false)); - (void)preprocessor.loadFiles(files); // TODO: check result + (void)preprocessor.loadAllIncludes(files); // TODO: check result simplecpp::TokenList tokens2 = preprocessor.preprocess("", files, outputList); (void)preprocessor.reportOutput(outputList, true); diff --git a/test/helpers.h b/test/helpers.h index 5a0bd106812..b91fe416045 100644 --- a/test/helpers.h +++ b/test/helpers.h @@ -27,6 +27,7 @@ #include "tokenlist.h" #include +#include #include #include #include diff --git a/test/testanalyzerinformation.cpp b/test/testanalyzerinformation.cpp index 0936dbbf3f2..2174f7d4676 100644 --- a/test/testanalyzerinformation.cpp +++ b/test/testanalyzerinformation.cpp @@ -118,7 +118,8 @@ class TestAnalyzerInformation : public TestFixture { const tinyxml2::XMLError xmlError = doc.Parse( "" - "" + "" + "100" "" "" "" @@ -137,7 +138,8 @@ class TestAnalyzerInformation : public TestFixture { const tinyxml2::XMLError xmlError = doc.Parse( "" - "" + "" + "100" "" "" "" @@ -156,7 +158,8 @@ class TestAnalyzerInformation : public TestFixture { const tinyxml2::XMLError xmlError = doc.Parse( "" - "" + "" + "100" "" "" "" @@ -175,7 +178,8 @@ class TestAnalyzerInformation : public TestFixture { const tinyxml2::XMLError xmlError = doc.Parse( "" - "" + "" + "100" "" "" "" @@ -196,7 +200,8 @@ class TestAnalyzerInformation : public TestFixture { const tinyxml2::XMLError xmlError = doc.Parse( "" - "" + "" + "100" "" ); ASSERT_EQUALS(tinyxml2::XML_SUCCESS, xmlError); @@ -212,7 +217,8 @@ class TestAnalyzerInformation : public TestFixture { const tinyxml2::XMLError xmlError = doc.Parse( "" - "" + "" + "100" "" "" "" @@ -253,7 +259,7 @@ class TestAnalyzerInformation : public TestFixture { ASSERT_EQUALS(0, errorList.size()); } - // No 'hash' attribute found + // No 'hash' node found { std::list errorList; tinyxml2::XMLDocument doc; @@ -264,7 +270,7 @@ class TestAnalyzerInformation : public TestFixture { ); ASSERT_EQUALS(tinyxml2::XML_SUCCESS, xmlError); - ASSERT_EQUALS("no 'hash' attribute found", AnalyzerInformationTest::skipAnalysis(doc, 99, errorList)); + ASSERT_EQUALS("no 'hash' node found", AnalyzerInformationTest::skipAnalysis(doc, 99, errorList)); ASSERT_EQUALS(0, errorList.size()); } } diff --git a/test/testcppcheck.cpp b/test/testcppcheck.cpp index ffd4d387cfc..d88daceb0d9 100644 --- a/test/testcppcheck.cpp +++ b/test/testcppcheck.cpp @@ -17,6 +17,7 @@ */ #include "addoninfo.h" +#include "analyzerinfo.h" #include "color.h" #include "cppcheck.h" #include "errorlogger.h" @@ -42,6 +43,22 @@ #include +namespace { + class FilesDeleter { + public: + FilesDeleter() = default; + ~FilesDeleter() { + for (const std::string& fileName: mFilenames) + std::remove(fileName.c_str()); + } + void addFile(const std::string& fileName) { + mFilenames.push_back(fileName); + } + private: + std::vector mFilenames; + }; +} + class TestCppcheck : public TestFixture { public: TestCppcheck() : TestFixture("TestCppcheck") {} @@ -84,6 +101,7 @@ class TestCppcheck : public TestFixture { TEST_CASE(checkPlistOutput); TEST_CASE(premiumResultsCache); TEST_CASE(purgedConfiguration); + TEST_CASE(recheckInclude); } void getErrorMessages() const { @@ -573,7 +591,7 @@ class TestCppcheck : public TestFixture { simplecpp::TokenList tokens(code, files, "m1.c"); Preprocessor preprocessor(tokens, settings, errorLogger, Standards::Language::C); - ASSERT(preprocessor.loadFiles(files)); + ASSERT(preprocessor.loadAllIncludes(files)); AddonInfo premiumaddon; premiumaddon.name = "premiumaddon.json"; @@ -621,6 +639,124 @@ class TestCppcheck : public TestFixture { it->toString(false, templateFormat, "")); } + void recheckInclude() const + { + const auto settings = dinit(Settings, + $.templateFormat = templateFormat, + $.debugainfo = true, + $.buildDir = "test-build-dir"); + + ScopedFile build_dir("empty", "", "test-build-dir"); + + FilesDeleter filesDeleter; + filesDeleter.addFile("test-build-dir/files.txt"); + filesDeleter.addFile("test-build-dir/test.a1"); + filesDeleter.addFile("test-build-dir/test.s1"); + + AnalyzerInformation::writeFilesTxt(settings.buildDir, {"test.cpp"}, {}); + + // First check + { + REDIRECT; + + ScopedFile source_file("test.cpp", + "#include \"test1.h\"\n" + "#include \"test2.h\"\n"); + ScopedFile header1_file("test1.h", + "class TestClass1 {};\n"); + ScopedFile header2_file("test2.h", + "class TestClass2 {};\n"); + + Suppressions suppressions; + ErrorLogger2 errorLogger; + CppCheck cppcheck(settings, suppressions, errorLogger, nullptr, false, {}); + + const auto ret = cppcheck.check(FileWithDetails(source_file.path(), Standards::Language::CPP, 0)); + const auto out = GET_REDIRECT_OUTPUT; + + ASSERT_EQUALS(0, ret); + ASSERT_EQUALS("no cached result 'test-build-dir/test.a1' for 'test.cpp' found\n", out); + } + + // No change, don't recheck + { + REDIRECT; + + ScopedFile source_file("test.cpp", + "#include \"test1.h\"\n" + "#include \"test2.h\"\n"); + ScopedFile header1_file("test1.h", + "class TestClass1 {};\n"); + ScopedFile header2_file("test2.h", + "class TestClass2 {};\n"); + + Suppressions suppressions; + ErrorLogger2 errorLogger; + CppCheck cppcheck(settings, suppressions, errorLogger, nullptr, false, {}); + + const auto ret = cppcheck.check(FileWithDetails(source_file.path(), Standards::Language::CPP, 0)); + const auto out = GET_REDIRECT_OUTPUT; + + ASSERT_EQUALS(0, ret); + ASSERT_EQUALS("skipping analysis - loaded 0 cached finding(s) from 'test-build-dir/test.a1' for 'test.cpp'\n", out); + } + + // Header changed, recheck + { + REDIRECT; + + ScopedFile source_file("test.cpp", + "#include \"test1.h\"\n" + "#include \"test2.h\"\n"); + ScopedFile header1_file("test1.h", + "class TestClass1 {};\n"); + ScopedFile header2_file("test2.h", + "class TestClass2 { int a; };\n"); + + Suppressions suppressions; + ErrorLogger2 errorLogger; + CppCheck cppcheck(settings, suppressions, errorLogger, nullptr, false, {}); + + const auto ret = cppcheck.check(FileWithDetails(source_file.path(), Standards::Language::CPP, 0)); + const auto out = GET_REDIRECT_OUTPUT; + + ASSERT_EQUALS(0, ret); + ASSERT_EQUALS("discarding cached result from 'test-build-dir/test.a1' for 'test.cpp' - hash mismatch\n", out); + } + + // Source changed, recheck + { + REDIRECT; + + // Inclusion removed + ScopedFile source_file("test.cpp", + "#include \"test1.h\"\n"); + ScopedFile header1_file("test1.h", + "class TestClass1 {};\n"); + ScopedFile header2_file("test2.h", + "class TestClass2 { int a; };\n"); + + Suppressions suppressions; + ErrorLogger2 errorLogger; + CppCheck cppcheck(settings, suppressions, errorLogger, nullptr, false, {}); + + const auto ret = cppcheck.check(FileWithDetails(source_file.path(), Standards::Language::CPP, 0)); + const auto out = GET_REDIRECT_OUTPUT; + + ASSERT_EQUALS(0, ret); + ASSERT_EQUALS("discarding cached result from 'test-build-dir/test.a1' for 'test.cpp' - hash mismatch\n", out); + } + + // Inclusion removed from source, check that it's removed from analyzer info + { + AnalyzerInformation analyzerInfo; + const auto includes = analyzerInfo.getIncludes("test-build-dir", "test.cpp", "", 0); + + ASSERT_EQUALS(1, includes.size()); + ASSERT_EQUALS("test1.h", *includes.begin()); + } + } + // TODO: test suppressions // TODO: test all with FS }; diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 01ae240fd1b..4b4285ab159 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -59,7 +59,7 @@ class TestPreprocessor : public TestFixture { std::vector files; simplecpp::TokenList tokens1 = simplecpp::TokenList(code, files, "file.cpp", {}, &outputList); Preprocessor p(tokens1, settingsDefault, errorLogger, Path::identify(tokens1.getFiles()[0], false)); - ASSERT_LOC(p.loadFiles(files), file, line); + ASSERT_LOC(p.loadAllIncludes(files), file, line); simplecpp::TokenList tokens2 = p.preprocess("", files, outputList); (void)p.reportOutput(outputList, true); return tokens2.stringify(); @@ -420,7 +420,7 @@ class TestPreprocessor : public TestFixture { }); preprocessor.removeComments(); preprocessor.getConfigs(configDefines, configs); - ASSERT(preprocessor.loadFiles(files)); + ASSERT(preprocessor.loadAllIncludes(files)); ASSERT(!preprocessor.reportOutput(outputList, true)); std::string ret; for (const std::string & config : configs) @@ -439,7 +439,7 @@ class TestPreprocessor : public TestFixture { } }); preprocessor.removeComments(); - ASSERT(preprocessor.loadFiles(files)); + ASSERT(preprocessor.loadAllIncludes(files)); return preprocessor.calculateHash(""); } diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index a44492de433..1c67e0d7eb9 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -615,7 +615,7 @@ class TestTokenizer : public TestFixture { } }); preprocessor.createDirectives(directives); - ASSERT(preprocessor.loadFiles(files)); + ASSERT(preprocessor.loadAllIncludes(files)); (void)preprocessor.reportOutput(outputList, true); TokenList tokenlist{settings, Path::identify(filename, false)};