diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index f7e057da58f..076de71ef8c 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -1210,6 +1210,12 @@ static void valueFlowEnumValue(SymbolDatabase & symboldatabase, const Settings & } } +// trampoline to generate unique timer results entry +static void valueFlowEnumValueEarly(SymbolDatabase & symboldatabase, const Settings & settings) +{ + valueFlowEnumValue(symboldatabase, settings); +} + static void valueFlowGlobalConstVar(TokenList& tokenList, const Settings& settings) { // Get variable values... @@ -7200,7 +7206,7 @@ struct ValueFlowPassRunner { bool run_once(std::initializer_list> passes) const { return std::any_of(passes.begin(), passes.end(), [&](const ValuePtr& pass) { - return run(pass); + return run(pass, 0); }); } @@ -7210,10 +7216,11 @@ struct ValueFlowPassRunner { std::size_t n = state.settings.vfOptions.maxIterations; while (n > 0 && values != getTotalValues()) { values = getTotalValues(); - const std::string passnum = std::to_string(state.settings.vfOptions.maxIterations - n + 1); + const std::size_t passnum = state.settings.vfOptions.maxIterations - n + 1; + const std::string passnum_s = std::to_string(passnum); if (std::any_of(passes.begin(), passes.end(), [&](const ValuePtr& pass) { - ProgressReporter progressReporter(state.errorLogger, state.settings.reportProgress >= 0, state.tokenlist.getSourceFilePath(), std::string("ValueFlow::") + pass->name() + (' ' + passnum)); - return run(pass); + ProgressReporter progressReporter(state.errorLogger, state.settings.reportProgress >= 0, state.tokenlist.getSourceFilePath(), std::string("ValueFlow::") + pass->name() + (' ' + passnum_s)); + return run(pass, passnum); })) return true; --n; @@ -7233,7 +7240,7 @@ struct ValueFlowPassRunner { return false; } - bool run(const ValuePtr& pass) const + bool run(const ValuePtr& pass, std::size_t it) const { auto start = Clock::now(); if (start > stop) { @@ -7243,7 +7250,12 @@ struct ValueFlowPassRunner { if (!state.tokenlist.isCPP() && pass->cpp()) return false; if (timerResults) { - Timer t(pass->name(), state.settings.showtime, timerResults); + std::string name = pass->name(); + if (it > 0) { + name += ' '; + name += std::to_string(it); + } + Timer t(name, state.settings.showtime, timerResults); pass->run(state); } else { pass->run(state); @@ -7375,7 +7387,7 @@ void ValueFlow::setValues(TokenList& tokenlist, ValueFlowPassRunner runner{ValueFlowState{tokenlist, symboldatabase, errorLogger, settings}, timerResults}; runner.run_once({ - VFA(valueFlowEnumValue(symboldatabase, settings)), + VFA(valueFlowEnumValueEarly(symboldatabase, settings)), VFA(valueFlowNumber(tokenlist, settings)), VFA(valueFlowString(tokenlist, settings)), VFA(valueFlowTypeTraits(tokenlist, settings)), diff --git a/test/cli/other_test.py b/test/cli/other_test.py index 2ff65efc052..0ad82605eaf 100644 --- a/test/cli/other_test.py +++ b/test/cli/other_test.py @@ -954,8 +954,11 @@ def test_unused_function_include(tmpdir): __test_unused_function_include(tmpdir, []) +# TODO: test with clang-tidy +# TODO: test with --addon +# TODO: test with FileSettings # TODO: test with multiple files -def __test_showtime(tmp_path, showtime, exp_len, exp_last, extra_args=None): +def __test_showtime(tmp_path, showtime, exp_res, exp_last, extra_args=None): test_file = tmp_path / 'test.cpp' with open(test_file, 'wt') as f: f.write( @@ -979,37 +982,40 @@ def __test_showtime(tmp_path, showtime, exp_len, exp_last, extra_args=None): exitcode, stdout, stderr = cppcheck(args) assert exitcode == 0 lines = stdout.splitlines() + exp_len = exp_res + if exp_res: + exp_len += 1 # empty line at the beginning - only added when individual results exist if 'cppcheck internal API usage' in stdout: exp_len += 1 + exp_len += 1 # last line assert len(lines) == exp_len - idx_last = exp_len-1 - if idx_last: + if exp_res: assert lines[0] == '' - for i in range(1, idx_last): + for i in range(1, exp_res): assert 'avg.' in lines[i] - assert lines[idx_last].startswith(exp_last) + assert lines[exp_len-1].startswith(exp_last) assert stderr == '' def test_showtime_top5_file(tmp_path): - __test_showtime(tmp_path, 'top5_file', 7, 'Check time: ') + __test_showtime(tmp_path, 'top5_file', 5, 'Check time: ') # TODO: remove extra args when --executor=process works works def test_showtime_top5_summary(tmp_path): - __test_showtime(tmp_path, 'top5_summary', 7, 'Overall time: ', ['-j1']) + __test_showtime(tmp_path, 'top5_summary', 5, 'Overall time: ', ['-j1']) # TODO: remove when --executor=process works works def test_showtime_top5_summary_j_thread(tmp_path): - __test_showtime(tmp_path, 'top5_summary', 7, 'Overall time: ', ['-j2', '--executor=thread']) + __test_showtime(tmp_path, 'top5_summary', 5, 'Overall time: ', ['-j2', '--executor=thread']) # TODO: remove override when fixed @pytest.mark.skipif(sys.platform == 'win32', reason="requires ProcessExecutor") @pytest.mark.xfail(strict=True) # TODO: need to transfer the timer results to parent process - see #4452 def test_showtime_top5_summary_j_process(tmp_path): - __test_showtime(tmp_path, 'top5_summary', 7, 'Overall time: ', ['-j2', '--executor=process']) + __test_showtime(tmp_path, 'top5_summary', 5, 'Overall time: ', ['-j2', '--executor=process']) def test_showtime_file(tmp_path): @@ -1034,7 +1040,39 @@ def test_showtime_summary_j_process(tmp_path): def test_showtime_file_total(tmp_path): - __test_showtime(tmp_path, 'file-total', 1, 'Check time: ') + __test_showtime(tmp_path, 'file-total', 0, 'Check time: ') + + +def test_showtime_unique(tmp_path): + test_file = tmp_path / 'test.cpp' + with open(test_file, 'wt') as f: + f.write( +""" +void f() +{ + (void)(*((int*)0)); // cppcheck-suppress nullPointer +} +""") + + args = [ + '--showtime=summary', + '--quiet', + '--inline-suppr', + str(test_file) + ] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + multi_res = [] + for line in stdout.splitlines(): + # TODO: remove when we no longer emit empty line + if not line: + continue + if any(i in line for i in ['1 result(s)', 'Overall time:']): + continue + multi_res.append(line) + assert multi_res == [] + assert stderr == '' def test_missing_addon(tmpdir):