SingleExecutor: process markup files after code when scanning projects (#4972)

* SingleExecutor: added TODOs

* test `SingleExecutor` with files and project

* SingleExecutor: process markup files after code when scanning project

* TestSingleExecutor: generate scoped files before calling executor

* CI-unixish.yml: added `--output-on-failure` to CTest call

* helpers.cpp: improved error reporting in `~ScopedFile()`

* use unique filenames in executor tests to avoid collisions

* fixed `functionStatic` selfcheck warnings
This commit is contained in:
Oliver Stöneberg 2023-05-03 17:32:28 +02:00 committed by GitHub
parent f04d47ac61
commit b5ce2c708b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 131 additions and 51 deletions

View File

@ -111,7 +111,7 @@ jobs:
- name: Run CTest - name: Run CTest
run: | run: |
pushd cmake.output pushd cmake.output
ctest -j$(nproc) ctest --output-on-failure -j$(nproc)
build_uchar: build_uchar:

View File

@ -51,6 +51,8 @@ unsigned int SingleExecutor::check()
std::size_t processedsize = 0; std::size_t processedsize = 0;
unsigned int c = 0; unsigned int c = 0;
// TODO: processes either mSettings.project.fileSettings or mFiles - process/thread implementations process both
// TODO: thread/process implementations process fileSettings first
if (mSettings.project.fileSettings.empty()) { if (mSettings.project.fileSettings.empty()) {
for (std::map<std::string, std::size_t>::const_iterator i = mFiles.cbegin(); i != mFiles.cend(); ++i) { for (std::map<std::string, std::size_t>::const_iterator i = mFiles.cbegin(); i != mFiles.cend(); ++i) {
if (!mSettings.library.markupFile(i->first) if (!mSettings.library.markupFile(i->first)
@ -59,6 +61,7 @@ unsigned int SingleExecutor::check()
processedsize += i->second; processedsize += i->second;
if (!mSettings.quiet) if (!mSettings.quiet)
reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize); reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize);
// TODO: call analyseClangTidy()
c++; c++;
} }
} }
@ -66,24 +69,45 @@ unsigned int SingleExecutor::check()
// filesettings // filesettings
// check all files of the project // check all files of the project
for (const ImportProject::FileSettings &fs : mSettings.project.fileSettings) { for (const ImportProject::FileSettings &fs : mSettings.project.fileSettings) {
result += mCppcheck.check(fs); if (!mSettings.library.markupFile(fs.filename)
++c; || !mSettings.library.processMarkupAfterCode(fs.filename)) {
if (!mSettings.quiet) result += mCppcheck.check(fs);
reportStatus(c, mSettings.project.fileSettings.size(), c, mSettings.project.fileSettings.size()); ++c;
if (mSettings.clangTidy) if (!mSettings.quiet)
mCppcheck.analyseClangTidy(fs); reportStatus(c, mSettings.project.fileSettings.size(), c, mSettings.project.fileSettings.size());
if (mSettings.clangTidy)
mCppcheck.analyseClangTidy(fs);
}
} }
} }
// second loop to parse all markup files which may not work until all // second loop to parse all markup files which may not work until all
// c/cpp files have been parsed and checked // c/cpp files have been parsed and checked
for (std::map<std::string, std::size_t>::const_iterator i = mFiles.cbegin(); i != mFiles.cend(); ++i) { // TODO: get rid of duplicated code
if (mSettings.library.markupFile(i->first) && mSettings.library.processMarkupAfterCode(i->first)) { if (mSettings.project.fileSettings.empty()) {
result += mCppcheck.check(i->first); for (std::map<std::string, std::size_t>::const_iterator i = mFiles.cbegin(); i != mFiles.cend(); ++i) {
processedsize += i->second; if (mSettings.library.markupFile(i->first)
if (!mSettings.quiet) && mSettings.library.processMarkupAfterCode(i->first)) {
reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize); result += mCppcheck.check(i->first);
c++; processedsize += i->second;
if (!mSettings.quiet)
reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize);
// TODO: call analyseClangTidy()
c++;
}
}
}
else {
for (const ImportProject::FileSettings &fs : mSettings.project.fileSettings) {
if (mSettings.library.markupFile(fs.filename)
&& mSettings.library.processMarkupAfterCode(fs.filename)) {
result += mCppcheck.check(fs);
++c;
if (!mSettings.quiet)
reportStatus(c, mSettings.project.fileSettings.size(), c, mSettings.project.fileSettings.size());
if (mSettings.clangTidy)
mCppcheck.analyseClangTidy(fs);
}
} }
} }
if (mCppcheck.analyseWholeProgram()) if (mCppcheck.analyseWholeProgram())

View File

@ -53,7 +53,7 @@ namespace tinyxml2 {
class CPPCHECKLIB Library { class CPPCHECKLIB Library {
// TODO: get rid of this // TODO: get rid of this
friend class TestSymbolDatabase; // For testing only friend class TestSymbolDatabase; // For testing only
friend class TestSingleExecutor; // For testing only friend class TestSingleExecutorBase; // For testing only
friend class TestThreadExecutor; // For testing only friend class TestThreadExecutor; // For testing only
friend class TestProcessExecutor; // For testing only friend class TestProcessExecutor; // For testing only

View File

@ -14,3 +14,4 @@ release notes for cppcheck-2.11
- `constVariableReference` - `constVariableReference`
- `constVariablePointer` - `constVariablePointer`
- More command-line parameters will now check if the given integer argument is actually valid. Several other internal string-to-integer conversions will not be error checked. - More command-line parameters will now check if the given integer argument is actually valid. Several other internal string-to-integer conversions will not be error checked.
- scanning projects (with -j1) will now defer the analysis of markup files until the whole code was processed

View File

@ -22,6 +22,7 @@
#include "preprocessor.h" #include "preprocessor.h"
#include <cstdio> #include <cstdio>
#include <iostream>
#include <fstream> #include <fstream>
#include <stdexcept> #include <stdexcept>
#include <utility> #include <utility>
@ -60,12 +61,21 @@ ScopedFile::ScopedFile(std::string name, const std::string &content, std::string
} }
ScopedFile::~ScopedFile() { ScopedFile::~ScopedFile() {
std::remove(mFullPath.c_str()); const int remove_res = std::remove(mFullPath.c_str());
if (remove_res != 0) {
std::cout << "ScopedFile(" << mFullPath + ") - could not delete file (" << remove_res << ")";
}
if (!mPath.empty() && mPath != Path::getCurrentPath()) { if (!mPath.empty() && mPath != Path::getCurrentPath()) {
#ifdef _WIN32 #ifdef _WIN32
RemoveDirectoryA(mPath.c_str()); if (!RemoveDirectoryA(mPath.c_str())) {
std::cout << "ScopedFile(" << mFullPath + ") - could not delete folder (" << GetLastError() << ")";
}
#else #else
rmdir(mPath.c_str()); const int rmdir_res = rmdir(mPath.c_str());
if (rmdir_res == -1) {
const int err = errno;
std::cout << "ScopedFile(" << mFullPath + ") - could not delete folder (" << err << ")";
}
#endif #endif
} }
} }

View File

@ -41,6 +41,11 @@ public:
private: private:
Settings settings = settingsBuilder().library("std.cfg").build(); Settings settings = settingsBuilder().library("std.cfg").build();
static std::string fprefix()
{
return "process";
}
/** /**
* Execute check using n jobs for y files which are have * Execute check using n jobs for y files which are have
* identical data, given within data. * identical data, given within data.
@ -53,7 +58,7 @@ private:
if (filesList.empty()) { if (filesList.empty()) {
for (int i = 1; i <= files; ++i) { for (int i = 1; i <= files; ++i) {
std::ostringstream oss; std::ostringstream oss;
oss << "file_" << i << ".cpp"; oss << fprefix() << "_" << i << ".cpp";
filemap[oss.str()] = data.size(); filemap[oss.str()] = data.size();
} }
} }
@ -186,7 +191,7 @@ private:
settings.library.mProcessAfterCode.emplace(".cp1", true); settings.library.mProcessAfterCode.emplace(".cp1", true);
const std::vector<std::string> files = { const std::vector<std::string> files = {
"file_1.cp1", "file_2.cpp", "file_3.cp1", "file_4.cpp" fprefix() + "_1.cp1", fprefix() + "_2.cpp", fprefix() + "_3.cp1", fprefix() + "_4.cpp"
}; };
// the checks are not executed on the markup files => expected result is 2 // the checks are not executed on the markup files => expected result is 2
@ -198,21 +203,21 @@ private:
"}", "}",
SHOWTIME_MODES::SHOWTIME_NONE, nullptr, files); SHOWTIME_MODES::SHOWTIME_NONE, nullptr, files);
// TODO: order of "Checking" and "checked" is affected by thread // TODO: order of "Checking" and "checked" is affected by thread
/*TODO_ASSERT_EQUALS("Checking file_2.cpp ...\n" /*TODO_ASSERT_EQUALS("Checking " + fprefix() + "_2.cpp ...\n"
"1/4 files checked 25% done\n" "1/4 files checked 25% done\n"
"Checking file_4.cpp ...\n" "Checking " + fprefix() + "_4.cpp ...\n"
"2/4 files checked 50% done\n" "2/4 files checked 50% done\n"
"Checking file_1.cp1 ...\n" "Checking " + fprefix() + "_1.cp1 ...\n"
"3/4 files checked 75% done\n" "3/4 files checked 75% done\n"
"Checking file_3.cp1 ...\n" "Checking " + fprefix() + "_3.cp1 ...\n"
"4/4 files checked 100% done\n", "4/4 files checked 100% done\n",
"Checking file_1.cp1 ...\n" "Checking " + fprefix() + "_1.cp1 ...\n"
"1/4 files checked 25% done\n" "1/4 files checked 25% done\n"
"Checking file_2.cpp ...\n" "Checking " + fprefix() + "_2.cpp ...\n"
"2/4 files checked 50% done\n" "2/4 files checked 50% done\n"
"Checking file_3.cp1 ...\n" "Checking " + fprefix() + "_3.cp1 ...\n"
"3/4 files checked 75% done\n" "3/4 files checked 75% done\n"
"Checking file_4.cpp ...\n" "Checking " + fprefix() + "_4.cpp ...\n"
"4/4 files checked 100% done\n", "4/4 files checked 100% done\n",
output.str());*/ output.str());*/
settings = settingsOld; settings = settingsOld;

View File

@ -36,12 +36,20 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
class TestSingleExecutor : public TestFixture { class TestSingleExecutorBase : public TestFixture {
public: protected:
TestSingleExecutor() : TestFixture("TestSingleExecutor") {} TestSingleExecutorBase(const char * const name, bool useFS) : TestFixture(name), useFS(useFS) {}
private: private:
Settings settings = settingsBuilder().library("std.cfg").build(); Settings settings = settingsBuilder().library("std.cfg").build();
bool useFS;
std::string fprefix() const
{
if (useFS)
return "singlefs";
return "single";
}
static std::string zpad3(int i) static std::string zpad3(int i)
{ {
@ -55,18 +63,29 @@ private:
void check(int files, int result, const std::string &data, SHOWTIME_MODES showtime = SHOWTIME_MODES::SHOWTIME_NONE, const char* const plistOutput = nullptr, const std::vector<std::string>& filesList = {}) { void check(int files, int result, const std::string &data, SHOWTIME_MODES showtime = SHOWTIME_MODES::SHOWTIME_NONE, const char* const plistOutput = nullptr, const std::vector<std::string>& filesList = {}) {
errout.str(""); errout.str("");
output.str(""); output.str("");
settings.project.fileSettings.clear();
std::map<std::string, std::size_t> filemap; std::map<std::string, std::size_t> filemap;
if (filesList.empty()) { if (filesList.empty()) {
for (int i = 1; i <= files; ++i) { for (int i = 1; i <= files; ++i) {
const std::string s = "file_" + zpad3(i) + ".cpp"; const std::string s = fprefix() + "_" + zpad3(i) + ".cpp";
filemap[s] = data.size(); filemap[s] = data.size();
if (useFS) {
ImportProject::FileSettings fs;
fs.filename = s;
settings.project.fileSettings.emplace_back(std::move(fs));
}
} }
} }
else { else {
for (const auto& f : filesList) for (const auto& f : filesList)
{ {
filemap[f] = data.size(); filemap[f] = data.size();
if (useFS) {
ImportProject::FileSettings fs;
fs.filename = f;
settings.project.fileSettings.emplace_back(std::move(fs));
}
} }
} }
@ -78,13 +97,18 @@ private:
return false; return false;
}); });
cppcheck.settings() = settings; cppcheck.settings() = settings;
// TODO: test with settings.project.fileSettings;
SingleExecutor executor(cppcheck, filemap, settings, *this);
std::vector<std::unique_ptr<ScopedFile>> scopedfiles; std::vector<std::unique_ptr<ScopedFile>> scopedfiles;
scopedfiles.reserve(filemap.size()); scopedfiles.reserve(filemap.size());
for (std::map<std::string, std::size_t>::const_iterator i = filemap.cbegin(); i != filemap.cend(); ++i) for (std::map<std::string, std::size_t>::const_iterator i = filemap.cbegin(); i != filemap.cend(); ++i)
scopedfiles.emplace_back(new ScopedFile(i->first, data)); scopedfiles.emplace_back(new ScopedFile(i->first, data));
// clear files list so only fileSettings are used
if (useFS)
filemap.clear();
// TODO: test with settings.project.fileSettings;
SingleExecutor executor(cppcheck, filemap, settings, *this);
ASSERT_EQUALS(result, executor.check()); ASSERT_EQUALS(result, executor.check());
} }
@ -112,7 +136,7 @@ private:
"}"); "}");
std::string expected; std::string expected;
for (int i = 1; i <= 100; ++i) { for (int i = 1; i <= 100; ++i) {
expected += "Checking file_" + zpad3(i) + ".cpp ...\n"; expected += "Checking " + fprefix() + "_" + zpad3(i) + ".cpp ...\n";
expected += std::to_string(i) + "/100 files checked " + std::to_string(i) + "% done\n"; expected += std::to_string(i) + "/100 files checked " + std::to_string(i) + "% done\n";
} }
ASSERT_EQUALS(expected, output.str()); ASSERT_EQUALS(expected, output.str());
@ -190,7 +214,7 @@ private:
settings.library.mProcessAfterCode.emplace(".cp1", true); settings.library.mProcessAfterCode.emplace(".cp1", true);
const std::vector<std::string> files = { const std::vector<std::string> files = {
"file_1.cp1", "file_2.cpp", "file_3.cp1", "file_4.cpp" fprefix() + "_1.cp1", fprefix() + "_2.cpp", fprefix() + "_3.cp1", fprefix() + "_4.cpp"
}; };
// checks are not executed on markup files => expected result is 2 // checks are not executed on markup files => expected result is 2
@ -202,13 +226,13 @@ private:
"}", "}",
SHOWTIME_MODES::SHOWTIME_NONE, nullptr, files); SHOWTIME_MODES::SHOWTIME_NONE, nullptr, files);
// TODO: filter out the "files checked" messages // TODO: filter out the "files checked" messages
ASSERT_EQUALS("Checking file_2.cpp ...\n" ASSERT_EQUALS("Checking " + fprefix() + "_2.cpp ...\n"
"1/4 files checked 25% done\n" "1/4 files checked 25% done\n"
"Checking file_4.cpp ...\n" "Checking " + fprefix() + "_4.cpp ...\n"
"2/4 files checked 50% done\n" "2/4 files checked 50% done\n"
"Checking file_1.cp1 ...\n" "Checking " + fprefix() + "_1.cp1 ...\n"
"3/4 files checked 75% done\n" "3/4 files checked 75% done\n"
"Checking file_3.cp1 ...\n" "Checking " + fprefix() + "_3.cp1 ...\n"
"4/4 files checked 100% done\n", output.str()); "4/4 files checked 100% done\n", output.str());
settings = settingsOld; settings = settingsOld;
} }
@ -217,4 +241,15 @@ private:
// TODO: test whole program analysis // TODO: test whole program analysis
}; };
REGISTER_TEST(TestSingleExecutor) class TestSingleExecutorFiles : public TestSingleExecutorBase {
public:
TestSingleExecutorFiles() : TestSingleExecutorBase("TestSingleExecutorFiles", false) {}
};
class TestSingleExecutorFS : public TestSingleExecutorBase {
public:
TestSingleExecutorFS() : TestSingleExecutorBase("TestSingleExecutorFS", true) {}
};
REGISTER_TEST(TestSingleExecutorFiles)
REGISTER_TEST(TestSingleExecutorFS)

View File

@ -41,6 +41,11 @@ public:
private: private:
Settings settings = settingsBuilder().library("std.cfg").build(); Settings settings = settingsBuilder().library("std.cfg").build();
static std::string fprefix()
{
return "thread";
}
/** /**
* Execute check using n jobs for y files which are have * Execute check using n jobs for y files which are have
* identical data, given within data. * identical data, given within data.
@ -53,7 +58,7 @@ private:
if (filesList.empty()) { if (filesList.empty()) {
for (int i = 1; i <= files; ++i) { for (int i = 1; i <= files; ++i) {
std::ostringstream oss; std::ostringstream oss;
oss << "file_" << i << ".cpp"; oss << fprefix() << "_" << i << ".cpp";
filemap[oss.str()] = data.size(); filemap[oss.str()] = data.size();
} }
} }
@ -184,7 +189,7 @@ private:
settings.library.mProcessAfterCode.emplace(".cp1", true); settings.library.mProcessAfterCode.emplace(".cp1", true);
const std::vector<std::string> files = { const std::vector<std::string> files = {
"file_1.cp1", "file_2.cpp", "file_3.cp1", "file_4.cpp" fprefix() + "_1.cp1", fprefix() + "_2.cpp", fprefix() + "_3.cp1", fprefix() + "_4.cpp"
}; };
// checks are not executed on markup files => expected result is 2 // checks are not executed on markup files => expected result is 2
@ -196,21 +201,21 @@ private:
"}", "}",
SHOWTIME_MODES::SHOWTIME_NONE, nullptr, files); SHOWTIME_MODES::SHOWTIME_NONE, nullptr, files);
// TODO: order of "Checking" and "checked" is affected by thread // TODO: order of "Checking" and "checked" is affected by thread
/*TODO_ASSERT_EQUALS("Checking file_2.cpp ...\n" /*TODO_ASSERT_EQUALS("Checking " + fprefix() + "_2.cpp ...\n"
"1/4 files checked 25% done\n" "1/4 files checked 25% done\n"
"Checking file_4.cpp ...\n" "Checking " + fprefix() + "_4.cpp ...\n"
"2/4 files checked 50% done\n" "2/4 files checked 50% done\n"
"Checking file_1.cp1 ...\n" "Checking " + fprefix() + "_1.cp1 ...\n"
"3/4 files checked 75% done\n" "3/4 files checked 75% done\n"
"Checking file_3.cp1 ...\n" "Checking " + fprefix() + "_3.cp1 ...\n"
"4/4 files checked 100% done\n", "4/4 files checked 100% done\n",
"Checking file_1.cp1 ...\n" "Checking " + fprefix() + "_1.cp1 ...\n"
"1/4 files checked 25% done\n" "1/4 files checked 25% done\n"
"Checking file_2.cpp ...\n" "Checking " + fprefix() + "_2.cpp ...\n"
"2/4 files checked 50% done\n" "2/4 files checked 50% done\n"
"Checking file_3.cp1 ...\n" "Checking " + fprefix() + "_3.cp1 ...\n"
"3/4 files checked 75% done\n" "3/4 files checked 75% done\n"
"Checking file_4.cpp ...\n" "Checking " + fprefix() + "_4.cpp ...\n"
"4/4 files checked 100% done\n", "4/4 files checked 100% done\n",
output.str());*/ output.str());*/
settings = settingsOld; settings = settingsOld;