Skip to content

Commit bb2bb4f

Browse files
authored
Fix #11824 (Option --max-configs has no effect if -D is used) (#7980)
Number of checked configurations: cppcheck file.c => 12 cppcheck -DX file.c => 1 cppcheck -DX --max-configs=6 file.c => 6 move logic from cmdlineparser to Settings class where the code can be reused (from GUI). The `Settings::checkAllConfigs` is removed.
1 parent d1e4660 commit bb2bb4f

File tree

10 files changed

+137
-50
lines changed

10 files changed

+137
-50
lines changed

cli/cmdlineparser.cpp

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,6 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
407407
}
408408
}
409409

410-
bool def = false;
411-
bool maxconfigs = false;
412410
bool debug = false;
413411
bool inputAsFilter = false; // set by: --file-filter=+
414412

@@ -457,8 +455,6 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
457455
if (!mSettings.userDefines.empty())
458456
mSettings.userDefines += ";";
459457
mSettings.userDefines += define;
460-
461-
def = true;
462458
}
463459

464460
// -E
@@ -801,8 +797,10 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
801797
}
802798

803799
// Force checking of files that have "too many" configurations
804-
else if (std::strcmp(argv[i], "-f") == 0 || std::strcmp(argv[i], "--force") == 0)
800+
else if (std::strcmp(argv[i], "-f") == 0 || std::strcmp(argv[i], "--force") == 0) {
805801
mSettings.force = true;
802+
mSettings.maxConfigsOption = Settings::maxConfigsNotAssigned;
803+
}
806804

807805
else if (std::strcmp(argv[i], "--fsigned-char") == 0)
808806
defaultSign = 's';
@@ -974,9 +972,8 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
974972
return Result::Fail;
975973
}
976974

977-
mSettings.maxConfigs = tmp;
975+
mSettings.maxConfigsOption = tmp;
978976
mSettings.force = false;
979-
maxconfigs = true;
980977
}
981978

982979
// max ctu depth
@@ -1160,7 +1157,6 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
11601157
return Result::Fail;
11611158
}
11621159

1163-
mSettings.checkAllConfigurations = false; // Can be overridden with --max-configs or --force
11641160
std::string projectFile = argv[i]+10;
11651161
projectType = project.import(projectFile, &mSettings, &mSuppressions);
11661162
if (projectType == ImportProject::Type::CPPCHECK_GUI) {
@@ -1187,6 +1183,8 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
11871183
}
11881184
}
11891185
}
1186+
if (projectType == ImportProject::Type::COMPILE_DB)
1187+
mSettings.maxConfigsProject = 1;
11901188
if (projectType == ImportProject::Type::VS_SLN || projectType == ImportProject::Type::VS_VCXPROJ) {
11911189
mSettings.libraries.emplace_back("windows");
11921190
}
@@ -1591,14 +1589,6 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
15911589
substituteTemplateFormatStatic(mSettings.templateFormat, !mSettings.outputFile.empty());
15921590
substituteTemplateLocationStatic(mSettings.templateLocation, !mSettings.outputFile.empty());
15931591

1594-
if (mSettings.force || maxconfigs)
1595-
mSettings.checkAllConfigurations = true;
1596-
1597-
if (mSettings.force)
1598-
mSettings.maxConfigs = INT_MAX;
1599-
else if ((def || mSettings.preprocessOnly) && !maxconfigs)
1600-
mSettings.maxConfigs = 1U;
1601-
16021592
if (debug) {
16031593
mSettings.debugnormal = true;
16041594
mSettings.debugvalueflow = true;

gui/mainwindow.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,13 +1136,9 @@ bool MainWindow::getCppcheckSettings(Settings& settings, Suppressions& supprs)
11361136
supprs.nomsg.addSuppression(suppression); // TODO: check result
11371137
}
11381138

1139-
// Only check the given -D configuration
1140-
if (!defines.isEmpty())
1141-
settings.maxConfigs = 1;
1142-
11431139
// If importing a project, only check the given configuration
1144-
if (!mProjectFile->getImportProject().isEmpty())
1145-
settings.checkAllConfigurations = false;
1140+
if (mProjectFile->getImportProject().endsWith("json", Qt::CaseInsensitive))
1141+
settings.maxConfigsProject = 1;
11461142

11471143
const QString &buildDir = fromNativePath(mProjectFile->getBuildDir());
11481144
if (!buildDir.isEmpty()) {

lib/cppcheck.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ std::size_t CppCheck::calculateHash(const Preprocessor& preprocessor, const std:
859859
toolinfo << mSettings.userDefines;
860860
toolinfo << (mSettings.checkConfiguration ? 'c' : ' '); // --check-config
861861
toolinfo << (mSettings.force ? 'f' : ' ');
862-
toolinfo << mSettings.maxConfigs;
862+
toolinfo << mSettings.maxConfigsOption;
863863
toolinfo << std::to_string(static_cast<std::uint8_t>(mSettings.checkLevel));
864864
for (const auto &a : mSettings.addonInfos) {
865865
toolinfo << a.name;
@@ -908,6 +908,8 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str
908908
if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mUnusedFunctionsCheck)
909909
mUnusedFunctionsCheck.reset(new CheckUnusedFunctions());
910910

911+
const int maxConfigs = mSettings.getMaxConfigs();
912+
911913
mLogger->resetExitCode();
912914

913915
if (Settings::terminated())
@@ -1032,7 +1034,7 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str
10321034

10331035
// Get configurations..
10341036
std::set<std::string> configurations;
1035-
if ((mSettings.checkAllConfigurations && mSettings.userDefines.empty()) || mSettings.force) {
1037+
if (maxConfigs > 1) {
10361038
Timer::run("Preprocessor::getConfigs", mSettings.showtime, &s_timerResults, [&]() {
10371039
configurations = preprocessor.getConfigs();
10381040
});
@@ -1044,7 +1046,7 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str
10441046
for (const std::string &config : configurations)
10451047
(void)preprocessor.getcode(config, files, false);
10461048

1047-
if (configurations.size() > mSettings.maxConfigs)
1049+
if (configurations.size() > maxConfigs)
10481050
tooManyConfigsError(Path::toNativeSeparators(file.spath()), configurations.size());
10491051

10501052
if (analyzerInformation)
@@ -1090,14 +1092,13 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str
10901092

10911093
// Check only a few configurations (default 12), after that bail out, unless --force
10921094
// was used.
1093-
if (!mSettings.force && ++checkCount > mSettings.maxConfigs) {
1094-
// If maxConfigs has default value then report information message that configurations are skipped.
1095-
// If maxConfigs does not have default value then the user is explicitly skipping configurations so
1095+
if (!mSettings.force && ++checkCount > maxConfigs) {
1096+
// If maxConfigs is not assigned then report information message that configurations are skipped.
1097+
// If maxConfigs is assigned then the user is explicitly skipping configurations so
10961098
// the information message is not reported, the whole purpose of setting i.e. --max-configs=1 is to
10971099
// skip configurations. When --check-config is used then tooManyConfigs will be reported even if the
10981100
// value is non-default.
1099-
const Settings defaultSettings;
1100-
if (mSettings.maxConfigs == defaultSettings.maxConfigs && mSettings.severity.isEnabled(Severity::information))
1101+
if (!mSettings.isMaxConfigsAssigned() && mSettings.severity.isEnabled(Severity::information))
11011102
tooManyConfigsError(Path::toNativeSeparators(file.spath()), configurations.size());
11021103

11031104
break;
@@ -1198,7 +1199,7 @@ unsigned int CppCheck::checkInternal(const FileWithDetails& file, const std::str
11981199
}
11991200

12001201
// Skip if we already met the same simplified token list
1201-
if (mSettings.force || mSettings.maxConfigs > 1) {
1202+
if (maxConfigs > 1) {
12021203
const std::size_t hash = tokenizer.list.calculateHash();
12031204
if (hashes.find(hash) != hashes.end()) {
12041205
if (mSettings.debugwarnings)
@@ -1644,7 +1645,7 @@ void CppCheck::tooManyConfigsError(const std::string &file, const int numberOfCo
16441645
}
16451646

16461647
std::ostringstream msg;
1647-
msg << "Too many #ifdef configurations - cppcheck only checks " << mSettings.maxConfigs
1648+
msg << "Too many #ifdef configurations - cppcheck only checks " << mSettings.getMaxConfigs()
16481649
<< " of " << numberOfConfigurations << " configurations. Use --force to check all configurations.";
16491650

16501651
ErrorMessage errmsg(std::move(loclist),

lib/settings.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343

4444
std::atomic<bool> Settings::mTerminated;
4545

46+
const int Settings::maxConfigsNotAssigned = 0;
47+
const int Settings::maxConfigsDefault = 12;
48+
4649
const char Settings::SafeChecks::XmlRootName[] = "safe-checks";
4750
const char Settings::SafeChecks::XmlClasses[] = "class-public";
4851
const char Settings::SafeChecks::XmlExternalFunctions[] = "external-functions";

lib/settings.h

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,6 @@ class CPPCHECKLIB WARN_UNUSED Settings {
136136
/** @brief --cppcheck-build-dir. Always uses / as path separator. No trailing path separator. */
137137
std::string buildDir;
138138

139-
/** @brief check all configurations (false if -D or --max-configs is used */
140-
bool checkAllConfigurations = true;
141-
142139
/** Is the 'configuration checking' wanted? */
143140
bool checkConfiguration{};
144141

@@ -294,9 +291,31 @@ class CPPCHECKLIB WARN_UNUSED Settings {
294291
int loadAverage{};
295292
#endif
296293

297-
/** @brief Maximum number of configurations to check before bailing.
298-
Default is 12. (--max-configs=N) */
299-
int maxConfigs = 12;
294+
/** --max-configs value */
295+
int maxConfigsOption = 0; // "Not Assigned" value
296+
297+
/** max configs from --project option */
298+
int maxConfigsProject = 0; // "Not Assigned" value
299+
300+
static const int maxConfigsNotAssigned;
301+
static const int maxConfigsDefault;
302+
303+
bool isMaxConfigsAssigned() const {
304+
return maxConfigsOption != maxConfigsNotAssigned || maxConfigsProject != maxConfigsNotAssigned;
305+
}
306+
307+
/** @brief Maximum number of configurations to check before bailing. */
308+
int getMaxConfigs() const {
309+
if (force)
310+
return 0x7fffffff;
311+
if (maxConfigsOption != maxConfigsNotAssigned)
312+
return maxConfigsOption;
313+
if (maxConfigsProject != maxConfigsNotAssigned)
314+
return maxConfigsProject;
315+
if (!userDefines.empty())
316+
return 1;
317+
return maxConfigsDefault;
318+
}
300319

301320
/** @brief --max-ctu-depth */
302321
int maxCtuDepth = 2;

man/manual.md

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -287,16 +287,21 @@ To ignore certain folders in the project you can use `-i`. This will skip the an
287287

288288
cppcheck --project=foobar.cppcheck -ifoo
289289

290-
## CMake
290+
## Compilation database (cmake etc)
291291

292-
Generate a compile database (a JSON file containing compilation commands for each source file):
292+
Many build systems can generate a compilation database (a JSON file containing compilation commands for each source file).
293+
Example `cmake` command to generate the file:
293294

294295
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .
295296

296-
The file `compile_commands.json` is created in the current folder. Now run Cppcheck like this:
297+
When you have a `compile_commands.json` file you can run Cppcheck like this:
297298

298299
cppcheck --project=compile_commands.json
299300

301+
By default only 1 configuration is checked because that is consistent with the compilation. If you want to check more configurations you can use `--max-configs` or `--force`. For example:
302+
303+
cppcheck --project=compile_commands.json --force
304+
300305
To ignore certain folders you can use `-i`. This will skip analysis of source files in the `foo` folder.
301306

302307
cppcheck --project=compile_commands.json -ifoo
@@ -338,12 +343,16 @@ To ignore certain folders in the project you can use `-i`. This will skip analys
338343

339344
## Other
340345

341-
If you can generate a compile database, then it is possible to import that in Cppcheck.
346+
If you generate a compilation database, then it is possible to import that in Cppcheck.
347+
348+
### Makefile
342349

343-
In Linux you can use for instance the `bear` (build ear) utility to generate a compile database from arbitrary build tools:
350+
In Linux you can convert a Makefile to a compile_commands.json using for instance `bear` (build ear) utility:
344351

345352
bear -- make
346353

354+
If you don't use Linux; there are python scripts that converts a Makefile into a compilation database.
355+
347356
# Preprocessor Settings
348357

349358
If you use `--project` then Cppcheck will automatically use the preprocessor settings in the imported project file and
@@ -388,21 +397,28 @@ Example:
388397
cppcheck test.c
389398

390399
# only test configuration "-DA"
391-
# No bug is found (#error)
400+
# No bug is found; because C is not defined the #error will cause a preprocessor error
392401
cppcheck -DA test.c
393402

394403
# only test configuration "-DA -DC"
395404
# The first bug is found
396405
cppcheck -DA -DC test.c
397406

398-
# The configuration "-DC" is tested
407+
# Test all configurations that does not define "A"
399408
# The last bug is found
400409
cppcheck -UA test.c
401410

402411
# All configurations with "-DA" are tested
403412
# The two first bugs are found
404413
cppcheck --force -DA test.c
405414

415+
# only test 1 valid configuration
416+
# Bug(s) will be found
417+
cppcheck --max-configs=1 test.c
418+
419+
# test 2 valid configurations with "X" defined.
420+
# Bug(s) will be found
421+
cppcheck --max-configs=2 -DX test.c
406422

407423
## Include paths
408424

test/cli/helloworld_test.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,10 @@ def test_addon_with_gui_project(tmp_path):
123123
ret, stdout, stderr = cppcheck(args, cwd=tmp_path)
124124
filename = os.path.join('helloworld', 'main.c')
125125
assert ret == 0, stdout
126-
assert stdout == 'Checking %s ...\n' % filename
126+
assert stdout.strip().split('\n') == [
127+
'Checking %s ...' % filename,
128+
'Checking %s: SOME_CONFIG...' % filename
129+
]
127130
assert stderr == ('[%s:5]: (error) Division by zero.\n'
128131
'[%s:4]: (style) misra violation (use --rule-texts=<file> to get proper output)\n' % (filename, filename))
129132

test/cli/other_test.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3952,8 +3952,8 @@ def test_simplecpp_syntax_error(tmp_path):
39523952

39533953
@pytest.mark.parametrize('max_configs,number_of_configs,check_config,expected_warn', [
39543954
# max configs = default, max configs < number of configs => warn
3955-
(12, 20, False, True),
3956-
(12, 20, True, True),
3955+
(None, 20, False, True),
3956+
(None, 20, True, True),
39573957
39583958
# max configs != default, max configs < number of configs => warn if --check-config
39593959
(6, 20, False, False),
@@ -3971,7 +3971,12 @@ def test_max_configs(tmp_path, max_configs, number_of_configs, check_config, exp
39713971
f.write(f'#{dir} defined(X{i})\nx = {i};\n')
39723972
f.write('#endif\n')
39733973

3974-
args = [f'--max-configs={max_configs}', '--enable=information', '--template=simple', str(test_file)]
3974+
args = ['--enable=information', '--template=simple', str(test_file)]
3975+
3976+
if max_configs is None:
3977+
max_configs = 12 # default value
3978+
else:
3979+
args = [f'--max-configs={max_configs}'] + args
39753980

39763981
if check_config:
39773982
args = ['--check-config'] + args

test/testcmdlineparser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1415,7 +1415,7 @@ class TestCmdlineParser : public TestFixture {
14151415
REDIRECT;
14161416
const char * const argv[] = {"cppcheck", "-f", "--max-configs=12", "file.cpp"};
14171417
ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parseFromArgs(argv));
1418-
ASSERT_EQUALS(12, settings->maxConfigs);
1418+
ASSERT_EQUALS(12, settings->maxConfigsOption);
14191419
ASSERT_EQUALS(false, settings->force);
14201420
}
14211421

0 commit comments

Comments
 (0)