summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBolshakov <bolsh.andrey@yandex.ru>2022-05-28 16:07:14 +0300
committerKim Gräsman <kim.grasman@gmail.com>2022-06-13 07:47:21 +0200
commitfb41044b9fc662d895fb3f85550a92d4706e4942 (patch)
tree561b38b4c765797baade6d306f9c37e0aa3e2304
parent799a8ef1b5da7d836bbb258677232d0810cacfcf (diff)
Suggest enumeration opaque declarations
Opaque (i.e., in fact, forward) declarations are allowed for scoped enumerations and unscoped ones with underlying type explicitly specified.
-rw-r--r--iwyu.cc9
-rw-r--r--iwyu_ast_util.cc4
-rw-r--r--iwyu_ast_util.h6
-rw-r--r--iwyu_output.cc23
-rw-r--r--tests/cxx/badinc.cc2
5 files changed, 38 insertions, 6 deletions
diff --git a/iwyu.cc b/iwyu.cc
index 2c1c302..e7a2387 100644
--- a/iwyu.cc
+++ b/iwyu.cc
@@ -2534,10 +2534,8 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
// this the canonical place to figure out if we can forward-declare.
bool CanForwardDeclareType(const ASTNode* ast_node) const {
CHECK_(ast_node->IsA<Type>());
- // Cannot forward-declare an enum even if it's in a forward-declare context.
- // TODO(vsapsai): make enums forward-declarable in C++11.
- if (ast_node->IsA<EnumType>())
- return false;
+ if (const auto* enum_type = ast_node->GetAs<EnumType>())
+ return CanBeOpaqueDeclared(enum_type);
// If we're in a forward-declare context, well then, there you have it.
if (ast_node->in_forward_declare_context())
return true;
@@ -2956,6 +2954,9 @@ class InstantiatedTemplateVisitor
return true;
}
+ if (const auto* enum_type = dyn_cast<EnumType>(type))
+ return !CanBeOpaqueDeclared(enum_type);
+
// If we're inside a typedef, we don't need our full type info --
// in this case we follow what the C++ language allows and let
// the underlying type of a typedef be forward-declared. This has
diff --git a/iwyu_ast_util.cc b/iwyu_ast_util.cc
index 8ae1dd0..74adba5 100644
--- a/iwyu_ast_util.cc
+++ b/iwyu_ast_util.cc
@@ -1346,6 +1346,10 @@ map<const clang::Type*, const clang::Type*> GetTplTypeResugarMapForClass(
GetTplTypeResugarMapForClassNoComponentTypes(type));
}
+bool CanBeOpaqueDeclared(const clang::EnumType* type) {
+ return type->getDecl()->isFixed();
+}
+
// --- Utilities for Stmt.
bool IsAddressOf(const Expr* expr) {
diff --git a/iwyu_ast_util.h b/iwyu_ast_util.h
index c8bd362..164d7d2 100644
--- a/iwyu_ast_util.h
+++ b/iwyu_ast_util.h
@@ -772,6 +772,12 @@ map<const clang::Type*, const clang::Type*> GetTplTypeResugarMapForClass(
map<const clang::Type*, const clang::Type*>
GetTplTypeResugarMapForClassNoComponentTypes(const clang::Type* type);
+// Returns true if, for the given enumeration type, opaque (i.e. forward,
+// in fact) declarations are allowed. It means that the enumeration should be
+// either scoped or unscoped with explicitly stated underlying type,
+// according to the standard.
+bool CanBeOpaqueDeclared(const clang::EnumType* type);
+
// --- Utilities for Stmt.
// Returns true if the given expr is '&<something>'.
diff --git a/iwyu_output.cc b/iwyu_output.cc
index 251f367..e67474c 100644
--- a/iwyu_output.cc
+++ b/iwyu_output.cc
@@ -33,6 +33,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Type.h"
#include "clang/Basic/SourceLocation.h"
namespace include_what_you_use {
@@ -146,6 +147,15 @@ const FakeNamedDecl* FakeNamedDeclIfItIsOne(const clang::NamedDecl* decl) {
return GetOrDefault(g_fake_named_decl_map, decl, nullptr);
}
+std::string PrintableUnderlyingType(const EnumDecl* enum_decl) {
+ if (const clang::TypeSourceInfo* type_source_info =
+ enum_decl->getIntegerTypeSourceInfo()) {
+ return " : " + type_source_info->getType().getAsString();
+ }
+
+ return std::string();
+}
+
} // anonymous namespace
FakeNamedDecl::FakeNamedDecl(const string& kind_name, const string& qual_name,
@@ -169,6 +179,12 @@ string GetKindName(const TagDecl* tag_decl) {
if (const FakeNamedDecl* fake = FakeNamedDeclIfItIsOne(named_decl)) {
return fake->kind_name();
}
+
+ if (const auto* enum_decl = dyn_cast<EnumDecl>(tag_decl)) {
+ if (enum_decl->isScoped())
+ return enum_decl->isScopedUsingClassTag() ? "enum class" : "enum struct";
+ }
+
return tag_decl->getKindName().str();
}
@@ -365,7 +381,12 @@ string PrintForwardDeclare(const NamedDecl* decl,
CHECK_((isa<TagDecl>(decl) || isa<TemplateDecl>(decl)) &&
"IWYU only allows forward declaring (possibly template) tag types");
- std::string fwd_decl = std::string(decl->getName()) + ";";
+ std::string fwd_decl = std::string(decl->getName());
+ if (const auto* enum_decl = dyn_cast<EnumDecl>(decl)) {
+ fwd_decl += PrintableUnderlyingType(enum_decl);
+ }
+ fwd_decl += ";";
+
bool seen_namespace = false;
// Anonymous namespaces are not using the more concise syntax.
bool concat_namespaces = cxx17ns && !decl->isInAnonymousNamespace();
diff --git a/tests/cxx/badinc.cc b/tests/cxx/badinc.cc
index 36e4aa3..76c73d0 100644
--- a/tests/cxx/badinc.cc
+++ b/tests/cxx/badinc.cc
@@ -1547,7 +1547,7 @@ int main() {
// IWYU: I1_Class is...*badinc-i1.h
// IWYU: kI1ConstInt is...*badinc-i1.h
I1_Class* newed_i1_class_array = new I1_Class[kI1ConstInt];
- // TODO(csilvers): IWYU: I2_Enum is...*badinc-i2.h
+ // IWYU: I2_Enum is...*badinc-i2.h
// IWYU: std::vector is...*<vector>
delete newed_vector;
// IWYU: I1_Class is...*badinc-i1.h