@@ -2300,7 +2300,81 @@ static bool isConstant(const Token* tok) {
23002300 return tok && (tok->isEnumerator () || Token::Match (tok, " %bool%|%num%|%str%|%char%|nullptr|NULL" ));
23012301}
23022302
2303- static bool isConstStatement (const Token *tok, const Library& library, bool platformIndependent, bool isNestedBracket = false )
2303+ static bool isBuiltinCommaOperand (const Token* tok, const Settings& settings)
2304+ {
2305+ if (!tok)
2306+ return false ;
2307+ const auto builtinType = [](const ValueType* vt) {
2308+ return vt && (vt->pointer > 0 || vt->type == ValueType::VOID || (vt->isPrimitive () && !vt->isEnum ()));
2309+ };
2310+ // A comma chain can itself return an object through an overloaded operator.
2311+ if (tok->str () == " ," )
2312+ return isBuiltinCommaOperand (tok->astOperand1 (), settings) && isBuiltinCommaOperand (tok->astOperand2 (), settings);
2313+ if (!tok->isAssignmentOp ())
2314+ return builtinType (tok->valueType ());
2315+ const ValueType* lhs = tok->astOperand1 () ? tok->astOperand1 ()->valueType () : nullptr ;
2316+ const ValueType* rhs = tok->astOperand2 () ? tok->astOperand2 ()->valueType () : nullptr ;
2317+ if (builtinType (lhs) && (tok->str () == " =" || builtinType (rhs)))
2318+ return true ;
2319+
2320+ // Assignment value types are copied from the lhs, even when an overload
2321+ // returns another type. Do not select an arbitrary overload: only rely on
2322+ // the return type when all visible candidates have a built-in result.
2323+ for (const ValueType* vt : {lhs, rhs}) {
2324+ if (!builtinType (vt) && (!vt || !vt->typeScope ))
2325+ return false ;
2326+ if (vt && vt->typeScope ) {
2327+ // Template aliases/qualifications and inherited overloads cannot
2328+ // be resolved reliably by comparing the available parameter types.
2329+ if (vt->typeScope ->className .find (' <' ) != std::string::npos ||
2330+ (vt->typeScope ->definedType && !vt->typeScope ->definedType ->derivedFrom .empty ()))
2331+ return false ;
2332+ }
2333+ }
2334+ if (!tok->scope ())
2335+ return false ;
2336+ bool found = false ;
2337+ const std::string name = " operator" + tok->str ();
2338+ // Include non-member candidates conservatively, including hidden friends
2339+ // and anonymous namespaces, without trying to rank overloads.
2340+ for (const Scope& scope : tok->scope ()->symdb .scopeList ) {
2341+ const auto range = scope.functionMap .equal_range (name);
2342+ for (auto it = range.first ; it != range.second ; ++it) {
2343+ const Function* function = it->second ;
2344+ const bool member = scope.isClassOrStruct () && !function->isFriend ();
2345+ if (member && (!lhs || lhs->typeScope != &scope))
2346+ continue ;
2347+ if (function->argCount () != (member ? 1U : 2U ))
2348+ continue ;
2349+ bool matches = true ;
2350+ for (unsigned int arg = 0 ; arg < function->argCount (); ++arg) {
2351+ const ValueType* operand = member || arg == 1U ? rhs : lhs;
2352+ const ValueType* parameter = function->getArgumentVar (arg)->valueType ();
2353+ const auto match = ValueType::matchParameter (operand, parameter);
2354+ if (match == ValueType::MatchResult::UNKNOWN )
2355+ return false ;
2356+ if (match == ValueType::MatchResult::NOMATCH ) {
2357+ // A user-defined conversion may still make this candidate
2358+ // viable; matchParameter does not resolve those conversions.
2359+ if (!builtinType (operand) || !builtinType (parameter))
2360+ return false ;
2361+ matches = false ;
2362+ }
2363+ }
2364+ if (!matches)
2365+ continue ;
2366+ if (!function->retDef )
2367+ return false ;
2368+ const ValueType result = ValueType::parseDecl (function->retDef , settings);
2369+ if (!builtinType (&result))
2370+ return false ;
2371+ found = true ;
2372+ }
2373+ }
2374+ return found;
2375+ }
2376+
2377+ static bool isConstStatement (const Token *tok, const Settings& settings, bool platformIndependent, bool isNestedBracket = false )
23042378{
23052379 if (!tok)
23062380 return false ;
@@ -2322,7 +2396,7 @@ static bool isConstStatement(const Token *tok, const Library& library, bool plat
23222396 tok2 = tok2->astParent ();
23232397 }
23242398 if (Token::Match (tok, " &&|%oror%" ))
2325- return isConstStatement (tok->astOperand1 (), library , platformIndependent) && isConstStatement (tok->astOperand2 (), library , platformIndependent);
2399+ return isConstStatement (tok->astOperand1 (), settings , platformIndependent) && isConstStatement (tok->astOperand2 (), settings , platformIndependent);
23262400 if (Token::Match (tok, " !|~|%cop%" ) && (tok->astOperand1 () || tok->astOperand2 ()))
23272401 return true ;
23282402 if (Token::simpleMatch (tok->previous (), " sizeof (" ))
@@ -2332,38 +2406,40 @@ static bool isConstStatement(const Token *tok, const Library& library, bool plat
23322406 if (isCPPCast (tok)) {
23332407 if (Token::simpleMatch (tok->astOperand1 (), " dynamic_cast" ) && Token::simpleMatch (tok->astOperand1 ()->linkAt (1 )->previous (), " & >" ))
23342408 return false ;
2335- return isWithoutSideEffects (tok) && isConstStatement (tok->astOperand2 (), library , platformIndependent);
2409+ return isWithoutSideEffects (tok) && isConstStatement (tok->astOperand2 (), settings , platformIndependent);
23362410 }
23372411 if (tok->isCast () && tok->next () && tok->next ()->isStandardType ())
2338- return isWithoutSideEffects (tok->astOperand1 ()) && isConstStatement (tok->astOperand1 (), library , platformIndependent);
2412+ return isWithoutSideEffects (tok->astOperand1 ()) && isConstStatement (tok->astOperand1 (), settings , platformIndependent);
23392413 if (Token::simpleMatch (tok, " ." ))
2340- return isConstStatement (tok->astOperand2 (), library , platformIndependent);
2414+ return isConstStatement (tok->astOperand2 (), settings , platformIndependent);
23412415 if (Token::simpleMatch (tok, " ," )) {
2416+ if (tok->isCpp () && (!isBuiltinCommaOperand (tok->astOperand1 (), settings) || !isBuiltinCommaOperand (tok->astOperand2 (), settings)))
2417+ return false ;
23422418 if (tok->astParent ()) // warn about const statement on rhs at the top level
2343- return isConstStatement (tok->astOperand1 (), library , platformIndependent) &&
2344- isConstStatement (tok->astOperand2 (), library , platformIndependent);
2419+ return isConstStatement (tok->astOperand1 (), settings , platformIndependent) &&
2420+ isConstStatement (tok->astOperand2 (), settings , platformIndependent);
23452421
23462422 const Token* lml = previousBeforeAstLeftmostLeaf (tok); // don't warn about matrix/vector assignment (e.g. Eigen)
23472423 if (lml)
23482424 lml = lml->next ();
23492425 const Token* stream = lml;
23502426 while (stream && Token::Match (stream->astParent (), " .|[|(|*" ))
23512427 stream = stream->astParent ();
2352- return (!stream || !isLikelyStream (stream)) && isConstStatement (tok->astOperand2 (), library , platformIndependent);
2428+ return (!stream || !isLikelyStream (stream)) && isConstStatement (tok->astOperand2 (), settings , platformIndependent);
23532429 }
23542430 if (Token::simpleMatch (tok, " ?" ) && Token::simpleMatch (tok->astOperand2 (), " :" )) // ternary operator
2355- return isConstStatement (tok->astOperand1 (), library , platformIndependent) &&
2356- isConstStatement (tok->astOperand2 ()->astOperand1 (), library , platformIndependent) &&
2357- isConstStatement (tok->astOperand2 ()->astOperand2 (), library , platformIndependent);
2431+ return isConstStatement (tok->astOperand1 (), settings , platformIndependent) &&
2432+ isConstStatement (tok->astOperand2 ()->astOperand1 (), settings , platformIndependent) &&
2433+ isConstStatement (tok->astOperand2 ()->astOperand2 (), settings , platformIndependent);
23582434 if (isBracketAccess (tok) && isWithoutSideEffects (tok->astOperand1 (), /* checkArrayAccess*/ true , /* checkReference*/ false )) {
23592435 const bool isChained = succeeds (tok->astParent (), tok);
23602436 if (Token::simpleMatch (tok->astParent (), " [" )) {
23612437 if (isChained)
2362- return isConstStatement (tok->astOperand2 (), library , platformIndependent) &&
2363- isConstStatement (tok->astParent (), library , platformIndependent);
2364- return isNestedBracket && isConstStatement (tok->astOperand2 (), library , platformIndependent);
2438+ return isConstStatement (tok->astOperand2 (), settings , platformIndependent) &&
2439+ isConstStatement (tok->astParent (), settings , platformIndependent);
2440+ return isNestedBracket && isConstStatement (tok->astOperand2 (), settings , platformIndependent);
23652441 }
2366- return isConstStatement (tok->astOperand2 (), library , platformIndependent, /* isNestedBracket*/ !isChained);
2442+ return isConstStatement (tok->astOperand2 (), settings , platformIndependent, /* isNestedBracket*/ !isChained);
23672443 }
23682444 if (!tok->astParent () && findLambdaEndToken (tok))
23692445 return true ;
@@ -2379,7 +2455,7 @@ static bool isConstStatement(const Token *tok, const Library& library, bool plat
23792455 funcStr.insert (0 , tok2->strAt (-2 ) + " ::" );
23802456 tok2 = tok2->tokAt (-2 );
23812457 }
2382- if (library.functions ().count (funcStr) > 0 )
2458+ if (settings. library .functions ().count (funcStr) > 0 )
23832459 return true ;
23842460 }
23852461 return false ;
@@ -2468,7 +2544,7 @@ void CheckOtherImpl::checkIncompleteStatement()
24682544 // Skip statement expressions
24692545 if (Token::simpleMatch (rtok, " ; } )" ) || Token::simpleMatch (tok->next (), " ; } )" ))
24702546 continue ;
2471- if (!isConstStatement (tok, mSettings . library , false ))
2547+ if (!isConstStatement (tok, mSettings , false ))
24722548 continue ;
24732549 if (isVoidStmt (tok))
24742550 continue ;
@@ -2657,7 +2733,7 @@ void CheckOtherImpl::checkMisusedScopedObject()
26572733 if (Token::simpleMatch (parTok, " <" ) && parTok->link ())
26582734 parTok = parTok->link ()->next ();
26592735 if (const Token* arg = parTok->astOperand2 ()) {
2660- if (!isConstStatement (arg, mSettings . library , false ))
2736+ if (!isConstStatement (arg, mSettings , false ))
26612737 continue ;
26622738 if (parTok->str () == " (" ) {
26632739 if (arg->varId () && !(arg->variable () && arg->variable ()->nameToken () != arg))
@@ -3138,7 +3214,7 @@ void CheckOtherImpl::checkDuplicateExpression()
31383214
31393215 else if (!tok->astOperand1 ()->values ().empty () && !tok->astOperand2 ()->values ().empty () && isEqualKnownValue (tok->astOperand1 (), tok->astOperand2 ()) &&
31403216 !isVariableChanged (tok->astParent (), /* indirect*/ 0 , mSettings ) &&
3141- isConstStatement (tok->astOperand1 (), mSettings . library , true ) && isConstStatement (tok->astOperand2 (), mSettings . library , true ))
3217+ isConstStatement (tok->astOperand1 (), mSettings , true ) && isConstStatement (tok->astOperand2 (), mSettings , true ))
31423218 duplicateValueTernaryError (tok);
31433219 }
31443220 }
0 commit comments