valueflow: limit valueflow in functions that have many ifs
This commit is contained in:
parent
eb0998dc2c
commit
2359b9ff82
|
@ -23,3 +23,5 @@ autoNoType
|
|||
bailoutUninitVar
|
||||
|
||||
*:externals/*/*
|
||||
|
||||
performanceValueflowMaxIfCountExceeded
|
||||
|
|
|
@ -606,6 +606,9 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
|
|||
else if (std::strncmp(argv[i], "--performance-valueflow-max-time=", 33) == 0)
|
||||
mSettings.performanceValueFlowMaxTime = std::atoi(argv[i] + 33);
|
||||
|
||||
else if (std::strncmp(argv[i], "--performance-valueflow-max-if-count=", 37) == 0)
|
||||
mSettings.performanceValueFlowMaxIfCount = std::atoi(argv[i] + 37);
|
||||
|
||||
// Specify platform
|
||||
else if (std::strncmp(argv[i], "--platform=", 11) == 0) {
|
||||
const std::string platform(11+argv[i]);
|
||||
|
@ -1212,6 +1215,13 @@ void CmdLineParser::printHelp()
|
|||
" is 2. A larger value will mean more errors can be found\n"
|
||||
" but also means the analysis will be slower.\n"
|
||||
" --output-file=<file> Write results to file, rather than standard error.\n"
|
||||
" --performance-valueflow-max-if-count=<limit>\n"
|
||||
" If you have many conditional scopes in a function then\n"
|
||||
" the number of possible control flow paths through that\n"
|
||||
" function explodes and that can lead to very long analysis\n"
|
||||
" time. Valueflow is limited in functions that have more\n"
|
||||
" than <limit> conditional scopes. The default limit is\n"
|
||||
" 100, which few functions will reach.\n"
|
||||
" --platform=<type>, --platform=<file>\n"
|
||||
" Specifies platform specific types and sizes. The\n"
|
||||
" available builtin platforms are:\n"
|
||||
|
|
|
@ -254,6 +254,9 @@ public:
|
|||
/** @brief Experimental: --performance-valueflow-max-time=T */
|
||||
int performanceValueFlowMaxTime;
|
||||
|
||||
/** @brief --performance-valueflow-max-if-count=C */
|
||||
int performanceValueFlowMaxIfCount = 100;
|
||||
|
||||
/** @brief plist output (--plist-output=<dir>) */
|
||||
std::string plistOutput;
|
||||
|
||||
|
|
|
@ -5747,9 +5747,15 @@ static bool intersects(const C1& c1, const C2& c2)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void valueFlowAfterAssign(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
|
||||
static void valueFlowAfterAssign(TokenList *tokenlist,
|
||||
SymbolDatabase* symboldatabase,
|
||||
ErrorLogger *errorLogger,
|
||||
const Settings *settings,
|
||||
const std::set<const Scope*>& skippedFunctions)
|
||||
{
|
||||
for (const Scope * scope : symboldatabase->functionScopes) {
|
||||
if (skippedFunctions.count(scope))
|
||||
continue;
|
||||
std::unordered_map<nonneg int, std::unordered_set<nonneg int>> backAssigns;
|
||||
for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
|
||||
// Assignment
|
||||
|
@ -6051,9 +6057,12 @@ struct ConditionHandler {
|
|||
void traverseCondition(TokenList* tokenlist,
|
||||
SymbolDatabase* symboldatabase,
|
||||
const Settings* settings,
|
||||
const std::set<const Scope*>& skippedFunctions,
|
||||
const std::function<void(const Condition& cond, Token* tok, const Scope* scope)>& f) const
|
||||
{
|
||||
for (const Scope *scope : symboldatabase->functionScopes) {
|
||||
if (skippedFunctions.count(scope))
|
||||
continue;
|
||||
for (Token *tok = const_cast<Token *>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
|
||||
if (Token::Match(tok, "if|while|for ("))
|
||||
continue;
|
||||
|
@ -6086,8 +6095,9 @@ struct ConditionHandler {
|
|||
void beforeCondition(TokenList* tokenlist,
|
||||
SymbolDatabase* symboldatabase,
|
||||
ErrorLogger* errorLogger,
|
||||
const Settings* settings) const {
|
||||
traverseCondition(tokenlist, symboldatabase, settings, [&](const Condition& cond, Token* tok, const Scope*) {
|
||||
const Settings* settings,
|
||||
const std::set<const Scope*>& skippedFunctions) const {
|
||||
traverseCondition(tokenlist, symboldatabase, settings, skippedFunctions, [&](const Condition& cond, Token* tok, const Scope*) {
|
||||
if (cond.vartok->exprId() == 0)
|
||||
return;
|
||||
|
||||
|
@ -6233,8 +6243,9 @@ struct ConditionHandler {
|
|||
void afterCondition(TokenList* tokenlist,
|
||||
SymbolDatabase* symboldatabase,
|
||||
ErrorLogger* errorLogger,
|
||||
const Settings* settings) const {
|
||||
traverseCondition(tokenlist, symboldatabase, settings, [&](const Condition& cond, Token* condTok, const Scope* scope) {
|
||||
const Settings* settings,
|
||||
const std::set<const Scope*>& skippedFunctions) const {
|
||||
traverseCondition(tokenlist, symboldatabase, settings, skippedFunctions, [&](const Condition& cond, Token* condTok, const Scope* scope) {
|
||||
const Token* top = condTok->astTop();
|
||||
|
||||
const MathLib::bigint path = cond.getPath();
|
||||
|
@ -6564,10 +6575,11 @@ static void valueFlowCondition(const ValuePtr<ConditionHandler>& handler,
|
|||
TokenList* tokenlist,
|
||||
SymbolDatabase* symboldatabase,
|
||||
ErrorLogger* errorLogger,
|
||||
const Settings* settings)
|
||||
const Settings* settings,
|
||||
const std::set<const Scope*>& skippedFunctions)
|
||||
{
|
||||
handler->beforeCondition(tokenlist, symboldatabase, errorLogger, settings);
|
||||
handler->afterCondition(tokenlist, symboldatabase, errorLogger, settings);
|
||||
handler->beforeCondition(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
|
||||
handler->afterCondition(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
|
||||
}
|
||||
|
||||
struct SimpleConditionHandler : ConditionHandler {
|
||||
|
@ -8966,6 +8978,36 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
|||
|
||||
const std::uint64_t stopTime = getValueFlowStopTime(settings);
|
||||
|
||||
std::set<const Scope*> skippedFunctions;
|
||||
if (settings->performanceValueFlowMaxIfCount < 1000) {
|
||||
for (const Scope* functionScope: symboldatabase->functionScopes) {
|
||||
int countIfScopes = 0;
|
||||
std::vector<const Scope*> scopes{functionScope};
|
||||
while (!scopes.empty()) {
|
||||
const Scope* s = scopes.back();
|
||||
scopes.pop_back();
|
||||
for (const Scope* s2: s->nestedList) {
|
||||
scopes.emplace_back(s2);
|
||||
if (s2->type == Scope::ScopeType::eIf)
|
||||
++countIfScopes;
|
||||
}
|
||||
}
|
||||
if (countIfScopes > settings->performanceValueFlowMaxIfCount) {
|
||||
skippedFunctions.emplace(functionScope);
|
||||
|
||||
const std::string& functionName = functionScope->className;
|
||||
const std::list<ErrorMessage::FileLocation> callstack(1, ErrorMessage::FileLocation(functionScope->bodyStart, tokenlist));
|
||||
const ErrorMessage errmsg(callstack, tokenlist->getSourceFilePath(), Severity::information,
|
||||
"ValueFlow analysis is limited in " + functionName + " because if-count in function " +
|
||||
std::to_string(countIfScopes) + " exceeds limit " +
|
||||
std::to_string(settings->performanceValueFlowMaxIfCount) + ". The limit can be adjusted with "
|
||||
"--performance-valueflow-max-if-count. Increasing the if-count limit will likely increase the "
|
||||
"analysis time.", "performanceValueflowMaxIfCountExceeded", Certainty::normal);
|
||||
errorLogger->reportErr(errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t values = 0;
|
||||
std::size_t n = settings->valueFlowMaxIterations;
|
||||
while (n > 0 && values != getTotalValues(tokenlist)) {
|
||||
|
@ -8976,7 +9018,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
|||
if (std::time(nullptr) < stopTime)
|
||||
valueFlowSymbolicOperators(symboldatabase, settings);
|
||||
if (std::time(nullptr) < stopTime)
|
||||
valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings);
|
||||
valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
|
||||
if (std::time(nullptr) < stopTime)
|
||||
valueFlowSymbolicInfer(symboldatabase, settings);
|
||||
if (std::time(nullptr) < stopTime)
|
||||
|
@ -8986,11 +9028,11 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
|||
if (std::time(nullptr) < stopTime)
|
||||
valueFlowRightShift(tokenlist, settings);
|
||||
if (std::time(nullptr) < stopTime)
|
||||
valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings);
|
||||
valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
|
||||
if (std::time(nullptr) < stopTime)
|
||||
valueFlowAfterSwap(tokenlist, symboldatabase, errorLogger, settings);
|
||||
if (std::time(nullptr) < stopTime)
|
||||
valueFlowCondition(SimpleConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings);
|
||||
valueFlowCondition(SimpleConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
|
||||
if (std::time(nullptr) < stopTime)
|
||||
valueFlowInferCondition(tokenlist, settings);
|
||||
if (std::time(nullptr) < stopTime)
|
||||
|
@ -9016,13 +9058,13 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
|||
if (std::time(nullptr) < stopTime)
|
||||
valueFlowIterators(tokenlist, settings);
|
||||
if (std::time(nullptr) < stopTime)
|
||||
valueFlowCondition(IteratorConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings);
|
||||
valueFlowCondition(IteratorConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
|
||||
if (std::time(nullptr) < stopTime)
|
||||
valueFlowIteratorInfer(tokenlist, settings);
|
||||
if (std::time(nullptr) < stopTime)
|
||||
if (std::time(nullptr) < stopTime && skippedFunctions.empty())
|
||||
valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings);
|
||||
if (std::time(nullptr) < stopTime)
|
||||
valueFlowCondition(ContainerConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings);
|
||||
valueFlowCondition(ContainerConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions);
|
||||
}
|
||||
if (std::time(nullptr) < stopTime)
|
||||
valueFlowSafeFunctions(tokenlist, symboldatabase, settings);
|
||||
|
|
Loading…
Reference in New Issue