Disable forward-declares for decls in inline namespaces

Technically users can forward-declare inline namespaces, e.g.

  namespace std {
  inline namespace __1 {

  template <class T>
  struct hash;

  }
  }

but that would be both confusing and problematic:

1) The symbol is referred to as std::hash, but declared here with a name
   nobody should ever see.
2) When a new ABI version is added for std::hash, a new internal
   namespace -- e.g. __2 -- will be added, and std::hash and
   std::__1::hash no longer refer to the same symbol.

As inline namespaces are intended primarily as a versioning feature for
library authors, avoid trying to second-guess them and just require a
full use for any decl inside an inline namespace.

This fixes issue #713.
This commit is contained in:
Kim Grasman 2019-11-06 21:12:34 +01:00 committed by Kim Gräsman
parent 8353f98e6a
commit 613efed42e
8 changed files with 118 additions and 0 deletions

View File

@ -953,6 +953,16 @@ bool IsExplicitInstantiation(const clang::Decl* decl) {
kind == clang::TSK_ExplicitInstantiationDefinition;
}
bool IsInInlineNamespace(const Decl* decl) {
const DeclContext* dc = decl->getDeclContext();
for (; dc; dc = dc->getParent()) {
if (dc->isInlineNamespace())
return true;
}
return false;
}
bool IsForwardDecl(const NamedDecl* decl) {
if (const auto* record_decl = dyn_cast<RecordDecl>(decl)) {

View File

@ -597,6 +597,9 @@ bool IsFriendDecl(const clang::Decl* decl);
// or definition.
bool IsExplicitInstantiation(const clang::Decl* decl);
// Returns true if this decl is nested inside an inline namespace.
bool IsInInlineNamespace(const clang::Decl* decl);
// Returns true if a named decl looks like a forward-declaration of a
// class (rather than a definition, a friend declaration, or an 'in
// place' declaration like 'struct Foo' in 'void MyFunc(struct Foo*);'

View File

@ -716,6 +716,10 @@ static void LogIncludeMapping(const string& reason, const OneUse& use) {
namespace internal {
bool DeclCanBeForwardDeclared(const Decl* decl) {
// Nothing inside an inline namespace can be forward-declared.
if (IsInInlineNamespace(decl))
return false;
// Class templates can always be forward-declared.
if (isa<ClassTemplateDecl>(decl))
return true;

View File

@ -158,6 +158,7 @@ class OneIwyuTest(unittest.TestCase):
'implicit_ctor.cc': ['.'],
'include_cycle.cc': ['.'],
'include_with_using.cc': ['.'],
'inline_namespace.cc': ['.'],
'internal/internal_files.cc': ['.'],
'iwyu_stricter_than_cpp.cc': ['.'],
'keep_includes.c': ['.'],

View File

@ -0,0 +1,15 @@
//===--- inline_namespace-d1.h - test input file for iwyu -----------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef INCLUDE_WHAT_YOU_USE_TESTS_CXX_INLINE_NAMESPACE_D1_H_
#define INCLUDE_WHAT_YOU_USE_TESTS_CXX_INLINE_NAMESPACE_D1_H_
#include "tests/cxx/inline_namespace-i1.h"
#endif // INCLUDE_WHAT_YOU_USE_TESTS_CXX_INLINE_NAMESPACE_D1_H_

View File

@ -0,0 +1,24 @@
//===--- inline_namespace-i1.h - test input file for iwyu -----------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef INCLUDE_WHAT_YOU_USE_TESTS_CXX_INLINE_NAMESPACE_I1_H_
#define INCLUDE_WHAT_YOU_USE_TESTS_CXX_INLINE_NAMESPACE_I1_H_
namespace xyz {
inline namespace v1 {
struct Foo {
int value;
};
} // namespace v1
} // namespace xyz
#endif // INCLUDE_WHAT_YOU_USE_TESTS_CXX_INLINE_NAMESPACE_I1_H_

View File

@ -0,0 +1,26 @@
//===--- inline_namespace.cc - test input file for iwyu -------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Tests that IWYU never considers a decl inside an inline namespace
// forward-declarable, and that diagnostics never mention the inline namespace
// name (xyz::v1).
#include "tests/cxx/inline_namespace.h"
// IWYU: xyz::Foo is...*inline_namespace-i1.h
int Function(const xyz::Foo& foo) {
// IWYU: xyz::Foo is...*inline_namespace-i1.h
return foo.value;
}
/**** IWYU_SUMMARY
(tests/cxx/inline_namespace.cc has correct #includes/fwd-decls)
***** IWYU_SUMMARY */

View File

@ -0,0 +1,35 @@
//===--- inline_namespace.h - test input file for iwyu --------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef INCLUDE_WHAT_YOU_USE_TESTS_CXX_INLINE_NAMESPACE_H_
#define INCLUDE_WHAT_YOU_USE_TESTS_CXX_INLINE_NAMESPACE_H_
#include "tests/cxx/inline_namespace-d1.h"
// A forward-declare would typically be enough here, but the presence of the
// inline namespace xyz::v1 disqualifies forward declaration, and promotes it to
// a full use.
// IWYU: xyz::Foo is...*inline_namespace-i1.h
int Function(const xyz::Foo& foo);
#endif // INCLUDE_WHAT_YOU_USE_TESTS_CXX_INLINE_NAMESPACE_H_
/**** IWYU_SUMMARY
tests/cxx/inline_namespace.h should add these lines:
#include "tests/cxx/inline_namespace-i1.h"
tests/cxx/inline_namespace.h should remove these lines:
- #include "tests/cxx/inline_namespace-d1.h" // lines XX-XX
The full include-list for tests/cxx/inline_namespace.h:
#include "tests/cxx/inline_namespace-i1.h" // for Foo
***** IWYU_SUMMARY */