summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKim Grasman <kim.grasman@gmail.com>2016-08-11 16:01:38 +0200
committerKim Grasman <kim.grasman@gmail.com>2016-08-15 21:10:02 +0200
commit9c432a97639f61a842bbc3f058e249fde6df39d6 (patch)
treead34e8d02aaa0ce3a3225c6976a81fae718ba259
parentf09dba693412463fcca0898a202763761fe60ccc (diff)
Recognize nested classes in friend declarations
Fragments such as this: friend class Container<T>::Iterator; would not be recognized as needing a forward-declaration, so IWYU would suggest removing forward-decls of Iterator. This patch changes this and expands the test suite for nested classes to cover containing class templates and friend declarations. Fixes issue #331.
-rw-r--r--iwyu.cc8
-rw-r--r--iwyu_ast_util.cc8
-rw-r--r--iwyu_ast_util.h6
-rw-r--r--tests/cxx/fwd_decl_nested_class.cc81
4 files changed, 90 insertions, 13 deletions
diff --git a/iwyu.cc b/iwyu.cc
index 5b3fa1b..8b05b6c 100644
--- a/iwyu.cc
+++ b/iwyu.cc
@@ -3795,12 +3795,12 @@ class IwyuAstConsumer
current_ast_node()->set_in_forward_declare_context(true);
if (compiler()->getLangOpts().CPlusPlus) {
// In C++, if we're already elaborated ('class Foo x') but not
- // namespace-qualified ('class ns::Foo x') there's no need even to
- // forward-declare.
+ // a qualified name ('class ns::Foo x', 'class Class::Nested x') there's
+ // no need even to forward-declare.
// Note that enums are never forward-declarable, so elaborated enums are
- // short-circuited in CanForwardDeclareType.
+ // already short-circuited in CanForwardDeclareType.
const ASTNode* parent = current_ast_node()->parent();
- if (!IsElaborationNode(parent) || IsNamespaceQualifiedNode(parent))
+ if (!IsElaborationNode(parent) || IsQualifiedNameNode(parent))
ReportDeclForwardDeclareUse(CurrentLoc(), type->getDecl());
} else {
// In C, all struct references are elaborated, so we really never need
diff --git a/iwyu_ast_util.cc b/iwyu_ast_util.cc
index 4663beb..f4a19bc 100644
--- a/iwyu_ast_util.cc
+++ b/iwyu_ast_util.cc
@@ -227,17 +227,13 @@ bool IsElaborationNode(const ASTNode* ast_node) {
return elaborated_type && elaborated_type->getKeyword() != clang::ETK_None;
}
-bool IsNamespaceQualifiedNode(const ASTNode* ast_node) {
+bool IsQualifiedNameNode(const ASTNode* ast_node) {
if (ast_node == nullptr)
return false;
const ElaboratedType* elaborated_type = ast_node->GetAs<ElaboratedType>();
if (elaborated_type == nullptr)
return false;
- const NestedNameSpecifier* qualifier = elaborated_type->getQualifier();
- if (qualifier == nullptr)
- return false;
- return (qualifier->getKind() == NestedNameSpecifier::Global ||
- qualifier->getKind() == NestedNameSpecifier::Namespace);
+ return elaborated_type->getQualifier() != nullptr;
}
bool IsNodeInsideCXXMethodBody(const ASTNode* ast_node) {
diff --git a/iwyu_ast_util.h b/iwyu_ast_util.h
index 20d0932..0f487d7 100644
--- a/iwyu_ast_util.h
+++ b/iwyu_ast_util.h
@@ -375,9 +375,9 @@ class CurrentASTNodeUpdater {
// uses ElaboratedType for namespaces ('ns::Foo myvar').
bool IsElaborationNode(const ASTNode* ast_node);
-// See if a given ast_node is a namespace-qualified ElaboratedType
-// node. (E.g. 'class ns::Foo myyvar'.)
-bool IsNamespaceQualifiedNode(const ASTNode* ast_node);
+// See if a given ast_node is a qualified name part of an ElaboratedType
+// node (e.g. 'class ns::Foo x', 'class ::Global x' or 'class Outer::Inner x'.)
+bool IsQualifiedNameNode(const ASTNode* ast_node);
// Return true if the given ast_node is inside a C++ method body. Do
// this by walking up the AST tree until you find a CXXMethodDecl,
diff --git a/tests/cxx/fwd_decl_nested_class.cc b/tests/cxx/fwd_decl_nested_class.cc
index 2e85891..c93e627 100644
--- a/tests/cxx/fwd_decl_nested_class.cc
+++ b/tests/cxx/fwd_decl_nested_class.cc
@@ -24,6 +24,9 @@ class Foo {
class UsedAsPtrArg; // Necessary -- this use doesn't see the later dfn.
class UsedAsPtrReturn; // Necessary -- this use doesn't see the later dfn.
class UsedAsPtrMember; // Necessary -- this use doesn't see the later dfn.
+ class UsedAsFriend; // Necessary -- used as part of a friend declaration
+
+ friend class Foo::UsedAsFriend;
Foo() : init_(UsedFullyInInitializer()) { }
~Foo() { }
@@ -62,6 +65,7 @@ class Foo {
class UsedAsPtrReturn { };
class UsedAsPtrMember { };
struct UsedFullyInMethodNotForwardDeclared { };
+ class UsedAsFriend { };
UsedFullyInInitializer init_;
UsedImplicitlyInInitializer implicit_;
@@ -85,6 +89,9 @@ class Outer {
template<typename T> class UsedAsPtrArg; // Necessary
template<typename T> class UsedAsPtrReturn; // Necessary
template<typename T> class UsedAsPtrMember; // Necessary
+ template<typename T> class UsedAsFriend; // Necessary
+
+ friend class Outer::UsedAsFriend<int>;
Outer() : init_(UsedFullyInInitializer<int>()) { }
~Outer() { }
@@ -121,6 +128,7 @@ class Outer {
template<typename T> class UsedAsPtrReturn { };
template<typename T> class UsedAsPtrMember { };
template<typename T> class UsedFullyInMethodNotForwardDeclared { };
+ template<typename T> class UsedAsFriend { };
UsedFullyInInitializer<int> init_;
UsedImplicitlyInInitializer<int> implicit_;
@@ -130,11 +138,77 @@ class Outer {
template<typename T> class Outer::NoUsageDefinedOutOfLine {};
+
+// Now do the same thing again, but the containing class is templated
+
+template<class T>
+class Container {
+ class NoUsage; // Unnecessary
+ class UsedAsPtrInMethod; // Unnecessary
+ class UsedFullyInMethod; // Unnecessary
+ class UsedFullyInInitializer; // Unnecessary
+ class UsedImplicitlyInInitializer; // Unnecessary
+ class UsedInTypedef; // Necessary
+ class UsedAsPtrArg; // Necessary
+ class UsedAsPtrReturn; // Necessary
+ class UsedAsPtrMember; // Necessary
+ class UsedAsFriend; // Necessary
+
+ friend class Container<T>::UsedAsFriend;
+
+ Container() : init_(UsedFullyInInitializer()) { }
+ ~Container() { }
+
+ // If a nested class is used in a body of a method, no preceding
+ // declaration/definition is needed.
+ void Bar1() {
+ UsedAsPtrInMethod* x;
+ }
+ void Bar2() {
+ UsedFullyInMethod x;
+ UsedFullyInMethodNotForwardDeclared y;
+ TplFn<UsedFullyInMethodNotForwardDeclared>();
+ }
+
+ // If a nested class is used in a typedef, a preceding declaration
+ // is needed.
+ typedef UsedInTypedef UsedInTypedefType;
+
+ // If a nested class is used in a method declaration, a preceding
+ // declaration is needed.
+ void Bar3(UsedAsPtrArg* p);
+ UsedAsPtrReturn* Bar4();
+
+ UsedAsPtrMember* x_;
+
+ class NoUsage { };
+ class UsedAsPtrInMethod { };
+ class UsedFullyInMethod { };
+ class UsedFullyInInitializer { };
+ class UsedImplicitlyInInitializer { };
+ class UsedImplicitlyInInitializerNeverDeclared { };
+ class UsedAsPtrArg { };
+ class UsedAsPtrReturn { };
+ class UsedAsPtrMember { };
+ class UsedFullyInMethodNotForwardDeclared { };
+ class UsedAsFriend { };
+
+ UsedFullyInInitializer init_;
+ UsedImplicitlyInInitializer implicit_;
+ UsedImplicitlyInInitializerNeverDeclared implicit_never_declared_;
+ UsedImplicitlyInInitializerNeverDeclared* implicit_never_declared_ptr_;
+};
+
/**** IWYU_SUMMARY
tests/cxx/fwd_decl_nested_class.cc should add these lines:
tests/cxx/fwd_decl_nested_class.cc should remove these lines:
+- class Container::NoUsage; // lines XX-XX
+- class Container::UsedAsPtrInMethod; // lines XX-XX
+- class Container::UsedFullyInInitializer; // lines XX-XX
+- class Container::UsedFullyInMethod; // lines XX-XX
+- class Container::UsedImplicitlyInInitializer; // lines XX-XX
- class Foo::NoUsage; // lines XX-XX
- class Foo::UsedAsPtrInMethod; // lines XX-XX
- class Foo::UsedFullyInInitializer; // lines XX-XX
@@ -148,12 +222,19 @@ tests/cxx/fwd_decl_nested_class.cc should remove these lines:
- template <typename T> class Outer::UsedImplicitlyInInitializer; // lines XX-XX
The full include-list for tests/cxx/fwd_decl_nested_class.cc:
+class Container::UsedAsFriend; // lines XX-XX
+class Container::UsedAsPtrArg; // lines XX-XX
+class Container::UsedAsPtrMember; // lines XX-XX
+class Container::UsedAsPtrReturn; // lines XX-XX
+class Container::UsedInTypedef; // lines XX-XX
class Foo::NoUsageDefinedOutOfLine; // lines XX-XX
+class Foo::UsedAsFriend; // lines XX-XX
class Foo::UsedAsPtrArg; // lines XX-XX
class Foo::UsedAsPtrMember; // lines XX-XX
class Foo::UsedAsPtrReturn; // lines XX-XX
class Foo::UsedInTypedef; // lines XX-XX
template <typename T> class Outer::NoUsageDefinedOutOfLine; // lines XX-XX
+template <typename T> class Outer::UsedAsFriend; // lines XX-XX
template <typename T> class Outer::UsedAsPtrArg; // lines XX-XX
template <typename T> class Outer::UsedAsPtrMember; // lines XX-XX
template <typename T> class Outer::UsedAsPtrReturn; // lines XX-XX