https://github.com/ckandeler updated https://github.com/llvm/llvm-project/pull/83412
>From 01f74ddece947755938ccecbcc5f9d18a41eb793 Mon Sep 17 00:00:00 2001 From: Christian Kandeler <christian.kande...@qt.io> Date: Thu, 29 Feb 2024 12:26:52 +0100 Subject: [PATCH] [clangd] Remove potential prefix from enum value names ... when converting unscoped to scoped enums. With traditional enums, a popular technique to guard against potential name clashes is to use the enum name as a pseudo-namespace, like this: enum MyEnum { MyEnumValue1, MyEnumValue2 }; With scoped enums, this makes no sense, making it extremely unlikely that the user wants to keep such a prefix when modernizing. Therefore, our tweak now removes it. --- .../clangd/refactor/tweaks/ScopifyEnum.cpp | 53 +++++++++++++------ .../unittests/tweaks/ScopifyEnumTests.cpp | 38 +++++++++++-- 2 files changed, 70 insertions(+), 21 deletions(-) diff --git a/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp b/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp index e36b3249bc7b92..8e13ae52d121a6 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp @@ -40,15 +40,12 @@ namespace { /// void f() { E e1 = EV1; } /// /// After: -/// enum class E { EV1, EV2 }; -/// void f() { E e1 = E::EV1; } +/// enum class E { V1, V2 }; +/// void f() { E e1 = E::V1; } /// /// Note that the respective project code might not compile anymore /// if it made use of the now-gone implicit conversion to int. /// This is out of scope for this tweak. -/// -/// TODO: In the above example, we could detect that the values -/// start with the enum name, and remove that prefix. class ScopifyEnum : public Tweak { const char *id() const final; @@ -63,7 +60,8 @@ class ScopifyEnum : public Tweak { std::function<tooling::Replacement(StringRef, StringRef, unsigned)>; llvm::Error addClassKeywordToDeclarations(); llvm::Error scopifyEnumValues(); - llvm::Error scopifyEnumValue(const EnumConstantDecl &CD, StringRef Prefix); + llvm::Error scopifyEnumValue(const EnumConstantDecl &CD, StringRef EnumName, + bool StripPrefix); llvm::Expected<StringRef> getContentForFile(StringRef FilePath); unsigned getOffsetFromPosition(const Position &Pos, StringRef Content) const; llvm::Error addReplacementForReference(const ReferencesResult::Reference &Ref, @@ -125,25 +123,42 @@ llvm::Error ScopifyEnum::addClassKeywordToDeclarations() { } llvm::Error ScopifyEnum::scopifyEnumValues() { - std::string PrefixToInsert(D->getName()); - PrefixToInsert += "::"; + StringRef EnumName(D->getName()); + bool StripPrefix = true; + for (auto E : D->enumerators()) { + if (!E->getName().starts_with(EnumName)) { + StripPrefix = false; + break; + } + } for (auto E : D->enumerators()) { - if (auto Err = scopifyEnumValue(*E, PrefixToInsert)) + if (auto Err = scopifyEnumValue(*E, EnumName, StripPrefix)) return Err; } return llvm::Error::success(); } llvm::Error ScopifyEnum::scopifyEnumValue(const EnumConstantDecl &CD, - StringRef Prefix) { + StringRef EnumName, + bool StripPrefix) { for (const auto &Ref : findReferences(*S->AST, getPosition(CD), 0, S->Index, false) .References) { - if (Ref.Attributes & ReferencesResult::Declaration) + if (Ref.Attributes & ReferencesResult::Declaration) { + if (StripPrefix) { + const auto MakeReplacement = [&EnumName](StringRef FilePath, + StringRef /* Content */, + unsigned Offset) { + return tooling::Replacement(FilePath, Offset, EnumName.size(), {}); + }; + if (auto Err = addReplacementForReference(Ref, MakeReplacement)) + return Err; + } continue; + } - const auto MakeReplacement = [&Prefix](StringRef FilePath, - StringRef Content, unsigned Offset) { + const auto MakeReplacement = [&](StringRef FilePath, StringRef Content, + unsigned Offset) { const auto IsAlreadyScoped = [Content, Offset] { if (Offset < 2) return false; @@ -164,9 +179,15 @@ llvm::Error ScopifyEnum::scopifyEnumValue(const EnumConstantDecl &CD, } return false; }; - return IsAlreadyScoped() - ? tooling::Replacement() - : tooling::Replacement(FilePath, Offset, 0, Prefix); + if (StripPrefix) { + if (IsAlreadyScoped()) + return tooling::Replacement(FilePath, Offset, EnumName.size(), {}); + return tooling::Replacement(FilePath, Offset + EnumName.size(), 0, + "::"); + } + return IsAlreadyScoped() ? tooling::Replacement() + : tooling::Replacement(FilePath, Offset, 0, + EnumName.str() + "::"); }; if (auto Err = addReplacementForReference(Ref, MakeReplacement)) return Err; diff --git a/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp index b5a964a5a26d80..ee09beed502efe 100644 --- a/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp +++ b/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp @@ -26,7 +26,7 @@ enum ^E; )cpp"); } -TEST_F(ScopifyEnumTest, ApplyTest) { +TEST_F(ScopifyEnumTest, ApplyTestWithPrefix) { std::string Original = R"cpp( enum ^E { EV1, EV2, EV3 }; enum E; @@ -39,13 +39,41 @@ E func(E in) } )cpp"; std::string Expected = R"cpp( -enum class E { EV1, EV2, EV3 }; +enum class E { V1, V2, V3 }; enum class E; E func(E in) { - E out = E::EV1; - if (in == E::EV2) - out = E::EV3; + E out = E::V1; + if (in == E::V2) + out = E::V3; + return out; +} +)cpp"; + FileName = "Test.cpp"; + SCOPED_TRACE(Original); + EXPECT_EQ(apply(Original), Expected); +} + +TEST_F(ScopifyEnumTest, ApplyTestWithoutPrefix) { + std::string Original = R"cpp( +enum ^E { V1, V2, V3 }; +enum E; +E func(E in) +{ + E out = V1; + if (in == V2) + out = E::V3; + return out; +} +)cpp"; + std::string Expected = R"cpp( +enum class E { V1, V2, V3 }; +enum class E; +E func(E in) +{ + E out = E::V1; + if (in == E::V2) + out = E::V3; return out; } )cpp"; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits