diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp --- a/clang-tools-extra/clangd/IncludeCleaner.cpp +++ b/clang-tools-extra/clangd/IncludeCleaner.cpp @@ -35,6 +35,16 @@ } bool VisitTagType(TagType *TT) { + // For enumerations we will require only the definition if it's present and + // the underlying type is not specified. + if (TT->isEnumeralType()) { + const auto *Enum = llvm::dyn_cast(TT->getDecl()); + assert(Enum); + if (!Enum->getIntegerTypeSourceInfo() && TT->getDecl()->getDefinition()) { + Result.insert(TT->getDecl()->getDefinition()->getLocation()); + return true; + } + } add(TT->getDecl()); return true; } @@ -67,29 +77,35 @@ } bool TraverseType(QualType T) { - if (isNew(T.getTypePtrOrNull())) { // don't care about quals + if (isNew(T.getTypePtrOrNull())) // don't care about quals Base::TraverseType(T); - } return true; } bool VisitUsingDecl(UsingDecl *D) { - for (const auto *Shadow : D->shadows()) { + for (const auto *Shadow : D->shadows()) add(Shadow->getTargetDecl()); - } return true; } + // Require redeclarations only for definitions and only when the underlying + // type is specified. + bool VisitEnumDecl(EnumDecl *D) { + if (D != D->getDefinition() || !D->getIntegerTypeSourceInfo()) + return false; + for (const auto *Redecl : D->redecls()) + Result.insert(Redecl->getLocation()); + return false; + } + private: using Base = RecursiveASTVisitor; void add(const Decl *D) { - if (!D || !isNew(D->getCanonicalDecl())) { + if (!D || !isNew(D->getCanonicalDecl())) return; - } - for (const Decl *Redecl : D->redecls()) { + for (const Decl *Redecl : D->redecls()) Result.insert(Redecl->getLocation()); - } } bool isNew(const void *P) { return P && Visited.insert(P).second; } diff --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp --- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -103,6 +103,34 @@ "struct ^X { enum ^Language { ^CXX = 42, Python = 9000}; };", "int Lang = X::CXX;", }, + { + "enum class Color;", + "enum class Color {};", + }, + { + "enum class ^Color : int;", + "enum class Color : int {};", + }, + { + "enum class Color : int {};", + "enum class Color : int;", + }, + { + "enum class Color;", + "enum class Color {}; Color c;", + }, + { + "enum class ^Color : char;", + "Color c;", + }, + { + "enum class ^Color : char {};", + "Color c;", + }, + { + "enum class ^Color;", + "Color c;", + }, { // When a type is resolved via a using declaration, the // UsingShadowDecl is not referenced in the AST.