added command-line option `--disable=<id>` to disable individual checks (#4712)

This commit is contained in:
Oliver Stöneberg 2023-01-21 10:39:44 +01:00 committed by GitHub
parent 7945b835e1
commit 38abeccd24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 300 additions and 9 deletions

View File

@ -301,6 +301,7 @@ TESTOBJ = test/options.o \
test/testpreprocessor.o \
test/testprocessexecutor.o \
test/testrunner.o \
test/testsettings.o \
test/testsimplifytemplate.o \
test/testsimplifytokens.o \
test/testsimplifytypedef.o \
@ -768,6 +769,9 @@ test/testprocessexecutor.o: test/testprocessexecutor.cpp cli/executor.h cli/proc
test/testrunner.o: test/testrunner.cpp externals/simplecpp/simplecpp.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/preprocessor.h lib/suppressions.h test/options.h test/testsuite.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testrunner.cpp
test/testsettings.o: test/testsettings.cpp 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/timer.h lib/utils.h test/testsuite.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsettings.cpp
test/testsimplifytemplate.o: test/testsimplifytemplate.cpp 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/valueflow.h test/testsuite.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsimplifytemplate.cpp

View File

@ -299,6 +299,14 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
else if (std::strcmp(argv[i], "--debug-warnings") == 0)
mSettings->debugwarnings = true;
else if (std::strncmp(argv[i], "--disable=", 10) == 0) {
const std::string errmsg = mSettings->removeEnabled(argv[i] + 10);
if (!errmsg.empty()) {
printError(errmsg);
return false;
}
}
// documentation..
else if (std::strcmp(argv[i], "--doc") == 0) {
std::ostringstream doc;
@ -321,13 +329,14 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
mSettings->dump = true;
else if (std::strncmp(argv[i], "--enable=", 9) == 0) {
const std::string errmsg = mSettings->addEnabled(argv[i] + 9);
const std::string enable_arg = argv[i] + 9;
const std::string errmsg = mSettings->addEnabled(enable_arg);
if (!errmsg.empty()) {
printError(errmsg);
return false;
}
// when "style" is enabled, also enable "warning", "performance" and "portability"
if (mSettings->severity.isEnabled(Severity::style)) {
if (enable_arg.find("style") != std::string::npos) {
mSettings->addEnabled("warning");
mSettings->addEnabled("performance");
mSettings->addEnabled("portability");
@ -1084,6 +1093,9 @@ void CmdLineParser::printHelp()
" be considered for evaluation.\n"
" --config-excludes-file=<file>\n"
" A file that contains a list of config-excludes\n"
" --disable=<id> Disable individual checks.\n"
" Please refer to the documentation of --enable=<id>\n"
" for futher details.\n"
" --dump Dump xml data for each translation unit. The dump\n"
" files have the extension .dump and contain ast,\n"
" tokenlist, symboldatabase, valueflow.\n"

View File

@ -113,7 +113,7 @@ void Settings::loadCppcheckCfg()
}
}
std::string Settings::addEnabled(const std::string &str)
std::string Settings::parseEnabled(const std::string &str, std::tuple<SimpleEnableGroup<Severity::SeverityType>, SimpleEnableGroup<Checks>> &groups)
{
// Enable parameters may be comma separated...
if (str.find(',') != std::string::npos) {
@ -122,7 +122,7 @@ std::string Settings::addEnabled(const std::string &str)
while ((pos = str.find(',', pos)) != std::string::npos) {
if (pos == prevPos)
return std::string("--enable parameter is empty");
const std::string errmsg(addEnabled(str.substr(prevPos, pos - prevPos)));
std::string errmsg(parseEnabled(str.substr(prevPos, pos - prevPos), groups));
if (!errmsg.empty())
return errmsg;
++pos;
@ -130,11 +130,18 @@ std::string Settings::addEnabled(const std::string &str)
}
if (prevPos >= str.length())
return std::string("--enable parameter is empty");
return addEnabled(str.substr(prevPos));
return parseEnabled(str.substr(prevPos), groups);
}
auto& severity = std::get<0>(groups);
auto& checks = std::get<1>(groups);
if (str == "all") {
severity.fill();
// "error" is always enabled and cannot be controlled - so exclude it from "all"
SimpleEnableGroup<Severity::SeverityType> newSeverity;
newSeverity.fill();
newSeverity.disable(Severity::SeverityType::error);
severity.enable(newSeverity);
checks.enable(Checks::missingInclude);
checks.enable(Checks::unusedFunction);
} else if (str == "warning") {
@ -159,13 +166,46 @@ std::string Settings::addEnabled(const std::string &str)
}
#endif
else {
// the actual option is prepending in the applyEnabled() call
if (str.empty())
return std::string("cppcheck: --enable parameter is empty");
return " parameter is empty";
else
return std::string("cppcheck: there is no --enable parameter with the name '" + str + "'");
return " parameter with the unknown name '" + str + "'";
}
return std::string();
return "";
}
std::string Settings::addEnabled(const std::string &str)
{
return applyEnabled(str, true);
}
std::string Settings::removeEnabled(const std::string &str)
{
return applyEnabled(str, false);
}
std::string Settings::applyEnabled(const std::string &str, bool enable)
{
std::tuple<SimpleEnableGroup<Severity::SeverityType>, SimpleEnableGroup<Checks>> groups;
std::string errmsg = parseEnabled(str, groups);
if (!errmsg.empty())
return (enable ? "--enable" : "--disable") + errmsg;
const auto s = std::get<0>(groups);
const auto c = std::get<1>(groups);
if (enable) {
severity.enable(s);
checks.enable(c);
}
else {
severity.disable(s);
checks.disable(c);
}
// FIXME: hack to make sure "error" is always enabled
severity.enable(Severity::SeverityType::error);
return errmsg;
}
bool Settings::isEnabled(const ValueFlow::Value *value, bool inconclusiveCheck) const

View File

@ -36,6 +36,7 @@
#include <list>
#include <set>
#include <string>
#include <tuple>
#include <vector>
#include <unordered_set>
@ -71,9 +72,15 @@ public:
void enable(T flag) {
mFlags |= (1U << (uint32_t)flag);
}
void enable(SimpleEnableGroup<T> group) {
mFlags |= group.intValue();
}
void disable(T flag) {
mFlags &= ~(1U << (uint32_t)flag);
}
void disable(SimpleEnableGroup<T> group) {
mFlags &= ~(group.intValue());
}
void setEnabled(T flag, bool enabled) {
if (enabled)
enable(flag);
@ -393,6 +400,14 @@ public:
*/
std::string addEnabled(const std::string &str);
/**
* @brief Disable extra checks by id
* @param str single id or list of id values to be enabled
* or empty string to enable all. e.g. "style,possibleError"
* @return error message. empty upon success
*/
std::string removeEnabled(const std::string &str);
/**
* @brief Returns true if given value can be shown
* @return true if the value can be shown
@ -417,6 +432,10 @@ public:
std::set<std::string> summaryReturn;
void loadSummaries();
private:
static std::string parseEnabled(const std::string &str, std::tuple<SimpleEnableGroup<Severity::SeverityType>, SimpleEnableGroup<Checks>> &groups);
std::string applyEnabled(const std::string &str, bool enable);
};
/// @}

View File

@ -105,6 +105,9 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
<arg choice="opt">
<option>-U&lt;id&gt;</option>
</arg>
<arg choice="opt">
<option>--disable=&lt;id&gt;</option>
</arg>
<arg choice="opt">
<option>--enable=&lt;id&gt;</option>
</arg>
@ -272,6 +275,15 @@ Example: -DDEBUG=1 -D__cplusplus</para>
Example: '-UDEBUG'</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>--disable=&lt;id&gt;</option>
</term>
<listitem>
<para>Disable individual checks. Please refer to the documentation of --enable=&lt;id&gt; for futher details.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>--enable=&lt;id&gt;</option>

View File

@ -2,3 +2,4 @@ release notes for cppcheck-2.10
- the deprecated Makefile option SRCDIR is no longer accepted
- if the file provided via "--file-list" cannot be opened it will now error out
- add command-line option "--disable=<id>" to individually disable checks

View File

@ -88,6 +88,15 @@ private:
TEST_CASE(enabledInternal);
#endif
TEST_CASE(enabledMultiple);
TEST_CASE(enabledInvalid);
TEST_CASE(enabledError);
TEST_CASE(enabledEmpty);
TEST_CASE(disableAll);
TEST_CASE(disableMultiple);
TEST_CASE(disableStylePartial);
TEST_CASE(disableInvalid);
TEST_CASE(disableError);
TEST_CASE(disableEmpty);
TEST_CASE(inconclusive);
TEST_CASE(errorExitcode);
TEST_CASE(errorExitcodeMissing);
@ -654,6 +663,109 @@ private:
ASSERT_EQUALS("", GET_REDIRECT_OUTPUT);
}
void enabledInvalid() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--enable=warning,missingIncludeSystem,style", "file.cpp"};
settings = Settings();
ASSERT(!defParser.parseFromArgs(3, argv));
ASSERT_EQUALS("cppcheck: error: --enable parameter with the unknown name 'missingIncludeSystem'\n", GET_REDIRECT_OUTPUT);
}
void enabledError() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--enable=error", "file.cpp"};
settings = Settings();
ASSERT(!defParser.parseFromArgs(3, argv));
ASSERT_EQUALS("cppcheck: error: --enable parameter with the unknown name 'error'\n", GET_REDIRECT_OUTPUT);
}
void enabledEmpty() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--enable=", "file.cpp"};
settings = Settings();
ASSERT(!defParser.parseFromArgs(3, argv));
ASSERT_EQUALS("cppcheck: error: --enable parameter is empty\n", GET_REDIRECT_OUTPUT);
}
void disableAll() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--enable=all", "--disable=all"};
settings.severity.clear();
settings.checks.clear();
ASSERT(defParser.parseFromArgs(3, argv));
ASSERT_EQUALS(true, settings.severity.isEnabled(Severity::error));
ASSERT_EQUALS(false, settings.severity.isEnabled(Severity::warning));
ASSERT_EQUALS(false, settings.severity.isEnabled(Severity::style));
ASSERT_EQUALS(false, settings.severity.isEnabled(Severity::performance));
ASSERT_EQUALS(false, settings.severity.isEnabled(Severity::portability));
ASSERT_EQUALS(false, settings.severity.isEnabled(Severity::debug));
ASSERT_EQUALS(false, settings.checks.isEnabled(Checks::unusedFunction));
ASSERT_EQUALS(false, settings.checks.isEnabled(Checks::missingInclude));
ASSERT_EQUALS(false, settings.checks.isEnabled(Checks::internalCheck));
ASSERT_EQUALS("", GET_REDIRECT_OUTPUT);
}
void disableMultiple() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--enable=all", "--disable=style", "--disable=unusedFunction"};
settings.severity.clear();
settings.checks.clear();
ASSERT(defParser.parseFromArgs(4, argv));
ASSERT_EQUALS(true, settings.severity.isEnabled(Severity::error));
ASSERT_EQUALS(true, settings.severity.isEnabled(Severity::warning));
ASSERT_EQUALS(false, settings.severity.isEnabled(Severity::style));
ASSERT_EQUALS(true, settings.severity.isEnabled(Severity::performance));
ASSERT_EQUALS(true, settings.severity.isEnabled(Severity::portability));
ASSERT_EQUALS(true, settings.severity.isEnabled(Severity::debug));
ASSERT_EQUALS(false, settings.checks.isEnabled(Checks::unusedFunction));
ASSERT_EQUALS(true, settings.checks.isEnabled(Checks::missingInclude));
ASSERT_EQUALS(false, settings.checks.isEnabled(Checks::internalCheck));
ASSERT_EQUALS("", GET_REDIRECT_OUTPUT);
}
// make sure the implied "style" checks are not added when "--enable=style" is specified
void disableStylePartial() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--enable=style", "--disable=performance", "--enable=unusedFunction"};
settings.severity.clear();
settings.checks.clear();
ASSERT(defParser.parseFromArgs(4, argv));
ASSERT_EQUALS(true, settings.severity.isEnabled(Severity::error));
ASSERT_EQUALS(true, settings.severity.isEnabled(Severity::warning));
ASSERT_EQUALS(true, settings.severity.isEnabled(Severity::style));
ASSERT_EQUALS(false, settings.severity.isEnabled(Severity::performance));
ASSERT_EQUALS(true, settings.severity.isEnabled(Severity::portability));
ASSERT_EQUALS(false, settings.severity.isEnabled(Severity::debug));
ASSERT_EQUALS(true, settings.checks.isEnabled(Checks::unusedFunction));
ASSERT_EQUALS(false, settings.checks.isEnabled(Checks::missingInclude));
ASSERT_EQUALS(false, settings.checks.isEnabled(Checks::internalCheck));
ASSERT_EQUALS("", GET_REDIRECT_OUTPUT);
}
void disableInvalid() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--disable=leaks", "file.cpp"};
settings = Settings();
ASSERT(!defParser.parseFromArgs(3, argv));
ASSERT_EQUALS("cppcheck: error: --disable parameter with the unknown name 'leaks'\n", GET_REDIRECT_OUTPUT);
}
void disableError() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--disable=error", "file.cpp"};
settings = Settings();
ASSERT(!defParser.parseFromArgs(3, argv));
ASSERT_EQUALS("cppcheck: error: --disable parameter with the unknown name 'error'\n", GET_REDIRECT_OUTPUT);
}
void disableEmpty() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--disable=", "file.cpp"};
settings = Settings();
ASSERT(!defParser.parseFromArgs(3, argv));
ASSERT_EQUALS("cppcheck: error: --disable parameter is empty\n", GET_REDIRECT_OUTPUT);
}
void inconclusive() {
REDIRECT;
const char * const argv[] = {"cppcheck", "--inconclusive"};

View File

@ -71,6 +71,7 @@
<ClCompile Include="testpreprocessor.cpp" />
<ClCompile Include="testprocessexecutor.cpp" />
<ClCompile Include="testrunner.cpp" />
<ClCompile Include="testsettings.cpp" />
<ClCompile Include="testsimplifytemplate.cpp" />
<ClCompile Include="testsimplifytokens.cpp" />
<ClCompile Include="testsimplifytypedef.cpp" />

90
test/testsettings.cpp Normal file
View File

@ -0,0 +1,90 @@
/*
* 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 "errortypes.h"
#include "settings.h"
#include "testsuite.h"
class TestSettings : public TestFixture {
public:
TestSettings() : TestFixture("TestSettings") {}
private:
void run() override {
TEST_CASE(simpleEnableGroup);
}
void simpleEnableGroup() const {
SimpleEnableGroup<Checks> group;
ASSERT_EQUALS(0, group.intValue());
ASSERT_EQUALS(false, group.isEnabled(Checks::unusedFunction));
ASSERT_EQUALS(false, group.isEnabled(Checks::missingInclude));
ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck));
group.fill();
ASSERT_EQUALS(4294967295, group.intValue());
ASSERT_EQUALS(true, group.isEnabled(Checks::unusedFunction));
ASSERT_EQUALS(true, group.isEnabled(Checks::missingInclude));
ASSERT_EQUALS(true, group.isEnabled(Checks::internalCheck));
group.clear();
ASSERT_EQUALS(0, group.intValue());
ASSERT_EQUALS(false, group.isEnabled(Checks::unusedFunction));
ASSERT_EQUALS(false, group.isEnabled(Checks::missingInclude));
ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck));
group.enable(Checks::unusedFunction);
group.setEnabled(Checks::missingInclude, true);
ASSERT_EQUALS(3, group.intValue());
ASSERT_EQUALS(true, group.isEnabled(Checks::unusedFunction));
ASSERT_EQUALS(true, group.isEnabled(Checks::missingInclude));
ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck));
group.disable(Checks::unusedFunction);
group.setEnabled(Checks::missingInclude, false);
ASSERT_EQUALS(0, group.intValue());
ASSERT_EQUALS(false, group.isEnabled(Checks::unusedFunction));
ASSERT_EQUALS(false, group.isEnabled(Checks::missingInclude));
ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck));
SimpleEnableGroup<Checks> newGroup;
newGroup.enable(Checks::missingInclude);
group.enable(newGroup);
ASSERT_EQUALS(2, group.intValue());
ASSERT_EQUALS(false, group.isEnabled(Checks::unusedFunction));
ASSERT_EQUALS(true, group.isEnabled(Checks::missingInclude));
ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck));
group.disable(newGroup);
ASSERT_EQUALS(0, group.intValue());
ASSERT_EQUALS(false, group.isEnabled(Checks::unusedFunction));
ASSERT_EQUALS(false, group.isEnabled(Checks::missingInclude));
ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck));
}
};
REGISTER_TEST(TestSettings)