@@ -4699,11 +4699,13 @@ void TemplateSimplifier::simplifyTemplates(const std::time_t maxtime)
46994699 Token * tok = it->token ();
47004700 tok->deleteNext (2 );
47014701 tok->deleteThis ();
4702- } else if (shouldDeferRemoval (*it)) {
4702+ } else if (shouldDeferRemoval (*it) || hasRemainingCalls (*it) ) {
47034703 // don't remove the declaration yet: pending type deductions may
47044704 // instantiate this function template again, and during the type
47054705 // deduction rounds the symbol database has references to these
4706- // tokens. It is removed later by removeDeferredTemplateDeclarations().
4706+ // tokens. It is removed later by removeDeferredTemplateDeclarations()
4707+ // - unless the template is still called without template arguments:
4708+ // those calls refer to the generic definition.
47074709 auto it1 = mTemplateForwardDeclarationsMap .find (it->token ());
47084710 if (it1 != mTemplateForwardDeclarationsMap .end ())
47094711 deferRemoval (it1->second );
@@ -4871,6 +4873,75 @@ void TemplateSimplifier::rememberDirtyCallSite(Token* nameTok)
48714873 mDirtyCallSites .push_back (nameTok);
48724874}
48734875
4876+ // The tokens of the given template declarations, from "template" to the end of each
4877+ // declaration
4878+ static std::unordered_set<const Token*> declarationTokens (const std::vector<const Token*>& declarations)
4879+ {
4880+ std::unordered_set<const Token*> tokens;
4881+ for (const Token* declTok : declarations) {
4882+ const Token* end = TemplateSimplifier::findTemplateDeclarationEnd (declTok);
4883+ if (!end)
4884+ continue ;
4885+ for (const Token* tok = declTok; tok; tok = tok->next ()) {
4886+ tokens.insert (tok);
4887+ if (tok == end)
4888+ break ;
4889+ }
4890+ }
4891+ return tokens;
4892+ }
4893+
4894+ // Is the token the name of a plain call "f ( x )" - not a declaration "int f ( int x )"?
4895+ static bool isPlainCall (const Token* tok)
4896+ {
4897+ if (!tok->isName () || tok->isKeyword () || tok->strAt (1 ) != " (" )
4898+ return false ;
4899+ // skip the qualification: "N :: f ( x )"
4900+ const Token* prev = tok->previous ();
4901+ while (Token::simpleMatch (prev, " ::" ) && prev->previous () && prev->previous ()->isName ())
4902+ prev = prev->tokAt (-2 );
4903+ if (!prev)
4904+ return false ;
4905+ // "return f ( x )" is a call, "int f ( int x )" / "T * f ( T t )" is a declaration
4906+ if (Token::Match (prev, " return|case|throw|else|do|co_return|co_yield|co_await|sizeof" ))
4907+ return true ;
4908+ return !Token::Match (prev, " %name%|*|&|&&|>" );
4909+ }
4910+
4911+ // Are there plain calls "name ( x )" of the function template left in the token list,
4912+ // outside the tokens of its declarations? Such a call - in an uninstantiated template,
4913+ // or with arguments whose types are unknown - could not be instantiated and refers to
4914+ // the generic definition, so the definition must not be removed.
4915+ static bool hasRemainingCalls (const TokenList& tokenList,
4916+ const std::string& name,
4917+ const std::unordered_set<const Token*>& declarations)
4918+ {
4919+ for (const Token* tok = tokenList.front (); tok; tok = tok->next ()) {
4920+ if (tok->str () == name && declarations.count (tok) == 0 && isPlainCall (tok))
4921+ return true ;
4922+ }
4923+ return false ;
4924+ }
4925+
4926+ // Is the name token the name of a function template whose calls can be written without
4927+ // template arguments? A member function of a class template ("C < T > :: f") is
4928+ // instantiated with its class, never from a call.
4929+ static bool isDeducibleFunctionTemplateName (const Token* nameTok)
4930+ {
4931+ return nameTok && nameTok->strAt (1 ) == " (" && !Token::simpleMatch (nameTok->tokAt (-2 ), " > ::" );
4932+ }
4933+
4934+ bool TemplateSimplifier::hasRemainingCalls (const TokenAndName& declaration) const
4935+ {
4936+ if (!declaration.isFunction () || !isDeducibleFunctionTemplateName (declaration.nameToken ()))
4937+ return false ;
4938+ std::vector<const Token*> declarations{declaration.token ()};
4939+ const auto it = mTemplateForwardDeclarationsMap .find (declaration.token ());
4940+ if (it != mTemplateForwardDeclarationsMap .cend ())
4941+ declarations.push_back (it->second );
4942+ return ::hasRemainingCalls (mTokenList , declaration.name (), declarationTokens (declarations));
4943+ }
4944+
48744945bool TemplateSimplifier::removeDeferredTemplateDeclarations (SymbolDatabase* symbolDatabase)
48754946{
48764947 if (mDeferredRemovals .empty ())
@@ -4886,18 +4957,27 @@ bool TemplateSimplifier::removeDeferredTemplateDeclarations(SymbolDatabase* symb
48864957 }
48874958 mDeferredRemovals .clear ();
48884959
4889- // all tokens that are going to be removed
4890- std::unordered_set<const Token*> removedTokens;
4960+ // a function template that is still called without template arguments (the
4961+ // arguments could not be deduced) is kept - the calls refer to the generic definition
4962+ const std::unordered_set<const Token*> deferredTokens (
4963+ declarationTokens (std::vector<const Token*>(declarations.cbegin (), declarations.cend ())));
4964+ std::vector<Token*> removedDeclarations;
48914965 for (Token* declTok : declarations) {
4892- const Token* end = findTemplateDeclarationEnd (declTok);
4893- if (!end)
4894- continue ;
4895- for (const Token* tok = declTok; tok; tok = tok->next ()) {
4896- removedTokens.insert (tok);
4897- if (tok == end)
4898- break ;
4966+ const Token* nameTok = nullptr ;
4967+ if (const Token* paramEnd = declTok->next ()->findClosingBracket ()) {
4968+ const int namepos = getTemplateNamePosition (paramEnd);
4969+ if (namepos > 0 )
4970+ nameTok = paramEnd->tokAt (namepos);
48994971 }
4972+ if (isDeducibleFunctionTemplateName (nameTok) && ::hasRemainingCalls (mTokenList , nameTok->str (), deferredTokens))
4973+ continue ;
4974+ removedDeclarations.push_back (declTok);
49004975 }
4976+ declarations = std::move (removedDeclarations);
4977+
4978+ // all tokens that are going to be removed
4979+ const std::unordered_set<const Token*> removedTokens (
4980+ declarationTokens (std::vector<const Token*>(declarations.cbegin (), declarations.cend ())));
49014981
49024982 // dirty call sites inside the removed declarations are gone
49034983 mDirtyCallSites .erase (std::remove_if (mDirtyCallSites .begin (),
0 commit comments