@@ -2291,11 +2291,37 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool&
22912291 auto getFuncTok = [](const Token* tok) -> const Token* {
22922292 if (Token::simpleMatch (tok, " this" ))
22932293 tok = getFuncTokFromThis (tok);
2294- if ((Token::Match (tok, " %name% (|{" ) || Token::simpleMatch (tok->astParent (), " return {" )) && !tok->isStandardType () && !tok->isKeyword ())
2294+ bool isReturn = false ;
2295+ if ((Token::Match (tok, " %name% (|{" ) || (isReturn = Token::simpleMatch (tok->astParent (), " return {" ))) && !tok->isStandardType () && !tok->isKeyword ()) {
2296+ if (isReturn)
2297+ tok = tok->astParent ();
22952298 return tok;
2299+ }
22962300 return nullptr ;
22972301 };
22982302
2303+ auto checkFuncCall = [this , &memberAccessed](const Token* funcTok, const Scope* scope) {
2304+ if (isMemberFunc (scope, funcTok) && (funcTok->strAt (-1 ) != " ." || Token::simpleMatch (funcTok->tokAt (-2 ), " this ." ))) {
2305+ if (!isConstMemberFunc (scope, funcTok))
2306+ return false ;
2307+ memberAccessed = true ;
2308+ }
2309+ // Member variable given as parameter
2310+ const Token *lpar = funcTok->next ();
2311+ if (Token::simpleMatch (lpar, " ( ) (" ))
2312+ lpar = lpar->tokAt (2 );
2313+ for (const Token* tok = lpar->next (); tok && tok != funcTok->next ()->link (); tok = tok->next ()) {
2314+ if (tok->str () == " (" )
2315+ tok = tok->link ();
2316+ else if ((tok->isName () && isMemberVar (scope, tok)) || (tok->isUnaryOp (" &" ) && (tok = tok->astOperand1 ()))) {
2317+ const Variable* var = tok->variable ();
2318+ if (!var || !var->isMutable ())
2319+ return false ; // TODO: Only bailout if function takes argument as non-const reference
2320+ }
2321+ }
2322+ return true ;
2323+ };
2324+
22992325 // if the function doesn't have any assignment nor function call,
23002326 // it can be a const function..
23012327 for (const Token *tok1 = func->functionScope ->bodyStart ; tok1 && tok1 != func->functionScope ->bodyEnd ; tok1 = tok1->next ()) {
@@ -2374,6 +2400,18 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool&
23742400 break ;
23752401 }
23762402
2403+ auto hasOverloadedMemberAccess = [](const Token* end, const Scope* scope) -> bool {
2404+ if (!end || !scope || !Token::simpleMatch (end->astParent (), " ." ))
2405+ return false ;
2406+ auto it = std::find_if (scope->functionList .begin (), scope->functionList .end (), [](const Function& f) {
2407+ return f.isConst () && f.name () == " operator." ;
2408+ });
2409+ if (it == scope->functionList .end () || !it->retType || !it->retType ->classScope )
2410+ return false ;
2411+ const Function* func = it->retType ->classScope ->findFunction (end, /* requireConst*/ true );
2412+ return func && func->isConst ();
2413+ };
2414+
23772415 if (end->strAt (1 ) == " (" ) {
23782416 const Variable *var = lastVarTok->variable ();
23792417 if (!var)
@@ -2389,6 +2427,9 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool&
23892427 ;
23902428 else if (var->smartPointerType () && var->smartPointerType ()->classScope && isConstMemberFunc (var->smartPointerType ()->classScope , end)) {
23912429 ;
2430+ }
2431+ else if (hasOverloadedMemberAccess (end, var->typeScope ())) {
2432+ ;
23922433 } else if (!var->typeScope () || !isConstMemberFunc (var->typeScope (), end))
23932434 return false ;
23942435 }
@@ -2416,8 +2457,8 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool&
24162457 return false ;
24172458
24182459 tok1 = jumpBackToken?jumpBackToken:end; // Jump back to first [ to check inside, or jump to end of expression
2419- if (tok1 == end && Token::Match (end->previous (), " . %name% ( !!)" ))
2420- tok1 = tok1-> previous (); // check function call
2460+ if (tok1 == end && Token::Match (end->previous (), " . %name% ( !!)" ) && ! checkFuncCall (tok1, scope)) // function call on member
2461+ return false ;
24212462 }
24222463
24232464 // streaming: <<
@@ -2435,24 +2476,8 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool&
24352476
24362477 // function/constructor call, return init list
24372478 else if (const Token* funcTok = getFuncTok (tok1)) {
2438- if (isMemberFunc (scope, funcTok) && (funcTok->strAt (-1 ) != " ." || Token::simpleMatch (funcTok->tokAt (-2 ), " this ." ))) {
2439- if (!isConstMemberFunc (scope, funcTok))
2440- return false ;
2441- memberAccessed = true ;
2442- }
2443- // Member variable given as parameter
2444- const Token *lpar = funcTok->next ();
2445- if (Token::simpleMatch (lpar, " ( ) (" ))
2446- lpar = lpar->tokAt (2 );
2447- for (const Token* tok2 = lpar->next (); tok2 && tok2 != funcTok->next ()->link (); tok2 = tok2->next ()) {
2448- if (tok2->str () == " (" )
2449- tok2 = tok2->link ();
2450- else if ((tok2->isName () && isMemberVar (scope, tok2)) || (tok2->isUnaryOp (" &" ) && (tok2 = tok2->astOperand1 ()))) {
2451- const Variable* var = tok2->variable ();
2452- if (!var || !var->isMutable ())
2453- return false ; // TODO: Only bailout if function takes argument as non-const reference
2454- }
2455- }
2479+ if (!checkFuncCall (funcTok, scope))
2480+ return false ;
24562481 } else if (Token::simpleMatch (tok1, " > (" ) && (!tok1->link () || !Token::Match (tok1->link ()->previous (), " static_cast|const_cast|dynamic_cast|reinterpret_cast" ))) {
24572482 return false ;
24582483 }
0 commit comments