[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-05-02 Thread Christian Kandeler via cfe-commits

https://github.com/ckandeler closed 
https://github.com/llvm/llvm-project/pull/83412
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-05-02 Thread Christian Kandeler via cfe-commits

https://github.com/ckandeler reopened 
https://github.com/llvm/llvm-project/pull/83412
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-05-02 Thread Christian Kandeler via cfe-commits

https://github.com/ckandeler closed 
https://github.com/llvm/llvm-project/pull/83412
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-05-02 Thread Julian Schmidt via cfe-commits

https://github.com/5chmidti approved this pull request.

LGTM

https://github.com/llvm/llvm-project/pull/83412
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-05-02 Thread Christian Kandeler via cfe-commits

ckandeler wrote:

Underscore separators get removed now.

https://github.com/llvm/llvm-project/pull/83412
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-05-02 Thread Christian Kandeler via cfe-commits

https://github.com/ckandeler updated 
https://github.com/llvm/llvm-project/pull/83412

>From 349edc160e9c13068c42b35321814296bb713a09 Mon Sep 17 00:00:00 2001
From: Christian Kandeler 
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| 61 -
 .../unittests/tweaks/ScopifyEnumTests.cpp | 66 +--
 clang-tools-extra/docs/ReleaseNotes.rst   |  3 +
 3 files changed, 108 insertions(+), 22 deletions(-)

diff --git a/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp
index e36b3249bc7b92..2be2bbc422af75 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;
   llvm::Error addClassKeywordToDeclarations();
   llvm::Error scopifyEnumValues();
-  llvm::Error scopifyEnumValue(const EnumConstantDecl , StringRef Prefix);
+  llvm::Error scopifyEnumValue(const EnumConstantDecl , StringRef EnumName,
+   bool StripPrefix);
   llvm::Expected getContentForFile(StringRef FilePath);
   unsigned getOffsetFromPosition(const Position , StringRef Content) const;
   llvm::Error addReplacementForReference(const ReferencesResult::Reference 
,
@@ -125,25 +123,45 @@ llvm::Error ScopifyEnum::addClassKeywordToDeclarations() {
 }
 
 llvm::Error ScopifyEnum::scopifyEnumValues() {
-  std::string PrefixToInsert(D->getName());
-  PrefixToInsert += "::";
-  for (auto E : D->enumerators()) {
-if (auto Err = scopifyEnumValue(*E, PrefixToInsert))
+  StringRef EnumName(D->getName());
+  bool StripPrefix = true;
+  for (const EnumConstantDecl *E : D->enumerators()) {
+if (!E->getName().starts_with(EnumName)) {
+  StripPrefix = false;
+  break;
+}
+  }
+  for (const EnumConstantDecl *E : D->enumerators()) {
+if (auto Err = scopifyEnumValue(*E, EnumName, StripPrefix))
   return Err;
   }
   return llvm::Error::success();
 }
 
 llvm::Error ScopifyEnum::scopifyEnumValue(const EnumConstantDecl ,
-  StringRef Prefix) {
+  StringRef EnumName,
+  bool StripPrefix) {
   for (const auto  :
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 = [](StringRef FilePath,
+ StringRef Content,
+ unsigned Offset) {
+  unsigned Length = EnumName.size();
+  if (Content[Offset + Length] == '_')
+++Length;
+  return tooling::Replacement(FilePath, Offset, Length, {});
+};
+if (auto Err = addReplacementForReference(Ref, MakeReplacement))
+  return Err;
+  }
   continue;
+}
 
-const auto MakeReplacement = [](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 +182,18 @@ llvm::Error ScopifyEnum::scopifyEnumValue(const 
EnumConstantDecl ,
 }
 return false;
   };
-  return IsAlreadyScoped()
- ? tooling::Replacement()
- : tooling::Replacement(FilePath, Offset, 0, Prefix);
+  if (StripPrefix) {
+const int ExtraLength =
+Content[Offset + EnumName.size()] == '_' ? 1 : 0;
+if (IsAlreadyScoped())
+ 

[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-05-01 Thread Julian Schmidt via cfe-commits

https://github.com/5chmidti commented:

When the enumerators start with the enum name, but the names contain a `_` as a 
separator, then applying this tweak will result in `_`-prefixed enumerators. 
Can you please handle that case as well and remove the `_`? Otherwise, this 
looks good to me

https://github.com/llvm/llvm-project/pull/83412
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-04-15 Thread Christian Kandeler via cfe-commits

ckandeler wrote:

Incorporated review comments.

https://github.com/llvm/llvm-project/pull/83412
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-04-15 Thread Christian Kandeler via cfe-commits

https://github.com/ckandeler updated 
https://github.com/llvm/llvm-project/pull/83412

>From 78393d26e62f2f9a30f366501f8e61b1a32d6af4 Mon Sep 17 00:00:00 2001
From: Christian Kandeler 
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| 55 +--
 .../unittests/tweaks/ScopifyEnumTests.cpp | 38 +++--
 clang-tools-extra/docs/ReleaseNotes.rst   |  3 +
 3 files changed, 74 insertions(+), 22 deletions(-)

diff --git a/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp
index e36b3249bc7b92..d44f2bb862ed07 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;
   llvm::Error addClassKeywordToDeclarations();
   llvm::Error scopifyEnumValues();
-  llvm::Error scopifyEnumValue(const EnumConstantDecl , StringRef Prefix);
+  llvm::Error scopifyEnumValue(const EnumConstantDecl , StringRef EnumName,
+   bool StripPrefix);
   llvm::Expected getContentForFile(StringRef FilePath);
   unsigned getOffsetFromPosition(const Position , StringRef Content) const;
   llvm::Error addReplacementForReference(const ReferencesResult::Reference 
,
@@ -125,25 +123,42 @@ llvm::Error ScopifyEnum::addClassKeywordToDeclarations() {
 }
 
 llvm::Error ScopifyEnum::scopifyEnumValues() {
-  std::string PrefixToInsert(D->getName());
-  PrefixToInsert += "::";
-  for (auto E : D->enumerators()) {
-if (auto Err = scopifyEnumValue(*E, PrefixToInsert))
+  StringRef EnumName(D->getName());
+  bool StripPrefix = true;
+  for (const EnumConstantDecl *E : D->enumerators()) {
+if (!E->getName().starts_with(EnumName)) {
+  StripPrefix = false;
+  break;
+}
+  }
+  for (const EnumConstantDecl *E : D->enumerators()) {
+if (auto Err = scopifyEnumValue(*E, EnumName, StripPrefix))
   return Err;
   }
   return llvm::Error::success();
 }
 
 llvm::Error ScopifyEnum::scopifyEnumValue(const EnumConstantDecl ,
-  StringRef Prefix) {
+  StringRef EnumName,
+  bool StripPrefix) {
   for (const auto  :
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 = [](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 = [](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 ,
 }
 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,
+"::");
+  }

[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-04-15 Thread Julian Schmidt via cfe-commits

https://github.com/5chmidti edited 
https://github.com/llvm/llvm-project/pull/83412
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-04-15 Thread Julian Schmidt via cfe-commits

https://github.com/5chmidti commented:

I think this change could use a release note in the docs, could you please add 
one?

https://github.com/llvm/llvm-project/pull/83412
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-04-15 Thread Julian Schmidt via cfe-commits


@@ -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()) {

5chmidti wrote:

Please use `const EnumConstantDecl *` instead of `auto`.

https://github.com/llvm/llvm-project/pull/83412
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-04-15 Thread Julian Schmidt via cfe-commits


@@ -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()) {

5chmidti wrote:

This is not in your changed lines, but please change this `auto` to `const 
EnumConstantDecl *` as well.

https://github.com/llvm/llvm-project/pull/83412
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-03-01 Thread Christian Kandeler via cfe-commits

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 
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;
   llvm::Error addClassKeywordToDeclarations();
   llvm::Error scopifyEnumValues();
-  llvm::Error scopifyEnumValue(const EnumConstantDecl , StringRef Prefix);
+  llvm::Error scopifyEnumValue(const EnumConstantDecl , StringRef EnumName,
+   bool StripPrefix);
   llvm::Expected getContentForFile(StringRef FilePath);
   unsigned getOffsetFromPosition(const Position , StringRef Content) const;
   llvm::Error addReplacementForReference(const ReferencesResult::Reference 
,
@@ -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 ,
-  StringRef Prefix) {
+  StringRef EnumName,
+  bool StripPrefix) {
   for (const auto  :
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 = [](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 = [](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 ,
 }
 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, 

[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-02-29 Thread via cfe-commits

llvmbot wrote:




@llvm/pr-subscribers-clangd

Author: Christian Kandeler (ckandeler)


Changes

... 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.

---
Full diff: https://github.com/llvm/llvm-project/pull/83412.diff


2 Files Affected:

- (modified) clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp (+35-14) 
- (modified) clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp 
(+4-4) 


``diff
diff --git a/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp
index e36b3249bc7b92..5c042ee7b782b9 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;
   llvm::Error addClassKeywordToDeclarations();
   llvm::Error scopifyEnumValues();
-  llvm::Error scopifyEnumValue(const EnumConstantDecl , StringRef Prefix);
+  llvm::Error scopifyEnumValue(const EnumConstantDecl , StringRef EnumName,
+   bool StripPrefix);
   llvm::Expected getContentForFile(StringRef FilePath);
   unsigned getOffsetFromPosition(const Position , StringRef Content) const;
   llvm::Error addReplacementForReference(const ReferencesResult::Reference 
,
@@ -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 ,
-  StringRef Prefix) {
+  StringRef EnumName,
+  bool StripPrefix) {
   for (const auto  :
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 = [](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 = [](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 ,
 }
 return false;
   };
+  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, Prefix);
+ : tooling::Replacement(FilePath, Offset, 0, EnumName);
 };
 if (auto Err = addReplacementForReference(Ref, MakeReplacement))
   return Err;
diff --git a/clang-tools-extra/clangd/unittests/tweaks/ScopifyEnumTests.cpp 

[clang-tools-extra] [clangd] Remove potential prefix from enum value names (PR #83412)

2024-02-29 Thread Christian Kandeler via cfe-commits

https://github.com/ckandeler created 
https://github.com/llvm/llvm-project/pull/83412

... 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.

>From c687b73939821eba285b35e50c241738ba7915e4 Mon Sep 17 00:00:00 2001
From: Christian Kandeler 
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| 49 +--
 .../unittests/tweaks/ScopifyEnumTests.cpp |  8 +--
 2 files changed, 39 insertions(+), 18 deletions(-)

diff --git a/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/ScopifyEnum.cpp
index e36b3249bc7b92..5c042ee7b782b9 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;
   llvm::Error addClassKeywordToDeclarations();
   llvm::Error scopifyEnumValues();
-  llvm::Error scopifyEnumValue(const EnumConstantDecl , StringRef Prefix);
+  llvm::Error scopifyEnumValue(const EnumConstantDecl , StringRef EnumName,
+   bool StripPrefix);
   llvm::Expected getContentForFile(StringRef FilePath);
   unsigned getOffsetFromPosition(const Position , StringRef Content) const;
   llvm::Error addReplacementForReference(const ReferencesResult::Reference 
,
@@ -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 ,
-  StringRef Prefix) {
+  StringRef EnumName,
+  bool StripPrefix) {
   for (const auto  :
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 = [](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 = [](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 ,
 }
 return false;
   };
+  if (StripPrefix) {
+if (IsAlreadyScoped())
+  return tooling::Replacement(FilePath, Offset, EnumName.size(), {});
+