Fix handling of namespace scope with several bodystarts (#3438)

Follow up to 0093452bed.
Give the proper end to getVariableList, since it might not be bodyEnd.

Before that, getVariableList would add the same variables in several
unrelated scopes, and all kind of false positive would follow.

For instance, with the case I added in the unit-tests, I had:
```
../code.cpp:15:18: warning: The struct 'is_A' defines member variable with name 'foo' also defined in its parent struct 'is_A_impl'. [duplInheritedMember]
static const int foo = 8;
                 ^
../code.cpp:15:18: note: Parent variable 'is_A_impl::foo'
static const int foo = 8;
                 ^
../code.cpp:15:18: note: Derived variable 'is_A::foo'
static const int foo = 8;
                 ^
../code.cpp:15:18: style: struct member 'has_A::foo' is never used. [unusedStructMember]
static const int foo = 8;
                 ^
../code.cpp:15:18: style: struct member 'is_A::foo' is never used. [unusedStructMember]
static const int foo = 8;
                 ^
```
This commit is contained in:
Ken-Patrick Lehrmann 2021-09-04 11:09:33 +02:00 committed by GitHub
parent 2b7523e466
commit b3b3b6b2a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 5 deletions

View File

@ -4194,19 +4194,19 @@ void Scope::getVariableList(const Settings* settings)
{ {
if (!bodyStartList.empty()) { if (!bodyStartList.empty()) {
for (const Token *bs: bodyStartList) for (const Token *bs: bodyStartList)
getVariableList(settings, bs->next()); getVariableList(settings, bs->next(), bs->link());
} }
// global scope // global scope
else if (type == Scope::eGlobal) else if (type == Scope::eGlobal)
getVariableList(settings, check->mTokenizer->tokens()); getVariableList(settings, check->mTokenizer->tokens(), nullptr);
// forward declaration // forward declaration
else else
return; return;
} }
void Scope::getVariableList(const Settings* settings, const Token* start) void Scope::getVariableList(const Settings* settings, const Token* start, const Token* end)
{ {
// Variable declared in condition: if (auto x = bar()) // Variable declared in condition: if (auto x = bar())
if (Token::Match(classDef, "if|while ( %type%") && Token::simpleMatch(classDef->next()->astOperand2(), "=")) { if (Token::Match(classDef, "if|while ( %type%") && Token::simpleMatch(classDef->next()->astOperand2(), "=")) {
@ -4214,7 +4214,7 @@ void Scope::getVariableList(const Settings* settings, const Token* start)
} }
AccessControl varaccess = defaultAccess(); AccessControl varaccess = defaultAccess();
for (const Token *tok = start; tok && tok != bodyEnd; tok = tok->next()) { for (const Token *tok = start; tok && tok != end; tok = tok->next()) {
// syntax error? // syntax error?
if (tok->next() == nullptr) if (tok->next() == nullptr)
break; break;

View File

@ -1204,7 +1204,7 @@ private:
void findFunctionInBase(const std::string & name, nonneg int args, std::vector<const Function *> & matches) const; void findFunctionInBase(const std::string & name, nonneg int args, std::vector<const Function *> & matches) const;
/** @brief initialize varlist */ /** @brief initialize varlist */
void getVariableList(const Settings* settings, const Token *start); void getVariableList(const Settings* settings, const Token *start, const Token *end);
}; };
enum class Reference { enum class Reference {

View File

@ -355,6 +355,7 @@ private:
TEST_CASE(createSymbolDatabaseFindAllScopes1); TEST_CASE(createSymbolDatabaseFindAllScopes1);
TEST_CASE(createSymbolDatabaseFindAllScopes2); TEST_CASE(createSymbolDatabaseFindAllScopes2);
TEST_CASE(createSymbolDatabaseFindAllScopes3);
TEST_CASE(enum1); TEST_CASE(enum1);
TEST_CASE(enum2); TEST_CASE(enum2);
@ -4811,6 +4812,33 @@ private:
ASSERT(var2->variable()); ASSERT(var2->variable());
} }
void createSymbolDatabaseFindAllScopes3() {
GET_SYMBOL_DB("namespace ns {\n"
"\n"
"namespace ns_details {\n"
"template <typename T, typename = void> struct has_A : std::false_type {};\n"
"template <typename T> struct has_A<T, typename make_void<typename T::A>::type> : std::true_type {};\n"
"template <typename T, bool> struct is_A_impl : public std::is_trivially_copyable<T> {};\n"
"template <typename T> struct is_A_impl<T, true> : public std::is_same<typename T::A, std::true_type> {};\n"
"}\n"
"\n"
"template <typename T> struct is_A : ns_details::is_A_impl<T, ns_details::has_A<T>::value> {};\n"
"template <class T, class U> struct is_A<std::pair<T, U>> : std::integral_constant<bool, is_A<T>::value && is_A<U>::value> {};\n"
"}\n"
"\n"
"extern \"C\" {\n"
"static const int foo = 8;\n"
"}\n");
ASSERT(db);
ASSERT_EQUALS(6, db->scopeList.size());
ASSERT_EQUALS(1, db->scopeList.front().varlist.size());
auto list = db->scopeList;
list.pop_front();
for (const auto &scope : list) {
ASSERT_EQUALS(0, scope.varlist.size());
}
}
void enum1() { void enum1() {
GET_SYMBOL_DB("enum BOOL { FALSE, TRUE }; enum BOOL b;"); GET_SYMBOL_DB("enum BOOL { FALSE, TRUE }; enum BOOL b;");