From 3d4292dbbf5335961652187a312482d219305dc0 Mon Sep 17 00:00:00 2001 From: firewave Date: Thu, 13 Jun 2024 01:48:27 +0200 Subject: [PATCH] store language in `FileWithDetails` --- cli/cmdlineparser.cpp | 60 ++++++++++++++++++++++++++ cli/filelister.cpp | 12 +++--- gui/checkthread.cpp | 2 +- lib/filesettings.h | 21 ++++++++-- lib/path.cpp | 7 +++- lib/path.h | 10 +++-- test/testcmdlineparser.cpp | 9 ++++ test/testfilelister.cpp | 81 +++++++++++++++++++++++++++++++++--- test/testfilesettings.cpp | 8 +++- test/testpath.cpp | 25 +++++++++++ test/testprocessexecutor.cpp | 8 ++-- test/testsingleexecutor.cpp | 9 ++-- test/testsuppressions.cpp | 12 +++--- test/testthreadexecutor.cpp | 8 ++-- 14 files changed, 231 insertions(+), 41 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 748031e443e..c1586997395 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -214,6 +214,35 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[]) mFileSettings.clear(); + if (mSettings.enforcedLang != Standards::Language::None) + { + // apply enforced language + for (auto& fs : fileSettings) + { + if (mSettings.library.markupFile(fs.filename())) + continue; + fs.file.setLang(mSettings.enforcedLang); + } + } + else + { + // identify files + for (auto& fs : fileSettings) + { + if (mSettings.library.markupFile(fs.filename())) + continue; + fs.file.setLang(Path::identify(fs.filename(), mSettings.cppHeaderProbe)); + } + } + + // enforce the language since markup files are special and do not adhere to the enforced language + for (auto& fs : fileSettings) + { + if (mSettings.library.markupFile(fs.filename())) { + fs.file.setLang(Standards::Language::C); + } + } + // sort the markup last std::copy_if(fileSettings.cbegin(), fileSettings.cend(), std::back_inserter(mFileSettings), [&](const FileSettings &fs) { return !mSettings.library.markupFile(fs.filename()) || !mSettings.library.processMarkupAfterCode(fs.filename()); @@ -284,6 +313,37 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[]) files = std::move(filesResolved); } + if (mSettings.enforcedLang != Standards::Language::None) + { + // apply enforced language + for (auto& f : files) + { + if (mSettings.library.markupFile(f.path())) + continue; + f.setLang(mSettings.enforcedLang); + } + } + else + { + // identify remaining files + for (auto& f : files) + { + if (f.lang() != Standards::Language::None) + continue; + if (mSettings.library.markupFile(f.path())) + continue; + f.setLang(Path::identify(f.path(), mSettings.cppHeaderProbe)); + } + } + + // enforce the language since markup files are special and do not adhere to the enforced language + for (auto& f : files) + { + if (mSettings.library.markupFile(f.path())) { + f.setLang(Standards::Language::C); + } + } + // sort the markup last std::copy_if(files.cbegin(), files.cend(), std::inserter(mFiles, mFiles.end()), [&](const FileWithDetails& entry) { return !mSettings.library.markupFile(entry.path()) || !mSettings.library.processMarkupAfterCode(entry.path()); diff --git a/cli/filelister.cpp b/cli/filelister.cpp index 13f7619e6db..9c5720b9dc9 100644 --- a/cli/filelister.cpp +++ b/cli/filelister.cpp @@ -100,7 +100,8 @@ static std::string addFiles2(std::list&files, const std::string if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { // File - if ((!checkAllFilesInDir || Path::acceptFile(fname, extra)) && !ignored.match(fname)) { + Standards::Language lang = Standards::Language::None; + if ((!checkAllFilesInDir || Path::acceptFile(fname, extra, &lang)) && !ignored.match(fname)) { std::string nativename = Path::fromNativeSeparators(fname); // Limitation: file sizes are assumed to fit in a 'size_t' @@ -110,7 +111,7 @@ static std::string addFiles2(std::list&files, const std::string const std::size_t filesize = ffd.nFileSizeLow; #endif - files.emplace_back(std::move(nativename), filesize); + files.emplace_back(std::move(nativename), lang, filesize); } } else { // Directory @@ -192,7 +193,7 @@ static std::string addFiles2(std::list &files, return ""; // TODO: return error? if ((file_stat.st_mode & S_IFMT) != S_IFDIR) { - files.emplace_back(path, file_stat.st_size); + files.emplace_back(path, Standards::Language::None, file_stat.st_size); return ""; } @@ -229,12 +230,13 @@ static std::string addFiles2(std::list &files, } } } else { - if (Path::acceptFile(new_path, extra) && !ignored.match(new_path)) { + Standards::Language lang = Standards::Language::None; + if (Path::acceptFile(new_path, extra, &lang) && !ignored.match(new_path)) { if (stat(new_path.c_str(), &file_stat) == -1) { const int err = errno; return "could not stat file '" + new_path + "' (errno: " + std::to_string(err) + ")"; } - files.emplace_back(new_path, file_stat.st_size); + files.emplace_back(new_path, lang, file_stat.st_size); } } } diff --git a/gui/checkthread.cpp b/gui/checkthread.cpp index cc93b5c81c3..3ae7cee36a6 100644 --- a/gui/checkthread.cpp +++ b/gui/checkthread.cpp @@ -139,7 +139,7 @@ void CheckThread::run() qDebug() << "Whole program analysis"; std::list files2; std::transform(mFiles.cbegin(), mFiles.cend(), std::back_inserter(files2), [&](const QString& file) { - return FileWithDetails{file.toStdString(), 0}; + return FileWithDetails{file.toStdString(), Path::identify(file.toStdString(), cppcheck.settings().cppHeaderProbe), 0}; }); cppcheck.analyseWholeProgram(cppcheck.settings().buildDir, files2, {}, ctuInfo); mFiles.clear(); diff --git a/lib/filesettings.h b/lib/filesettings.h index 75bf8faaa50..5b3039cd4e7 100644 --- a/lib/filesettings.h +++ b/lib/filesettings.h @@ -22,6 +22,7 @@ #include "config.h" #include "path.h" #include "platform.h" +#include "standards.h" #include #include @@ -33,12 +34,13 @@ class FileWithDetails { public: explicit FileWithDetails(std::string path) - : FileWithDetails(std::move(path), 0) + : FileWithDetails(std::move(path), Standards::Language::None, 0) {} - FileWithDetails(std::string path, std::size_t size) + FileWithDetails(std::string path, Standards::Language lang, std::size_t size) : mPath(std::move(path)) , mPathSimplified(Path::simplifyPath(mPath)) + , mLang(lang) , mSize(size) { if (mPath.empty()) @@ -59,9 +61,20 @@ class FileWithDetails { return mSize; } + + void setLang(Standards::Language lang) + { + mLang = lang; + } + + Standards::Language lang() const + { + return mLang; + } private: std::string mPath; std::string mPathSimplified; + Standards::Language mLang = Standards::Language::None; std::size_t mSize; }; @@ -71,8 +84,8 @@ struct CPPCHECKLIB FileSettings { : file(std::move(path)) {} - FileSettings(std::string path, std::size_t size) - : file(std::move(path), size) + FileSettings(std::string path, Standards::Language lang, std::size_t size) + : file(std::move(path), lang, size) {} std::string cfg; diff --git a/lib/path.cpp b/lib/path.cpp index 185b9442222..98f9813ab57 100644 --- a/lib/path.cpp +++ b/lib/path.cpp @@ -215,10 +215,13 @@ static const std::unordered_set header_exts = { ".h", ".hpp", ".h++", ".hxx", ".hh" }; -bool Path::acceptFile(const std::string &path, const std::set &extra) +bool Path::acceptFile(const std::string &path, const std::set &extra, Standards::Language* lang) { bool header = false; - return (identify(path, false, &header) != Standards::Language::None && !header) || extra.find(getFilenameExtension(path)) != extra.end(); + Standards::Language l = identify(path, false, &header); + if (lang) + *lang = l; + return (l != Standards::Language::None && !header) || extra.find(getFilenameExtension(path)) != extra.end(); } static bool hasEmacsCppMarker(const char* path) diff --git a/lib/path.h b/lib/path.h index fda36c01f85..db22773ed65 100644 --- a/lib/path.h +++ b/lib/path.h @@ -137,21 +137,23 @@ class CPPCHECKLIB Path { * @brief Check if the file extension indicates that it's a C/C++ source file. * Check if the file has source file extension: *.c;*.cpp;*.cxx;*.c++;*.cc;*.txx * @param filename filename to check. path info is optional + * @param lang the detected language * @return true if the file extension indicates it should be checked */ - static bool acceptFile(const std::string &filename) { + static bool acceptFile(const std::string &filename, Standards::Language* lang = nullptr) { const std::set extra; - return acceptFile(filename, extra); + return acceptFile(filename, extra, lang); } /** * @brief Check if the file extension indicates that it's a C/C++ source file. * Check if the file has source file extension: *.c;*.cpp;*.cxx;*.c++;*.cc;*.txx * @param path filename to check. path info is optional - * @param extra extra file extensions + * @param extra extra file extensions + * @param lang the detected language * @return true if the file extension indicates it should be checked */ - static bool acceptFile(const std::string &path, const std::set &extra); + static bool acceptFile(const std::string &path, const std::set &extra, Standards::Language* lang = nullptr); /** * @brief Is filename a header based on file extension diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 8684bf973b8..3ef8a4423a5 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -430,6 +430,8 @@ class TestCmdlineParser : public TestFixture { TEST_CASE(ignorefilepaths2); TEST_CASE(ignorefilepaths3); + TEST_CASE(nonexistentpath); + TEST_CASE(checkconfig); TEST_CASE(unknownParam); @@ -2955,6 +2957,13 @@ class TestCmdlineParser : public TestFixture { ASSERT_EQUALS("foo.cpp", parser->getIgnoredPaths()[0]); } + void nonexistentpath() { + REDIRECT; + const char * const argv[] = {"cppcheck", "file.cpp"}; + ASSERT(!parser->fillSettingsFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: could not find or open any of the paths given.\n", logger->str()); + } + void checkconfig() { REDIRECT; const char * const argv[] = {"cppcheck", "--check-config", "file.cpp"}; diff --git a/test/testfilelister.cpp b/test/testfilelister.cpp index 092ac86a3b6..12c6428a838 100644 --- a/test/testfilelister.cpp +++ b/test/testfilelister.cpp @@ -39,6 +39,7 @@ class TestFileLister : public TestFixture { TEST_CASE(recursiveAddFilesEmptyPath); TEST_CASE(excludeFile1); TEST_CASE(excludeFile2); + TEST_CASE(addFiles); } // TODO: generate file list instead @@ -81,11 +82,25 @@ class TestFileLister : public TestFixture { }; // Make sure source files are added.. - ASSERT(find_file(dirprefix + "cli/main.cpp") != files.end()); - ASSERT(find_file(dirprefix + "lib/token.cpp") != files.end()); - ASSERT(find_file(dirprefix + "lib/tokenize.cpp") != files.end()); - ASSERT(find_file(dirprefix + "gui/main.cpp") != files.end()); - ASSERT(find_file(dirprefix + "test/testfilelister.cpp") != files.end()); + auto it = find_file(dirprefix + "cli/main.cpp"); + ASSERT(it != files.end()); + ASSERT_EQUALS_ENUM(Standards::Language::CPP, it->lang()); + + it = find_file(dirprefix + "lib/token.cpp"); + ASSERT(it != files.end()); + ASSERT_EQUALS_ENUM(Standards::Language::CPP, it->lang()); + + it = find_file(dirprefix + "lib/tokenize.cpp"); + ASSERT(it != files.end()); + ASSERT_EQUALS_ENUM(Standards::Language::CPP, it->lang()); + + it = find_file(dirprefix + "gui/main.cpp"); + ASSERT(it != files.end()); + ASSERT_EQUALS_ENUM(Standards::Language::CPP, it->lang()); + + it = find_file(dirprefix + "test/testfilelister.cpp"); + ASSERT(it != files.end()); + ASSERT_EQUALS_ENUM(Standards::Language::CPP, it->lang()); // Make sure headers are not added.. ASSERT(find_file(dirprefix + "lib/tokenize.h") == files.end()); @@ -120,7 +135,63 @@ class TestFileLister : public TestFixture { ASSERT_EQUALS(basedir + "lib/token.cpp", files.begin()->path()); } + void addFiles() const { + const std::string adddir = findBaseDir() + "."; + + // TODO: on Windows the prefix is different from when a recursive a folder (see recursiveAddFiles test) + const std::string dirprefix = adddir + "/"; + const std::string dirprefix_nat = Path::toNativeSeparators(dirprefix); + + std::list files; + + { + const std::string addfile = Path::join(Path::join(adddir, "cli"), "main.cpp"); + const std::string err = FileLister::addFiles(files, addfile, {}, true,PathMatch({})); + ASSERT_EQUALS("", err); + } + { + const std::string addfile = Path::join(Path::join(adddir, "lib"), "token.cpp"); + const std::string err = FileLister::addFiles(files, addfile, {}, true,PathMatch({})); + ASSERT_EQUALS("", err); + } + { + const std::string addfile = Path::join(Path::join(adddir, "cli"), "token.cpp"); // does not exist + const std::string err = FileLister::addFiles(files, addfile, {}, true,PathMatch({})); + ASSERT_EQUALS("", err); + } + { + const std::string addfile = Path::join(Path::join(adddir, "lib2"), "token.cpp"); // does not exist + const std::string err = FileLister::addFiles(files, addfile, {}, true,PathMatch({})); +#ifdef _WIN32 + // TODO: get rid of this error - caused by missing intermediate folder + ASSERT_EQUALS("finding files failed. Search pattern: '" + dirprefix_nat + "lib2\\token.cpp'. (error: 3)", err); +#else + ASSERT_EQUALS("", err); +#endif + } + { + const std::string addfile = Path::join(Path::join(adddir, "lib"), "matchcompiler.h"); + const std::string err = FileLister::addFiles(files, addfile, {}, true,PathMatch({})); + ASSERT_EQUALS("", err); + } + + ASSERT_EQUALS(3, files.size()); + auto it = files.cbegin(); + ASSERT_EQUALS(dirprefix + "cli/main.cpp", it->path()); + ASSERT_EQUALS(Path::simplifyPath(dirprefix + "cli/main.cpp"), it->spath()); + ASSERT_EQUALS_ENUM(Standards::Language::None, it->lang()); + it++; + ASSERT_EQUALS(dirprefix + "lib/token.cpp", it->path()); + ASSERT_EQUALS(Path::simplifyPath(dirprefix + "lib/token.cpp"), it->spath()); + ASSERT_EQUALS_ENUM(Standards::Language::None, it->lang()); + it++; + ASSERT_EQUALS(dirprefix + "lib/matchcompiler.h", it->path()); + ASSERT_EQUALS(Path::simplifyPath(dirprefix + "lib/matchcompiler.h"), it->spath()); + ASSERT_EQUALS_ENUM(Standards::Language::None, it->lang()); + } + // TODO: test errors + // TODO: test wildcards }; REGISTER_TEST(TestFileLister) diff --git a/test/testfilesettings.cpp b/test/testfilesettings.cpp index 7cf371305ea..63f28d34990 100644 --- a/test/testfilesettings.cpp +++ b/test/testfilesettings.cpp @@ -33,36 +33,42 @@ class TestFileSettings : public TestFixture { const FileWithDetails p{"file.cpp"}; ASSERT_EQUALS("file.cpp", p.path()); ASSERT_EQUALS("file.cpp", p.spath()); + ASSERT_EQUALS_ENUM(Standards::Language::None, p.lang()); ASSERT_EQUALS(0, p.size()); } { - const FileWithDetails p{"file.cpp", 123}; + const FileWithDetails p{"file.cpp", Standards::Language::C, 123}; ASSERT_EQUALS("file.cpp", p.path()); ASSERT_EQUALS("file.cpp", p.spath()); + ASSERT_EQUALS_ENUM(Standards::Language::C, p.lang()); ASSERT_EQUALS(123, p.size()); } { const FileWithDetails p{"in/file.cpp"}; ASSERT_EQUALS("in/file.cpp", p.path()); ASSERT_EQUALS("in/file.cpp", p.spath()); + ASSERT_EQUALS_ENUM(Standards::Language::None, p.lang()); ASSERT_EQUALS(0, p.size()); } { const FileWithDetails p{"in\\file.cpp"}; ASSERT_EQUALS("in\\file.cpp", p.path()); ASSERT_EQUALS("in/file.cpp", p.spath()); + ASSERT_EQUALS_ENUM(Standards::Language::None, p.lang()); ASSERT_EQUALS(0, p.size()); } { const FileWithDetails p{"in/../file.cpp"}; ASSERT_EQUALS("in/../file.cpp", p.path()); ASSERT_EQUALS("file.cpp", p.spath()); + ASSERT_EQUALS_ENUM(Standards::Language::None, p.lang()); ASSERT_EQUALS(0, p.size()); } { const FileWithDetails p{"in\\..\\file.cpp"}; ASSERT_EQUALS("in\\..\\file.cpp", p.path()); ASSERT_EQUALS("file.cpp", p.spath()); + ASSERT_EQUALS_ENUM(Standards::Language::None, p.lang()); ASSERT_EQUALS(0, p.size()); } } diff --git a/test/testpath.cpp b/test/testpath.cpp index a51037c3081..8e8be2764bb 100644 --- a/test/testpath.cpp +++ b/test/testpath.cpp @@ -76,6 +76,16 @@ class TestPath : public TestFixture { ASSERT(Path::acceptFile("index")==false); ASSERT(Path::acceptFile("")==false); ASSERT(Path::acceptFile("C")==false); + { + Standards::Language lang; + ASSERT(Path::acceptFile("index.c", &lang)); + ASSERT_EQUALS_ENUM(Standards::Language::C, lang); + } + { + Standards::Language lang; + ASSERT(Path::acceptFile("index.cpp", &lang)); + ASSERT_EQUALS_ENUM(Standards::Language::CPP, lang); + } // don't accept any headers ASSERT_EQUALS(false, Path::acceptFile("index.h")); @@ -88,6 +98,21 @@ class TestPath : public TestFixture { ASSERT(Path::acceptFile("index.header", extra)); ASSERT(Path::acceptFile("index.h", extra)==false); ASSERT(Path::acceptFile("index.hpp", extra)==false); + { + Standards::Language lang; + ASSERT(Path::acceptFile("index.c", extra, &lang)); + ASSERT_EQUALS_ENUM(Standards::Language::C, lang); + } + { + Standards::Language lang; + ASSERT(Path::acceptFile("index.cpp", extra, &lang)); + ASSERT_EQUALS_ENUM(Standards::Language::CPP, lang); + } + { + Standards::Language lang; + ASSERT(Path::acceptFile("index.extra", extra, &lang)); + ASSERT_EQUALS_ENUM(Standards::Language::None, lang); + } } void getCurrentPath() const { diff --git a/test/testprocessexecutor.cpp b/test/testprocessexecutor.cpp index 1892cd4252f..bdcfb19dcc5 100644 --- a/test/testprocessexecutor.cpp +++ b/test/testprocessexecutor.cpp @@ -72,18 +72,18 @@ class TestProcessExecutorBase : public TestFixture { if (opt.filesList.empty()) { for (int i = 1; i <= files; ++i) { std::string f_s = fprefix() + "_" + std::to_string(i) + ".cpp"; - filelist.emplace_back(f_s, data.size()); + filelist.emplace_back(f_s, Standards::Language::CPP, data.size()); if (useFS) { - fileSettings.emplace_back(std::move(f_s), data.size()); + fileSettings.emplace_back(std::move(f_s), Standards::Language::CPP, data.size()); } } } else { for (const auto& f : opt.filesList) { - filelist.emplace_back(f, data.size()); + filelist.emplace_back(f, Standards::Language::CPP, data.size()); if (useFS) { - fileSettings.emplace_back(f, data.size()); + fileSettings.emplace_back(f, Standards::Language::CPP, data.size()); } } } diff --git a/test/testsingleexecutor.cpp b/test/testsingleexecutor.cpp index 1e9338fe1e7..8220db353ef 100644 --- a/test/testsingleexecutor.cpp +++ b/test/testsingleexecutor.cpp @@ -77,19 +77,18 @@ class TestSingleExecutorBase : public TestFixture { if (opt.filesList.empty()) { for (int i = 1; i <= files; ++i) { std::string f_s = fprefix() + "_" + zpad3(i) + ".cpp"; - filelist.emplace_back(f_s, data.size()); + filelist.emplace_back(f_s, Standards::Language::CPP, data.size()); if (useFS) { - fileSettings.emplace_back(std::move(f_s), data.size()); + fileSettings.emplace_back(std::move(f_s), Standards::Language::CPP, data.size()); } } } else { for (const auto& f : opt.filesList) { - filelist.emplace_back(f, data.size()); + filelist.emplace_back(f, Standards::Language::CPP, data.size()); if (useFS) { - - fileSettings.emplace_back(f, data.size()); + fileSettings.emplace_back(f, Standards::Language::CPP, data.size()); } } } diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index 1a24ce57566..a03d24fe29e 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -230,9 +230,9 @@ class TestSuppressions : public TestFixture { std::list filelist; for (std::map::const_iterator i = f.cbegin(); i != f.cend(); ++i) { - filelist.emplace_back(i->first, i->second.size()); + filelist.emplace_back(i->first, Standards::Language::CPP, i->second.size()); if (useFS) { - fileSettings.emplace_back(i->first, i->second.size()); + fileSettings.emplace_back(i->first, Standards::Language::CPP, i->second.size()); } } @@ -277,9 +277,9 @@ class TestSuppressions : public TestFixture { std::list fileSettings; std::list filelist; - filelist.emplace_back("test.cpp", strlen(code)); + filelist.emplace_back("test.cpp", Standards::Language::CPP, strlen(code)); if (useFS) { - fileSettings.emplace_back("test.cpp", strlen(code)); + fileSettings.emplace_back("test.cpp", Standards::Language::CPP, strlen(code)); } /*const*/ auto settings = dinit(Settings, @@ -321,9 +321,9 @@ class TestSuppressions : public TestFixture { std::list fileSettings; std::list filelist; - filelist.emplace_back("test.cpp", strlen(code)); + filelist.emplace_back("test.cpp", Standards::Language::CPP, strlen(code)); if (useFS) { - fileSettings.emplace_back("test.cpp", strlen(code)); + fileSettings.emplace_back("test.cpp", Standards::Language::CPP, strlen(code)); } /*const*/ auto settings = dinit(Settings, diff --git a/test/testthreadexecutor.cpp b/test/testthreadexecutor.cpp index 7e5f85d70fa..66c3582a4f4 100644 --- a/test/testthreadexecutor.cpp +++ b/test/testthreadexecutor.cpp @@ -72,18 +72,18 @@ class TestThreadExecutorBase : public TestFixture { if (opt.filesList.empty()) { for (int i = 1; i <= files; ++i) { std::string f_s = fprefix() + "_" + std::to_string(i) + ".cpp"; - filelist.emplace_back(f_s, data.size()); + filelist.emplace_back(f_s, Standards::Language::CPP, data.size()); if (useFS) { - fileSettings.emplace_back(std::move(f_s), data.size()); + fileSettings.emplace_back(std::move(f_s), Standards::Language::CPP, data.size()); } } } else { for (const auto& f : opt.filesList) { - filelist.emplace_back(f, data.size()); + filelist.emplace_back(f, Standards::Language::CPP, data.size()); if (useFS) { - fileSettings.emplace_back(f, data.size()); + fileSettings.emplace_back(f, Standards::Language::CPP, data.size()); } } }