diff options
author | Bolshakov <bolsh.andrey@yandex.ru> | 2022-05-28 16:07:14 +0300 |
---|---|---|
committer | Kim Gräsman <kim.grasman@gmail.com> | 2022-06-13 07:47:21 +0200 |
commit | fb41044b9fc662d895fb3f85550a92d4706e4942 (patch) | |
tree | 561b38b4c765797baade6d306f9c37e0aa3e2304 | |
parent | 799a8ef1b5da7d836bbb258677232d0810cacfcf (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.cc | 9 | ||||
-rw-r--r-- | iwyu_ast_util.cc | 4 | ||||
-rw-r--r-- | iwyu_ast_util.h | 6 | ||||
-rw-r--r-- | iwyu_output.cc | 23 | ||||
-rw-r--r-- | tests/cxx/badinc.cc | 2 |
5 files changed, 38 insertions, 6 deletions
@@ -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 |