@@ -1252,7 +1252,10 @@ namespace {
12521252 // Scope aware symbol table used for function template argument
12531253 // deduction. It is filled during a single forward pass over the token
12541254 // list; at any point during that pass it contains exactly the
1255- // declarations that are visible at the current position.
1255+ // declarations that are visible at the current position. The member
1256+ // declarations of a class are kept when its scope ends so that they stay
1257+ // visible in out of class member function bodies and in classes that
1258+ // derive from it.
12561259 class DeductionSymbolTable {
12571260 public:
12581261 DeductionSymbolTable () : mScopes (1 ) {} // global scope
@@ -1263,6 +1266,19 @@ namespace {
12631266 mScopes .back ().endTok = endTok;
12641267 }
12651268
1269+ // enter the body of the class |className| (a full name)
1270+ void enterClassScope (const Token *endTok, std::string className) {
1271+ enterScope (endTok);
1272+ mScopes .back ().memberClass = std::move (className);
1273+ mScopes .back ().isClassBody = true ;
1274+ }
1275+
1276+ // record that the class |className| (a full name) has the given
1277+ // base classes (names as written in the code)
1278+ void addBaseClasses (const std::string &className, std::vector<std::string> baseClasses) {
1279+ mBaseClasses [className] = std::move (baseClasses);
1280+ }
1281+
12661282 // Called when the forward pass reaches |tok| (a ')' or '}'): leave
12671283 // the scopes that end here. A parenthesized group that is directly
12681284 // attached to a body - a function parameter list or a for/if/while
@@ -1273,28 +1289,48 @@ namespace {
12731289 const Token *bodyStart = findAttachedBody (tok);
12741290 if (bodyStart && bodyStart->link ()) {
12751291 mScopes .back ().endTok = bodyStart->link ();
1292+ // in an out of class member function body the class
1293+ // members are visible
1294+ if (mScopes .back ().memberClass .empty ())
1295+ mScopes .back ().memberClass = memberFunctionClass (tok);
12761296 continue ; // re-check: the end token changed
12771297 }
12781298 }
1299+ // keep the members of a class when its scope ends
1300+ if (mScopes .back ().isClassBody )
1301+ mClassMembers [mScopes .back ().memberClass ] = std::move (mScopes .back ().members );
12791302 mScopes .pop_back ();
12801303 }
12811304 }
12821305
12831306 void addVariable (const std::string &name, const ParsedType &type) {
1284- mScopes .back ().variables [name] = type;
1307+ mScopes .back ().members . variables [name] = type;
12851308 }
12861309
12871310 void addFunction (const std::string &name, const ParsedType &returnType) {
1288- mScopes .back ().functions [name].push_back (returnType);
1311+ mScopes .back ().members . functions [name].push_back (returnType);
12891312 }
12901313
12911314 bool lookupVariable (const std::string &name, ParsedType &result) const {
12921315 for (auto scope = mScopes .crbegin (); scope != mScopes .crend (); ++scope) {
1293- const auto it = scope->variables .find (name);
1294- if (it != scope->variables .cend ()) {
1316+ const auto it = scope->members . variables .find (name);
1317+ if (it != scope->members . variables .cend ()) {
12951318 result = it->second ;
12961319 return true ;
12971320 }
1321+ if (scope->memberClass .empty ())
1322+ continue ;
1323+ // class members (and inherited members) are visible here
1324+ for (const std::string &className : withBaseClasses (scope->memberClass )) {
1325+ const auto classIt = mClassMembers .find (className);
1326+ if (classIt == mClassMembers .cend ())
1327+ continue ;
1328+ const auto varIt = classIt->second .variables .find (name);
1329+ if (varIt != classIt->second .variables .cend ()) {
1330+ result = varIt->second ;
1331+ return true ;
1332+ }
1333+ }
12981334 }
12991335 return false ;
13001336 }
@@ -1303,20 +1339,79 @@ namespace {
13031339 bool lookupFunctionReturnType (const std::string &name, ParsedType &result) const {
13041340 bool found = false ;
13051341 for (auto scope = mScopes .crbegin (); scope != mScopes .crend (); ++scope) {
1306- const auto it = scope->functions .find (name);
1307- if (it == scope->functions .cend ())
1342+ if (!collectFunctionReturnType (scope->members , name, result, found))
1343+ return false ;
1344+ if (scope->memberClass .empty ())
13081345 continue ;
1309- for (const ParsedType &declaration : it-> second ) {
1310- if (found && ! sameDeducedType (result. type , declaration. type ))
1311- return false ; // overloads with different return types
1312- result = declaration;
1313- found = true ;
1346+ for (const std::string &className : withBaseClasses (scope-> memberClass ) ) {
1347+ const auto classIt = mClassMembers . find (className);
1348+ if (classIt != mClassMembers . cend () &&
1349+ ! collectFunctionReturnType (classIt-> second , name, result, found))
1350+ return false ;
13141351 }
13151352 }
13161353 return found;
13171354 }
13181355
1356+ // The class |scope| followed by its transitive base classes; used to
1357+ // search a class scope the way unqualified lookup does. For a name
1358+ // that is not a known class only the scope itself is returned.
1359+ std::vector<std::string> withBaseClasses (const std::string &scope) const {
1360+ std::vector<std::string> result;
1361+ result.push_back (scope);
1362+ if (scope.empty ())
1363+ return result;
1364+ for (std::size_t i = 0 ; i < result.size () && result.size () < 20 ; ++i) {
1365+ const auto it = mBaseClasses .find (result[i]);
1366+ if (it == mBaseClasses .cend ())
1367+ continue ;
1368+ const std::string::size_type separator = result[i].rfind (" :: " );
1369+ const std::string enclosing = (separator == std::string::npos) ? " " : result[i].substr (0 , separator);
1370+ for (const std::string &base : it->second ) {
1371+ const std::string resolved = resolveClassName (enclosing, base);
1372+ if (std::find (result.cbegin (), result.cend (), resolved) == result.cend ())
1373+ result.push_back (resolved);
1374+ }
1375+ }
1376+ return result;
1377+ }
1378+
13191379 private:
1380+ struct Members {
1381+ std::unordered_map<std::string, ParsedType> variables;
1382+ std::unordered_map<std::string, std::vector<ParsedType>> functions;
1383+ };
1384+
1385+ // add the return types of the declarations of |name| in |members|;
1386+ // returns false when the declarations do not agree on the type
1387+ static bool collectFunctionReturnType (const Members &members, const std::string &name, ParsedType &result, bool &found) {
1388+ const auto it = members.functions .find (name);
1389+ if (it == members.functions .cend ())
1390+ return true ;
1391+ for (const ParsedType &declaration : it->second ) {
1392+ if (found && !sameDeducedType (result.type , declaration.type ))
1393+ return false ; // overloads with different return types
1394+ result = declaration;
1395+ found = true ;
1396+ }
1397+ return true ;
1398+ }
1399+
1400+ // Resolve the class name |name| as it would be looked up from
1401+ // |enclosingScope|: the innermost enclosing scope wins.
1402+ std::string resolveClassName (std::string enclosingScope, const std::string &name) const {
1403+ for (;;) {
1404+ const std::string candidate = enclosingScope.empty () ? name : enclosingScope + " :: " + name;
1405+ if (mClassMembers .find (candidate) != mClassMembers .cend () ||
1406+ mBaseClasses .find (candidate) != mBaseClasses .cend ())
1407+ return candidate;
1408+ if (enclosingScope.empty ())
1409+ return name;
1410+ const std::string::size_type separator = enclosingScope.rfind (" :: " );
1411+ enclosingScope = (separator == std::string::npos) ? " " : enclosingScope.substr (0 , separator);
1412+ }
1413+ }
1414+
13201415 // If the parenthesized group ending at |rpar| is directly attached to
13211416 // a body ("...) {", "...) const {", "...) : init(x) {") return the
13221417 // "{" of that body, otherwise nullptr.
@@ -1341,12 +1436,37 @@ namespace {
13411436 return Token::simpleMatch (tok, " {" ) ? tok : nullptr ;
13421437 }
13431438
1439+ // For the parameter list of an out of class member function
1440+ // definition ("void A::g(..)") return the full name of the class,
1441+ // otherwise an empty string.
1442+ std::string memberFunctionClass (const Token *rpar) const {
1443+ const Token *lpar = rpar->link ();
1444+ if (!lpar)
1445+ return " " ;
1446+ const Token *nameTok = lpar->previous ();
1447+ if (!nameTok || !nameTok->isName ())
1448+ return " " ;
1449+ std::string qualifier;
1450+ const Token *tok = nameTok;
1451+ while (Token::Match (tok->tokAt (-2 ), " %name% ::" )) {
1452+ qualifier = qualifier.empty () ? tok->strAt (-2 ) : tok->strAt (-2 ) + " :: " + qualifier;
1453+ tok = tok->tokAt (-2 );
1454+ }
1455+ if (qualifier.empty ())
1456+ return " " ;
1457+ const std::string enclosingScope = nameTok->scopeInfo () ? nameTok->scopeInfo ()->name : std::string ();
1458+ return resolveClassName (enclosingScope, qualifier);
1459+ }
1460+
13441461 struct Scope {
13451462 const Token *endTok = nullptr ;
1346- std::unordered_map<std::string, ParsedType> variables;
1347- std::unordered_map<std::string, std::vector<ParsedType>> functions;
1463+ std::string memberClass; // class whose members are visible in this scope
1464+ bool isClassBody = false ;
1465+ Members members;
13481466 };
13491467 std::vector<Scope> mScopes ;
1468+ std::unordered_map<std::string, Members> mClassMembers ;
1469+ std::unordered_map<std::string, std::vector<std::string>> mBaseClasses ;
13501470 };
13511471}
13521472
@@ -1867,22 +1987,29 @@ void TemplateSimplifier::deduceFunctionTemplateArguments()
18671987 DeductionSymbolTable symbols;
18681988 const DeductionContext ctx = { mSettings , mTokenList , symbols };
18691989
1990+ // the body of the class definition that is about to start
1991+ const Token *classBodyStart = nullptr ;
1992+ std::string className;
1993+
18701994 for (Token *tok = mTokenList .front (); tok; tok = tok->next ()) {
1871- // Skip template declarations: the declarations inside them are not
1872- // visible outside and calls inside them are only deduced when the
1873- // template is instantiated. User specializations (template <>)
1874- // contain concrete code and are walked.
18751995 if (!tok->isName ()) {
18761996 // scope begin/end (only "(){}[]" have links; "[]" is not a scope)
18771997 if (tok->link ()) {
18781998 if (Token::Match (tok, " )|}" ))
18791999 symbols.leaveScopesEndingAt (tok);
1880- else if (Token::Match (tok, " (|{" ))
2000+ else if (tok == classBodyStart) {
2001+ symbols.enterClassScope (tok->link (), std::move (className));
2002+ classBodyStart = nullptr ;
2003+ } else if (Token::Match (tok, " (|{" ))
18812004 symbols.enterScope (tok->link ());
18822005 }
18832006 continue ;
18842007 }
18852008
2009+ // Skip template declarations: the declarations inside them are not
2010+ // visible outside and calls inside them are only deduced when the
2011+ // template is instantiated. User specializations (template <>)
2012+ // contain concrete code and are walked.
18862013 if (Token::simpleMatch (tok, " template <" )) {
18872014 Token *closing = tok->next ()->findClosingBracket ();
18882015 if (!closing)
@@ -1896,6 +2023,60 @@ void TemplateSimplifier::deduceFunctionTemplateArguments()
18962023 continue ;
18972024 }
18982025
2026+ // class definition: record the base classes and remember the body
2027+ // so that the member declarations can be associated with the class
2028+ if (Token::Match (tok, " class|struct|union %name%" ) && !Token::simpleMatch (tok->previous (), " enum" )) {
2029+ Token *nameTok = tok->next ();
2030+ while (Token::Match (nameTok, " %name% %name%" )) // skip attribute macros
2031+ nameTok = nameTok->next ();
2032+ Token *after = nameTok->next ();
2033+ if (Token::simpleMatch (after, " final" ))
2034+ after = after->next ();
2035+ std::vector<std::string> baseClasses;
2036+ if (Token::simpleMatch (after, " :" )) {
2037+ Token *base = after->next ();
2038+ after = nullptr ;
2039+ while (base) {
2040+ while (Token::Match (base, " public|protected|private|virtual" ))
2041+ base = base->next ();
2042+ if (!base || !base->isName ())
2043+ break ;
2044+ std::string baseName = base->str ();
2045+ while (Token::Match (base->next (), " :: %name%" )) {
2046+ baseName += " :: " + base->strAt (2 );
2047+ base = base->tokAt (2 );
2048+ }
2049+ base = base->next ();
2050+ if (base && base->str () == " <" ) {
2051+ // template base classes are not supported
2052+ base = base->findClosingBracket ();
2053+ if (base)
2054+ base = base->next ();
2055+ baseName.clear ();
2056+ }
2057+ if (!baseName.empty ())
2058+ baseClasses.push_back (std::move (baseName));
2059+ if (Token::simpleMatch (base, " ," ))
2060+ base = base->next ();
2061+ else
2062+ break ;
2063+ }
2064+ if (Token::simpleMatch (base, " {" ))
2065+ after = base;
2066+ else
2067+ baseClasses.clear ();
2068+ }
2069+ if (Token::simpleMatch (after, " {" ) && after->link ()) {
2070+ const std::string &enclosingScope = tok->scopeInfo () ? tok->scopeInfo ()->name : std::string ();
2071+ className = enclosingScope.empty () ? nameTok->str () : (enclosingScope + " :: " + nameTok->str ());
2072+ if (!baseClasses.empty ())
2073+ symbols.addBaseClasses (className, std::move (baseClasses));
2074+ classBodyStart = after;
2075+ tok = after->previous (); // continue at the "{"
2076+ }
2077+ continue ;
2078+ }
2079+
18992080 // function template call with type deduction? (a deducible call is
19002081 // "name (" or "name :: name (" - anything else can be skipped cheaply)
19012082 if (Token::Match (tok->next (), " (|::" ) && isTemplateInstantion (tok) && tok->scopeInfo ()) {
@@ -1914,23 +2095,30 @@ void TemplateSimplifier::deduceFunctionTemplateArguments()
19142095 continue ;
19152096
19162097 // The name is looked up like the compiler does: in the
1917- // enclosing scopes from the innermost outwards, where a match
1918- // in an inner scope shadows declarations in outer scopes.
2098+ // enclosing scopes from the innermost outwards (searching the
2099+ // base classes of a class scope), where a match in an inner
2100+ // scope shadows declarations in outer scopes.
19192101 std::vector<const DeductionCandidate *> candidates;
2102+ std::string matchedScope;
2103+ std::string levelScope;
19202104 std::string scopePath = scopeName;
19212105 for (;;) {
1922- std::string fullName;
1923- if (!scopePath.empty ())
1924- fullName = scopePath + " :: " ;
2106+ levelScope = scopePath;
19252107 if (!qualification.empty ())
1926- fullName += qualification + " :: " ;
1927- fullName += tok->str ();
1928-
1929- for (auto pos = range.first ; pos != range.second ; ++pos) {
1930- // look for declaration with same qualification or constructor with same qualification
1931- if (pos->second .declaration ->fullName () == fullName ||
1932- (pos->second .declaration ->scope () == fullName && tok->str () == pos->second .declaration ->name ()))
1933- candidates.push_back (&pos->second );
2108+ levelScope += (levelScope.empty () ? " " : " :: " ) + qualification;
2109+
2110+ for (const std::string &scope : symbols.withBaseClasses (levelScope)) {
2111+ const std::string fullName = scope.empty () ? tok->str () : (scope + " :: " + tok->str ());
2112+ for (auto pos = range.first ; pos != range.second ; ++pos) {
2113+ // look for declaration with same qualification or constructor with same qualification
2114+ if (pos->second .declaration ->fullName () == fullName ||
2115+ (pos->second .declaration ->scope () == fullName && tok->str () == pos->second .declaration ->name ()))
2116+ candidates.push_back (&pos->second );
2117+ }
2118+ if (!candidates.empty ()) {
2119+ matchedScope = scope;
2120+ break ; // a match shadows the base classes
2121+ }
19342122 }
19352123 if (!candidates.empty () || scopePath.empty ())
19362124 break ;
@@ -1939,8 +2127,23 @@ void TemplateSimplifier::deduceFunctionTemplateArguments()
19392127 scopePath = (separator == std::string::npos) ? " " : scopePath.substr (0 , separator);
19402128 }
19412129
1942- if (!candidates.empty ())
2130+ if (!candidates.empty ()) {
19432131 deduceTemplateArgumentsAtFunctionCall (ctx, tok, candidates);
2132+ if (matchedScope != levelScope && tok->strAt (1 ) == " <" ) {
2133+ // The declaration was found in a base class: qualify
2134+ // the call so that the instantiation is matched to
2135+ // the declaration.
2136+ std::string::size_type start = 0 ;
2137+ for (;;) {
2138+ const std::string::size_type separator = matchedScope.find (" :: " , start);
2139+ tok->insertTokenBefore (matchedScope.substr (start, separator == std::string::npos ? separator : separator - start));
2140+ tok->insertTokenBefore (" ::" );
2141+ if (separator == std::string::npos)
2142+ break ;
2143+ start = separator + 4 ;
2144+ }
2145+ }
2146+ }
19442147 continue ;
19452148 }
19462149 }
0 commit comments