diff options
author | Kim Grasman <kim.grasman@gmail.com> | 2016-08-11 16:01:38 +0200 |
---|---|---|
committer | Kim Grasman <kim.grasman@gmail.com> | 2016-08-15 21:10:02 +0200 |
commit | 9c432a97639f61a842bbc3f058e249fde6df39d6 (patch) | |
tree | ad34e8d02aaa0ce3a3225c6976a81fae718ba259 | |
parent | f09dba693412463fcca0898a202763761fe60ccc (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.cc | 8 | ||||
-rw-r--r-- | iwyu_ast_util.cc | 8 | ||||
-rw-r--r-- | iwyu_ast_util.h | 6 | ||||
-rw-r--r-- | tests/cxx/fwd_decl_nested_class.cc | 81 |
4 files changed, 90 insertions, 13 deletions
@@ -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 |