improved testing of missing includes / made it possible to clear the include cache in simplecpp (#4704)

This commit is contained in:
Oliver Stöneberg 2023-02-07 22:02:12 +01:00 committed by GitHub
parent 41d2d1b0b5
commit 4d9caa99f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 401 additions and 10 deletions

View File

@ -264,6 +264,7 @@ CLIOBJ = cli/cmdlineparser.o \
cli/threadexecutor.o
TESTOBJ = test/fixture.o \
test/helpers.o \
test/main.o \
test/options.o \
test/test64bit.o \
@ -659,6 +660,9 @@ cli/threadexecutor.o: cli/threadexecutor.cpp cli/cppcheckexecutor.h cli/executor
test/fixture.o: test/fixture.cpp lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h test/fixture.h test/options.h test/redirect.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/fixture.cpp
test/helpers.o: test/helpers.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/helpers.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/helpers.cpp
test/main.o: test/main.cpp externals/simplecpp/simplecpp.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/preprocessor.h lib/suppressions.h test/fixture.h test/options.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/main.cpp
@ -770,7 +774,7 @@ test/testplatform.o: test/testplatform.cpp externals/tinyxml2/tinyxml2.h lib/che
test/testpostfixoperator.o: test/testpostfixoperator.cpp lib/check.h lib/checkpostfixoperator.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testpostfixoperator.cpp
test/testpreprocessor.o: test/testpreprocessor.cpp externals/simplecpp/simplecpp.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h
test/testpreprocessor.o: test/testpreprocessor.cpp externals/simplecpp/simplecpp.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.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/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testpreprocessor.cpp
test/testprocessexecutor.o: test/testprocessexecutor.cpp cli/executor.h cli/processexecutor.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h test/redirect.h

View File

@ -626,6 +626,7 @@ static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cf
dui.std = mSettings.standards.getCPP();
else
dui.std = mSettings.standards.getC();
dui.clearIncludeCache = mSettings.clearIncludeCache;
return dui;
}

View File

@ -44,6 +44,7 @@ Settings::Settings()
clang(false),
clangExecutable("clang"),
clangTidy(false),
clearIncludeCache(false),
daca(false),
debugnormal(false),
debugSimplified(false),

View File

@ -150,6 +150,9 @@ public:
/** Use clang-tidy */
bool clangTidy;
/** Internal: Clear the simplecpp non-existing include cache */
bool clearIncludeCache;
/** @brief include paths excluded from checking the configuration */
std::set<std::string> configExcludePaths;

61
test/helpers.cpp Normal file
View File

@ -0,0 +1,61 @@
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2022 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "helpers.h"
#include "path.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#endif
ScopedFile::ScopedFile(std::string name, const std::string &content, std::string path)
: mName(std::move(name))
, mPath(Path::toNativeSeparators(std::move(path)))
, mFullPath(Path::join(mPath, mName))
{
if (!mPath.empty() && mPath != Path::getCurrentPath()) {
#ifdef _WIN32
if (!CreateDirectoryA(mPath.c_str(), nullptr))
throw std::runtime_error("ScopedFile(" + mFullPath + ") - could not create directory");
#else
if (mkdir(mPath.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0)
throw std::runtime_error("ScopedFile(" + mFullPath + ") - could not create directory");
#endif
}
std::ofstream of(mFullPath);
if (!of.is_open())
throw std::runtime_error("ScopedFile(" + mFullPath + ") - could not open file");
of << content;
}
ScopedFile::~ScopedFile() {
std::remove(mFullPath.c_str());
if (!mPath.empty() && mPath != Path::getCurrentPath()) {
#ifdef _WIN32
RemoveDirectoryA(mPath.c_str());
#else
rmdir(mPath.c_str());
#endif
}
}

View File

@ -74,16 +74,17 @@ private:
class ScopedFile {
public:
ScopedFile(std::string name, const std::string &content) : mName(std::move(name)) {
std::ofstream of(mName);
of << content;
}
ScopedFile(std::string name, const std::string &content, std::string path = "");
~ScopedFile();
~ScopedFile() {
remove(mName.c_str());
const std::string& path() const
{
return mFullPath;
}
private:
std::string mName;
const std::string mName;
const std::string mPath;
const std::string mFullPath;
};
#endif // helpersH

View File

@ -21,6 +21,7 @@
// the code for a known configuration, it generates the code for each configuration.
#include "errortypes.h"
#include "path.h"
#include "platform.h"
#include "preprocessor.h"
#include "settings.h"
@ -259,6 +260,17 @@ private:
TEST_CASE(testDirectiveIncludeComments);
TEST_CASE(testMissingInclude);
TEST_CASE(testMissingInclude2);
TEST_CASE(testMissingInclude3);
TEST_CASE(testMissingInclude4);
TEST_CASE(testMissingInclude5);
TEST_CASE(testMissingInclude6);
TEST_CASE(testMissingSystemInclude);
TEST_CASE(testMissingSystemInclude2);
TEST_CASE(testMissingSystemInclude3);
TEST_CASE(testMissingSystemInclude4);
TEST_CASE(testMissingSystemInclude5);
TEST_CASE(testMissingIncludeMixed);
TEST_CASE(testMissingIncludeCheckConfig);
}
@ -2432,11 +2444,299 @@ private:
ASSERT_EQUALS(dumpdata, ostr.str());
}
// test for existing local include
void testMissingInclude() {
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
Settings settings;
settings.clearIncludeCache = true;
settings.severity.clear();
settings.checks.enable(Checks::missingInclude);
Preprocessor preprocessor(settings, this);
ScopedFile header("header.h", "");
std::string code("#include \"header.h\"");
errout.str("");
preprocessor.getcode(code, "", "test.c");
ASSERT_EQUALS(false, Preprocessor::missingIncludeFlag);
ASSERT_EQUALS(false, Preprocessor::missingSystemIncludeFlag);
// the expected messages are emitted outside of the Preprocessor
ASSERT_EQUALS("", errout.str());
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
}
// test for missing local include
void testMissingInclude2() {
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
Settings settings;
settings.clearIncludeCache = true;
settings.severity.clear();
settings.checks.enable(Checks::missingInclude);
Preprocessor preprocessor(settings, this);
std::string code("#include \"header.h\"");
errout.str("");
preprocessor.getcode(code, "", "test.c");
ASSERT_EQUALS(true, Preprocessor::missingIncludeFlag);
ASSERT_EQUALS(false, Preprocessor::missingSystemIncludeFlag);
// the expected messages are emitted outside of the Preprocessor
ASSERT_EQUALS("", errout.str());
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
}
// test for missing local include - no include path given
void testMissingInclude3() {
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
Settings settings;
settings.clearIncludeCache = true;
settings.severity.clear();
settings.checks.enable(Checks::missingInclude);
Preprocessor preprocessor(settings, this);
ScopedFile header("header.h", "", "inc");
std::string code("#include \"header.h\"");
errout.str("");
preprocessor.getcode(code, "", "test.c");
ASSERT_EQUALS(true, Preprocessor::missingIncludeFlag);
ASSERT_EQUALS(false, Preprocessor::missingSystemIncludeFlag);
// the expected messages are emitted outside of the Preprocessor
ASSERT_EQUALS("", errout.str());
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
}
// test for existing local include - include path provided
void testMissingInclude4() {
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
Settings settings;
settings.clearIncludeCache = true;
settings.severity.clear();
settings.checks.enable(Checks::missingInclude);
settings.includePaths.emplace_back("inc");
Preprocessor preprocessor(settings, this);
ScopedFile header("header.h", "", "inc");
std::string code("#include \"inc/header.h\"");
errout.str("");
preprocessor.getcode(code, "", "test.c");
ASSERT_EQUALS(false, Preprocessor::missingIncludeFlag);
ASSERT_EQUALS(false, Preprocessor::missingSystemIncludeFlag);
// the expected messages are emitted outside of the Preprocessor
ASSERT_EQUALS("", errout.str());
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
}
// test for existing local include - absolute path
void testMissingInclude5() {
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
Settings settings;
settings.clearIncludeCache = true;
settings.severity.clear();
settings.checks.enable(Checks::missingInclude);
settings.includePaths.emplace_back("inc");
Preprocessor preprocessor(settings, this);
ScopedFile header("header.h", "", Path::getCurrentPath());
std::string code("#include \"" + header.path() + "\"");
errout.str("");
preprocessor.getcode(code, "", "test.c");
ASSERT_EQUALS(false, Preprocessor::missingIncludeFlag);
ASSERT_EQUALS(false, Preprocessor::missingSystemIncludeFlag);
// the expected messages are emitted outside of the Preprocessor
ASSERT_EQUALS("", errout.str());
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
}
// test for missing local include - absolute path
void testMissingInclude6() {
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
Settings settings;
settings.clearIncludeCache = true;
settings.severity.clear();
settings.checks.enable(Checks::missingInclude);
Preprocessor preprocessor(settings, this);
const std::string header = Path::join(Path::getCurrentPath(), "header.h");
std::string code("#include \"" + header + "\"");
errout.str("");
preprocessor.getcode(code, "", "test.c");
ASSERT_EQUALS(true, Preprocessor::missingIncludeFlag);
ASSERT_EQUALS(false, Preprocessor::missingSystemIncludeFlag);
// the expected messages are emitted outside of the Preprocessor
ASSERT_EQUALS("", errout.str());
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
}
// test for missing system include - system includes are not searched for in relative path
void testMissingSystemInclude() {
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
Settings settings;
settings.clearIncludeCache = true;
settings.severity.clear();
settings.checks.enable(Checks::missingInclude);
Preprocessor preprocessor(settings, this);
ScopedFile header("header.h", "");
std::string code("#include <header.h>");
errout.str("");
preprocessor.getcode(code, "", "test.c");
ASSERT_EQUALS(false, Preprocessor::missingIncludeFlag);
ASSERT_EQUALS(true, Preprocessor::missingSystemIncludeFlag);
// the expected messages are emitted outside of the Preprocessor
ASSERT_EQUALS("", errout.str());
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
}
// test for missing system include
void testMissingSystemInclude2() {
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
Settings settings;
settings.clearIncludeCache = true;
settings.severity.clear();
settings.checks.enable(Checks::missingInclude);
Preprocessor preprocessor(settings, this);
std::string code("#include <header.h>");
errout.str("");
preprocessor.getcode(code, "", "test.c");
ASSERT_EQUALS(false, Preprocessor::missingIncludeFlag);
ASSERT_EQUALS(true, Preprocessor::missingSystemIncludeFlag);
// the expected messages are emitted outside of the Preprocessor
ASSERT_EQUALS("", errout.str());
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
}
// test for existing system include in system include path
void testMissingSystemInclude3() {
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
Settings settings;
settings.clearIncludeCache = true;
settings.severity.clear();
settings.checks.enable(Checks::missingInclude);
settings.includePaths.emplace_back("system");
Preprocessor preprocessor(settings, this);
ScopedFile header("header.h", "", "system");
std::string code("#include <header.h>");
errout.str("");
preprocessor.getcode(code, "", "test.c");
ASSERT_EQUALS(false, Preprocessor::missingIncludeFlag);
ASSERT_EQUALS(false, Preprocessor::missingSystemIncludeFlag);
// the expected messages are emitted outside of the Preprocessor
ASSERT_EQUALS("", errout.str());
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
}
// test for existing system include - absolute path
void testMissingSystemInclude4() {
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
Settings settings;
settings.clearIncludeCache = true;
settings.severity.clear();
settings.checks.enable(Checks::missingInclude);
settings.includePaths.emplace_back("inc");
Preprocessor preprocessor(settings, this);
ScopedFile header("header.h", "", Path::getCurrentPath());
std::string code("#include <" + header.path() + ">");
errout.str("");
preprocessor.getcode(code, "", "test.c");
ASSERT_EQUALS(false, Preprocessor::missingIncludeFlag);
ASSERT_EQUALS(false, Preprocessor::missingSystemIncludeFlag);
// the expected messages are emitted outside of the Preprocessor
ASSERT_EQUALS("", errout.str());
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
}
// test for missing system include - absolute path
void testMissingSystemInclude5() {
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
Settings settings;
settings.clearIncludeCache = true;
settings.severity.clear();
settings.checks.enable(Checks::missingInclude);
Preprocessor preprocessor(settings, this);
const std::string header = Path::join(Path::getCurrentPath(), "header.h");
std::string code("#include <" + header + ">");
errout.str("");
preprocessor.getcode(code, "", "test.c");
ASSERT_EQUALS(false, Preprocessor::missingIncludeFlag);
ASSERT_EQUALS(true, Preprocessor::missingSystemIncludeFlag);
// the expected messages are emitted outside of the Preprocessor
ASSERT_EQUALS("", errout.str());
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
}
// test for missing local and system include
void testMissingIncludeMixed() {
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;
Settings settings;
settings.clearIncludeCache = true;
settings.severity.clear();
settings.checks.enable(Checks::missingInclude);
Preprocessor preprocessor(settings, this);
@ -2465,18 +2765,34 @@ private:
Preprocessor::missingSystemIncludeFlag = false;
Settings settings;
settings.clearIncludeCache = true;
settings.checkConfiguration = true;
settings.severity.clear();
settings.includePaths.emplace_back("system");
// needs to be reported regardless of enabled checks
Preprocessor preprocessor(settings, this);
ScopedFile header("header.h", "");
ScopedFile header2("header2.h", "");
ScopedFile header3("header3.h", "", "system");
ScopedFile header4("header4.h", "", "inc");
ScopedFile header5("header5.h", "", Path::getCurrentPath());
ScopedFile header6("header6.h", "", Path::getCurrentPath());
const std::string missing3 = Path::join(Path::getCurrentPath(), "missing3.h");
const std::string missing4 = Path::join(Path::getCurrentPath(), "missing4.h");
std::string code("#include \"missing.h\"\n"
"#include <header.h>\n"
"#include <missing2.h>\n"
"#include \"header2.h\"");
"#include \"header2.h\"\n"
"#include <header3.h>\n"
"#include \"header4.h\"\n"
"#include \"inc/header4.h\"\n"
"#include \"" + header5.path() + "\"\n"
"#include \"" + missing3 + "\"\n"
"#include <" + header6.path() + ">\n"
"#include <" + missing4 + ">\n");
errout.str("");
preprocessor.getcode(code, "", "test.c");
ASSERT_EQUALS(true, Preprocessor::missingIncludeFlag);
@ -2484,7 +2800,10 @@ private:
ASSERT_EQUALS("[test.c:1]: (information) Include file: \"missing.h\" not found.\n"
"[test.c:2]: (information) Include file: <header.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.\n"
"[test.c:3]: (information) Include file: <missing2.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.\n", errout.str());
"[test.c:3]: (information) Include file: <missing2.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.\n"
"[test.c:6]: (information) Include file: \"header4.h\" not found.\n"
"[test.c:9]: (information) Include file: \"" + missing3 + "\" not found.\n"
"[test.c:11]: (information) Include file: <" + missing4 + "> not found. Please note: Cppcheck does not need standard library headers to get proper results.\n", errout.str());
Preprocessor::missingIncludeFlag = false;
Preprocessor::missingSystemIncludeFlag = false;

View File

@ -39,6 +39,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="helpers.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="options.cpp" />
<ClCompile Include="test64bit.cpp" />