https://github.com/GuillaumeF0 created 
https://github.com/llvm/llvm-project/pull/157727

None

>From 4042478aac711d34ef396f54ad314a092b967778 Mon Sep 17 00:00:00 2001
From: Guillaume Frognier <guillaume.frogn...@gmail.com>
Date: Tue, 9 Sep 2025 19:25:43 +0200
Subject: [PATCH] [clangd] Add code action to generate getter and setter for a
 field in a C++ class

---
 clang-tools-extra/clangd/CMakeLists.txt       |   1 +
 clang-tools-extra/clangd/Config.h             |   8 +
 clang-tools-extra/clangd/ConfigCompile.cpp    |  21 +-
 clang-tools-extra/clangd/ConfigFragment.h     |   6 +
 clang-tools-extra/clangd/ConfigYAML.cpp       |  12 +
 .../clangd/refactor/GenerateAccessorBase.cpp  | 214 ++++++++++++++++++
 .../clangd/refactor/GenerateAccessorBase.h    |  54 +++++
 .../clangd/refactor/tweaks/CMakeLists.txt     |   2 +
 .../clangd/refactor/tweaks/GenerateGetter.cpp |  72 ++++++
 .../clangd/refactor/tweaks/GenerateSetter.cpp |  93 ++++++++
 .../clangd/unittests/CMakeLists.txt           |   2 +
 .../unittests/tweaks/GenerateGetterTests.cpp  | 129 +++++++++++
 .../unittests/tweaks/GenerateSetterTests.cpp  | 162 +++++++++++++
 .../clangd/refactor/tweaks/BUILD.gn           |   1 +
 .../clangd/unittests/BUILD.gn                 |   1 +
 15 files changed, 774 insertions(+), 4 deletions(-)
 create mode 100644 clang-tools-extra/clangd/refactor/GenerateAccessorBase.cpp
 create mode 100644 clang-tools-extra/clangd/refactor/GenerateAccessorBase.h
 create mode 100644 clang-tools-extra/clangd/refactor/tweaks/GenerateGetter.cpp
 create mode 100644 clang-tools-extra/clangd/refactor/tweaks/GenerateSetter.cpp
 create mode 100644 
clang-tools-extra/clangd/unittests/tweaks/GenerateGetterTests.cpp
 create mode 100644 
clang-tools-extra/clangd/unittests/tweaks/GenerateSetterTests.cpp

diff --git a/clang-tools-extra/clangd/CMakeLists.txt 
b/clang-tools-extra/clangd/CMakeLists.txt
index fb3f05329be21..ce72b09d47f40 100644
--- a/clang-tools-extra/clangd/CMakeLists.txt
+++ b/clang-tools-extra/clangd/CMakeLists.txt
@@ -144,6 +144,7 @@ add_clang_library(clangDaemon STATIC
   index/dex/PostingList.cpp
   index/dex/Trigram.cpp
 
+  refactor/GenerateAccessorBase.cpp
   refactor/InsertionPoint.cpp
   refactor/Rename.cpp
   refactor/Tweak.cpp
diff --git a/clang-tools-extra/clangd/Config.h 
b/clang-tools-extra/clangd/Config.h
index 01997cee08515..c006b06a5c9b2 100644
--- a/clang-tools-extra/clangd/Config.h
+++ b/clang-tools-extra/clangd/Config.h
@@ -133,6 +133,14 @@ struct Config {
     // List of matcher functions for inserting certain headers with <> or "".
     std::vector<std::function<bool(llvm::StringRef)>> QuotedHeaders;
     std::vector<std::function<bool(llvm::StringRef)>> AngledHeaders;
+
+    // Getter and setter's prefixes
+    // Prefix for a get accessor e.g. "get"
+    std::string GetterPrefix;
+    // Prefix for a set accessor e.g. "set"
+    std::string SetterPrefix;
+    // Prefix for a set accessor's parameter e.g. "new"
+    std::string SetterParameterPrefix;
   } Style;
 
   /// controls the completion options for argument lists.
diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp 
b/clang-tools-extra/clangd/ConfigCompile.cpp
index 962a48bcb7671..7ecc1f610a9b2 100644
--- a/clang-tools-extra/clangd/ConfigCompile.cpp
+++ b/clang-tools-extra/clangd/ConfigCompile.cpp
@@ -508,6 +508,19 @@ struct FragmentCompiler {
             C.Style.AngledHeaders.emplace_back(AngledFilter);
           });
     }
+    if (F.GetterPrefix)
+      Out.Apply.push_back([Value(**F.GetterPrefix)](const Params &, Config &C) 
{
+        C.Style.GetterPrefix = Value;
+      });
+    if (F.SetterPrefix)
+      Out.Apply.push_back([Value(**F.SetterPrefix)](const Params &, Config &C) 
{
+        C.Style.SetterPrefix = Value;
+      });
+    if (F.SetterParameterPrefix)
+      Out.Apply.push_back(
+          [Value(**F.SetterParameterPrefix)](const Params &, Config &C) {
+            C.Style.SetterParameterPrefix = Value;
+          });
   }
 
   auto compileHeaderRegexes(llvm::ArrayRef<Located<std::string>> 
HeaderPatterns)
@@ -564,10 +577,10 @@ struct FragmentCompiler {
       auto Fast = isFastTidyCheck(Str);
       if (!Fast.has_value()) {
         diag(Warning,
-             llvm::formatv(
-                 "Latency of clang-tidy check '{0}' is not known. "
-                 "It will only run if ClangTidy.FastCheckFilter is Loose or 
None",
-                 Str)
+             llvm::formatv("Latency of clang-tidy check '{0}' is not known. "
+                           "It will only run if ClangTidy.FastCheckFilter is "
+                           "Loose or None",
+                           Str)
                  .str(),
              Arg.Range);
       } else if (!*Fast) {
diff --git a/clang-tools-extra/clangd/ConfigFragment.h 
b/clang-tools-extra/clangd/ConfigFragment.h
index 2afeb36574b21..36e9584ed6d2d 100644
--- a/clang-tools-extra/clangd/ConfigFragment.h
+++ b/clang-tools-extra/clangd/ConfigFragment.h
@@ -326,6 +326,12 @@ struct Fragment {
     /// Matching is performed against the absolute path of the header
     /// within the project.
     std::vector<Located<std::string>> AngledHeaders;
+    /// Getter generation prefix i.e. "get".
+    std::optional<Located<std::string>> GetterPrefix;
+    /// Setter generation prefix i.e. "set".
+    std::optional<Located<std::string>> SetterPrefix;
+    /// Setter generation parameter prefix i.e. "new".
+    std::optional<Located<std::string>> SetterParameterPrefix;
   };
   StyleBlock Style;
 
diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp 
b/clang-tools-extra/clangd/ConfigYAML.cpp
index 392cf19b05a55..fee7436fe9e3e 100644
--- a/clang-tools-extra/clangd/ConfigYAML.cpp
+++ b/clang-tools-extra/clangd/ConfigYAML.cpp
@@ -130,6 +130,18 @@ class Parser {
       if (auto Values = scalarValues(N))
         F.AngledHeaders = std::move(*Values);
     });
+    Dict.handle("GetterPrefix", [&](Node &N) {
+      if (auto Value = scalarValue(N, "GetterPrefix"))
+        F.GetterPrefix = *Value;
+    });
+    Dict.handle("SetterPrefix", [&](Node &N) {
+      if (auto Value = scalarValue(N, "SetterPrefix"))
+        F.SetterPrefix = *Value;
+    });
+    Dict.handle("SetterParameterPrefix", [&](Node &N) {
+      if (auto Value = scalarValue(N, "SetterParameterPrefix"))
+        F.SetterParameterPrefix = *Value;
+    });
     Dict.parse(N);
   }
 
diff --git a/clang-tools-extra/clangd/refactor/GenerateAccessorBase.cpp 
b/clang-tools-extra/clangd/refactor/GenerateAccessorBase.cpp
new file mode 100644
index 0000000000000..d7d21051babb4
--- /dev/null
+++ b/clang-tools-extra/clangd/refactor/GenerateAccessorBase.cpp
@@ -0,0 +1,214 @@
+//===--- GenerateAccessor.cpp - Base class for getter/setter generation 
---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "GenerateAccessorBase.h"
+#include "AST.h"
+#include "Config.h"
+#include "ParsedAST.h"
+#include "refactor/InsertionPoint.h"
+#include "refactor/Tweak.h"
+#include "support/Logger.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <string>
+
+namespace clang {
+namespace clangd {
+
+Expected<Tweak::Effect> GenerateAccessorBase::apply(const Selection &Inputs) {
+  // Prefer to place the new method ...
+  std::vector<Anchor> Anchors = {
+      // On top of fields declaration
+      {[](const Decl *D) { return llvm::isa<FieldDecl>(D); }, Anchor::Above},
+      // At the bottom of public section
+      {[](const Decl *D) { return D->getAccess() == AS_public; },
+       Anchor::Below},
+      // Fallback: At the end of class
+      {[](const Decl *D) { return true; }, Anchor::Below},
+  };
+
+  std::string Code = buildCode();
+
+  auto Edit = insertDecl(Code, *Class, std::move(Anchors), AS_public);
+  if (!Edit)
+    return Edit.takeError();
+
+  return Effect::mainFileEdit(Inputs.AST->getSourceManager(),
+                              tooling::Replacements{std::move(*Edit)});
+}
+
+bool GenerateAccessorBase::prepare(const Selection &Inputs) {
+  // This tweak is available for C++ only.
+  if (!Inputs.AST->getLangOpts().CPlusPlus)
+    return false;
+
+  if (auto *N = Inputs.ASTSelection.commonAncestor()) {
+    Field = N->ASTNode.get<FieldDecl>();
+  }
+
+  // Trigger only on Field declaration.
+  if (!Field || !Field->getIdentifier())
+    return false;
+
+  // No setter for constant field, by extension no action for generate getter
+  // will be provided.
+  if (Field->getType().isConstQualified())
+    return false;
+
+  // Trigger only inside a class declaration.
+  Class = dyn_cast<CXXRecordDecl>(Field->getParent());
+  if (!Class || !Class->isThisDeclarationADefinition())
+    return false;
+
+  // Define accessor's name, field's base name and check if field has any
+  // prefix or suffix.
+  build();
+
+  // Trigger only if the class does not already have this method.
+  for (const auto *M : Class->methods()) {
+    if (M->getName() == AccessorName) {
+      return false;
+    }
+  }
+
+  dlog("GenerateAccessorBase for {0}?", Field->getName());
+
+  return true;
+}
+
+void GenerateAccessorBase::build() {
+  // Retrieve clang-tidy options for field's prefix and suffix in order to
+  // determine the base name of the field. Do not take Hungarian notation into
+  // account.
+  const auto &ClangTiddyOptions =
+      Config::current().Diagnostics.ClangTidy.CheckOptions;
+
+  auto GetOption = [&ClangTiddyOptions](llvm::StringRef Key) -> std::string {
+    auto It =
+        ClangTiddyOptions.find("readability-identifier-naming." + Key.str());
+    return It != ClangTiddyOptions.end() ? It->second : "";
+  };
+
+  std::string FieldPrefix;
+  std::string FieldSuffix;
+  // Visibility (public/protected/private)
+  switch (Field->getAccessUnsafe()) {
+  case AS_private:
+    FieldPrefix = GetOption("PrivateMemberPrefix");
+    FieldSuffix = GetOption("PrivateMemberSuffix");
+    break;
+  case AS_protected:
+    FieldPrefix = GetOption("ProtectedMemberPrefix");
+    FieldSuffix = GetOption("ProtectedMemberSuffix");
+    break;
+  case AS_public:
+    FieldPrefix = GetOption("PublicMemberPrefix");
+    FieldSuffix = GetOption("PublicMemberSuffix");
+    break;
+  case AS_none:
+    break;
+  }
+  if (FieldPrefix.empty() && FieldSuffix.empty()) {
+    FieldPrefix = GetOption("MemberPrefix");
+    FieldSuffix = GetOption("MemberSuffix");
+  }
+
+  llvm::StringRef BaseNameRef = Field->getName();
+  if (!FieldPrefix.empty()) {
+    FieldWithPreffixOrSuffix |= BaseNameRef.consume_front(FieldPrefix);
+  }
+  if (!FieldSuffix.empty()) {
+    FieldWithPreffixOrSuffix |= BaseNameRef.consume_back(FieldSuffix);
+  }
+
+  std::string FieldBaseNameLocal = BaseNameRef.str();
+  FieldBaseName = FieldBaseNameLocal;
+
+  // Get user-configured getter/setter prefix.
+  std::string AccessorPrefix = retrieveAccessorPrefix();
+  if (AccessorPrefix.empty()) {
+    AccessorName = FieldBaseNameLocal;
+    return;
+  }
+
+  // Define getter method case.
+  std::string MethodCase = GetOption("PublicMethodCase");
+  if (MethodCase.empty())
+    MethodCase = GetOption("MethodCase");
+  if (MethodCase.empty())
+    MethodCase = GetOption("FunctionCase");
+
+  if (MethodCase == "lower_case") {
+    toLower(AccessorPrefix);
+    toLower(FieldBaseNameLocal);
+    AccessorName = AccessorPrefix + '_' + FieldBaseNameLocal;
+    return;
+  }
+  if (MethodCase == "UPPER_CASE") {
+    toUpper(AccessorPrefix);
+    toUpper(FieldBaseNameLocal);
+    AccessorName = AccessorPrefix + '_' + FieldBaseNameLocal;
+    return;
+  }
+  if (MethodCase == "camelBack") {
+    toLower(AccessorPrefix);
+    toCamelCase(FieldBaseNameLocal);
+    AccessorName = AccessorPrefix + FieldBaseNameLocal;
+    return;
+  }
+  if (MethodCase == "CamelCase") {
+    toCamelCase(AccessorPrefix);
+    toCamelCase(FieldBaseNameLocal);
+    AccessorName = AccessorPrefix + FieldBaseNameLocal;
+    return;
+  }
+  if (MethodCase == "camel_Snake_Back") {
+    toLower(AccessorPrefix);
+    toCamelCase(FieldBaseNameLocal);
+    AccessorName = AccessorPrefix + '_' + FieldBaseNameLocal;
+    return;
+  }
+  if (MethodCase == "Camel_Snake_Case") {
+    toCamelCase(AccessorPrefix);
+    toCamelCase(FieldBaseNameLocal);
+    AccessorName = AccessorPrefix + '_' + FieldBaseNameLocal;
+    return;
+  }
+  if (MethodCase == "Leading_upper_snake_case") {
+    toCamelCase(AccessorPrefix);
+    toLower(FieldBaseNameLocal);
+    AccessorName = AccessorPrefix + '_' + FieldBaseNameLocal;
+    return;
+  }
+  FieldBaseNameLocal[0] = llvm::toUpper(FieldBaseNameLocal[0]);
+  AccessorName = AccessorPrefix + FieldBaseNameLocal;
+}
+
+void GenerateAccessorBase::toLower(std::string &s) const {
+  std::transform(s.begin(), s.end(), s.begin(),
+                 [](unsigned char c) { return llvm::toLower(c); });
+}
+
+void GenerateAccessorBase::toUpper(std::string &s) const {
+  std::transform(s.begin(), s.end(), s.begin(),
+                 [](unsigned char c) { return llvm::toUpper(c); });
+}
+
+void GenerateAccessorBase::toCamelCase(std::string &s) const {
+  s[0] = llvm::toUpper(s[0]);
+  std::transform(s.begin() + 1, s.end(), s.begin() + 1,
+                 [](unsigned char c) { return llvm::toLower(c); });
+}
+
+} // namespace clangd
+} // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clangd/refactor/GenerateAccessorBase.h 
b/clang-tools-extra/clangd/refactor/GenerateAccessorBase.h
new file mode 100644
index 0000000000000..eda198353329c
--- /dev/null
+++ b/clang-tools-extra/clangd/refactor/GenerateAccessorBase.h
@@ -0,0 +1,54 @@
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_GENERATEACCESSORBASE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_GENERATEACCESSORBASE_H
+
+//===--- GenerateAccessor.h - Base class for getter/setter generation 
-----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "refactor/Tweak.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace clang {
+namespace clangd {
+
+class GenerateAccessorBase : public Tweak {
+public:
+  const char *id() const override = 0;
+  llvm::StringLiteral kind() const override = 0;
+  std::string title() const override = 0;
+
+  bool prepare(const Selection &Inputs) override;
+
+  Expected<Effect> apply(const Selection &Inputs) override;
+
+protected:
+  virtual std::string buildCode() const = 0;
+
+  void build();
+
+  virtual std::string retrieveAccessorPrefix() const = 0;
+
+  void toLower(std::string &s) const;
+
+  void toUpper(std::string &s) const;
+
+  void toCamelCase(std::string &s) const;
+
+  const CXXRecordDecl *Class = nullptr;
+  const FieldDecl *Field = nullptr;
+  std::string AccessorName;
+  std::string FieldBaseName;
+  bool FieldWithPreffixOrSuffix = false;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_GENERATEACCESSORBASE_H
diff --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt 
b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
index 1d6e38088ad67..943ddededa5f9 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
+++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
@@ -21,6 +21,8 @@ add_clang_library(clangDaemonTweaks OBJECT
   ExpandMacro.cpp
   ExtractFunction.cpp
   ExtractVariable.cpp
+  GenerateGetter.cpp
+  GenerateSetter.cpp
   MemberwiseConstructor.cpp
   ObjCLocalizeStringLiteral.cpp
   ObjCMemberwiseInitializer.cpp
diff --git a/clang-tools-extra/clangd/refactor/tweaks/GenerateGetter.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/GenerateGetter.cpp
new file mode 100644
index 0000000000000..10a6727366611
--- /dev/null
+++ b/clang-tools-extra/clangd/refactor/tweaks/GenerateGetter.cpp
@@ -0,0 +1,72 @@
+//===--- GenerateGetter.cpp - Generate getter methods 
---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AST.h"
+#include "Config.h"
+#include "refactor/GenerateAccessorBase.h"
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+static constexpr const char *GetterPrefixDefault = "get";
+
+namespace clang {
+namespace clangd {
+namespace {
+
+// A tweak that generates a getter for a field.
+//
+// Given:
+//   struct S { int x; };
+// Produces:
+//   int getX() const { return x; }
+//
+// Method's prefix can be configured with Style.GetterPrefix.
+//
+// We place the method inline, other tweaks are available to outline it.
+class GenerateGetter : public GenerateAccessorBase {
+public:
+  const char *id() const final;
+  llvm::StringLiteral kind() const override {
+    return CodeAction::REFACTOR_KIND;
+  }
+  std::string title() const override { return "Generate getter"; }
+
+private:
+  std::string buildCode() const override {
+    // Field type
+    QualType T = Field->getType().getLocalUnqualifiedType();
+
+    std::string S;
+    llvm::raw_string_ostream OS(S);
+    llvm::StringRef FieldName = Field->getName();
+
+    OS << printType(T, *Class) << " " << AccessorName << "() const { return "
+       << FieldName << "; }\n";
+    return S;
+  }
+
+  std::string retrieveAccessorPrefix() const override {
+    // Get user-configured getter prefix.
+    std::string GetterPrefix = Config::current().Style.GetterPrefix;
+    if (!GetterPrefix.empty()) {
+      return GetterPrefix;
+    }
+    if (FieldWithPreffixOrSuffix) {
+      return "";
+    }
+    return GetterPrefixDefault;
+  }
+};
+
+REGISTER_TWEAK(GenerateGetter)
+
+} // namespace
+} // namespace clangd
+} // namespace clang
diff --git a/clang-tools-extra/clangd/refactor/tweaks/GenerateSetter.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/GenerateSetter.cpp
new file mode 100644
index 0000000000000..0127f5968b333
--- /dev/null
+++ b/clang-tools-extra/clangd/refactor/tweaks/GenerateSetter.cpp
@@ -0,0 +1,93 @@
+//===--- GenerateSetter.cpp - Generate setter methods 
---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AST.h"
+#include "Config.h"
+#include "refactor/GenerateAccessorBase.cpp"
+#include "refactor/GenerateAccessorBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/OpenMPClause.h"
+#include "clang/AST/TypeBase.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+static constexpr const char *SetterPrefixDefault = "set";
+static constexpr const char *SetterParameterPrefixDefault = "new";
+
+namespace clang {
+namespace clangd {
+namespace {
+
+// A tweak that generates a setter for a field.
+//
+// Given:
+//   struct S { int x; };
+// Produces:
+//   void setX(int newX) { x = newX; }
+//
+// Method's prefix can be configured with Style.SetterPrefix.
+// Method's parameter prefix can be configured with
+// GetterSetter.SetterParameterPrefix.
+//
+// We place the method inline, other tweaks are available to outline it.
+class GenerateSetter : public GenerateAccessorBase {
+public:
+  const char *id() const final;
+  llvm::StringLiteral kind() const override {
+    return CodeAction::REFACTOR_KIND;
+  }
+  std::string title() const override { return "Generate setter"; }
+
+private:
+  std::string buildCode() const override {
+    QualType T = Field->getType().getLocalUnqualifiedType();
+    auto &Context = Class->getASTContext();
+
+    std::string S;
+    llvm::raw_string_ostream OS(S);
+
+    OS << "void " << AccessorName << "(";
+
+    // Use const-ref if type is not trivially copiable or its size is larger
+    // than the size of a pointer
+    if (!T.isTriviallyCopyableType(Context) ||
+        Context.getTypeSize(T) > Context.getTypeSize(Context.VoidPtrTy)) {
+      OS << "const " << printType(T, *Class);
+      if (!T->isReferenceType())
+        OS << " &";
+    } else
+      OS << printType(T, *Class) << " ";
+
+    std::string SetterParameterPrefix =
+        Config::current().Style.SetterParameterPrefix;
+    if (SetterParameterPrefix.empty() && !FieldWithPreffixOrSuffix) {
+      SetterParameterPrefix = SetterParameterPrefixDefault;
+    }
+    llvm::StringRef FieldName = Field->getName();
+    OS << SetterParameterPrefix << FieldBaseName << ") { " << FieldName << " = 
"
+       << SetterParameterPrefix << FieldBaseName << "; }\n";
+    return S;
+  }
+
+  std::string retrieveAccessorPrefix() const override {
+    // Get user-configured setter prefix
+    std::string SetterPrefix = Config::current().Style.SetterPrefix;
+    if (SetterPrefix.empty()) {
+      return SetterPrefixDefault;
+    }
+    return SetterPrefix;
+  }
+
+}; // namespace
+
+REGISTER_TWEAK(GenerateSetter)
+
+} // namespace
+} // namespace clangd
+} // namespace clang
diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt 
b/clang-tools-extra/clangd/unittests/CMakeLists.txt
index 9656eeaeb37ce..3dbae94560ee3 100644
--- a/clang-tools-extra/clangd/unittests/CMakeLists.txt
+++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt
@@ -129,6 +129,8 @@ add_unittest(ClangdUnitTests ClangdTests
   tweaks/ExpandMacroTests.cpp
   tweaks/ExtractFunctionTests.cpp
   tweaks/ExtractVariableTests.cpp
+  tweaks/GenerateGetterTests.cpp
+  tweaks/GenerateSetterTests.cpp
   tweaks/MemberwiseConstructorTests.cpp
   tweaks/ObjCLocalizeStringLiteralTests.cpp
   tweaks/ObjCMemberwiseInitializerTests.cpp
diff --git a/clang-tools-extra/clangd/unittests/tweaks/GenerateGetterTests.cpp 
b/clang-tools-extra/clangd/unittests/tweaks/GenerateGetterTests.cpp
new file mode 100644
index 0000000000000..6542d6dfeaad4
--- /dev/null
+++ b/clang-tools-extra/clangd/unittests/tweaks/GenerateGetterTests.cpp
@@ -0,0 +1,129 @@
+//===-- GenerateGetterTests.cpp 
-------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Config.h"
+#include "TweakTesting.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+TWEAK_TEST(GenerateGetter);
+
+TEST_F(GenerateGetterTest, Availability) {
+  // available on class member:
+  EXPECT_AVAILABLE("struct S { int ^x, y; };");
+  EXPECT_AVAILABLE("class S { private: int ^x; };");
+  EXPECT_AVAILABLE("class S { protected: int ^x; };");
+  EXPECT_AVAILABLE("union S { int ^x; };");
+  EXPECT_AVAILABLE("struct S { int ^x = 0; };");
+  // available on forward member:
+  EXPECT_AVAILABLE("/*error-ok*/class Forward; class A { Forward ^f; };");
+  // available on pointer type:
+  EXPECT_AVAILABLE("class Forward; class A { Forward *^f; };");
+  // available on reference type:
+  EXPECT_AVAILABLE("class A { int &^f; };");
+
+  // unavailable outside class member:
+  EXPECT_UNAVAILABLE("^struct ^S ^{ int f^oo(); ^int x, y; };");
+  // unavailable if method already exists:
+  EXPECT_UNAVAILABLE("struct S { int getX(); int ^x, y; };");
+  // unavailable on constant type:
+  EXPECT_UNAVAILABLE("class S { const int ^x, y; };");
+  // unavailable on static member:
+  EXPECT_UNAVAILABLE("struct S { static int ^x; };");
+}
+
+TEST_F(GenerateGetterTest, Edits) {
+  auto RunGetterTest = [&](llvm::StringRef GetterPrefix,
+                           llvm::StringMap<std::string> Options,
+                           llvm::StringRef Input, llvm::StringRef Expected) {
+    Config Cfg;
+    if (!GetterPrefix.empty())
+      Cfg.Style.GetterPrefix = GetterPrefix.str();
+
+    for (auto &KV : Options)
+      Cfg.Diagnostics.ClangTidy.CheckOptions.insert_or_assign(
+          ("readability-identifier-naming." + KV.getKey()).str(),
+          KV.getValue());
+
+    WithContextValue WithCfg(Config::Key, std::move(Cfg));
+    EXPECT_EQ(apply(Input.str()), Expected.str());
+  };
+
+  Header = R"cpp(
+    struct Foo {
+      char a;
+      char b;
+      char c;
+    };
+  )cpp";
+
+  // Comply with style configuration:
+  RunGetterTest("summon",
+                {
+                    {"PublicMemberPrefix", "m_"},
+                    {"PublicMemberSuffix", "_s"},
+                    {"PublicMethodCase", "CamelCase"},
+                },
+                "struct S{ int m_m^ember_s;};",
+                "struct S{ int SummonMember() const { return m_member_s; }\n"
+                "int m_member_s;};");
+
+  RunGetterTest("get",
+                {
+                    {"PrivateMemberPrefix", "_"},
+                    {"PrivateMemberSuffix", ""},
+                    {"PublicMethodCase", "camelBack"},
+                },
+                "class S { int ^_member; };",
+                "class S { int _member; public:\n"
+                "int getMember() const { return _member; }\n};");
+
+  // Member prefix and suffix comply with style precedence:
+  RunGetterTest(
+      "",
+      {
+          {"PrivateMemberPrefix", "pre_"},
+          {"PrivateMemberSuffix", "_post"},
+          {"MemberPrefix", "not_used"},
+          {"MemberSuffix", "not_used"},
+          {"MethodCase", "lower_case"},
+      },
+      "class S { public: S(); private: Foo *pre_foo^_post; };",
+      "class S { public: S(); Foo * foo() const { return pre_foo_post; }\n"
+      "private: Foo *pre_foo_post; };");
+
+  // Method case comply with style precedence:
+  RunGetterTest("get",
+                {
+                    {"ProtectedMemberPrefix", "pre_"},
+                    {"ProtectedMemberSuffix", "_post"},
+                    {"PublicMethodCase", "Leading_upper_snake_case"},
+                    {"MethodCase", "lower_case"},
+                },
+                "class S { public: Foo &Get_foo(); protected: Foo "
+                "&pre_foo_^post; };",
+                "unavailable");
+
+  // Don't comply to unrelated member suffix, then fallback to default getter
+  // prefix
+  RunGetterTest(
+      "",
+      {
+          {"ProtectedMemberSuffix", "_post"},
+      },
+      "class S { public: S(); private: Foo foo^_post; };",
+      "class S { public: S(); Foo getFoo_post() const { return foo_post; }\n"
+      "private: Foo foo_post; };");
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
diff --git a/clang-tools-extra/clangd/unittests/tweaks/GenerateSetterTests.cpp 
b/clang-tools-extra/clangd/unittests/tweaks/GenerateSetterTests.cpp
new file mode 100644
index 0000000000000..1f3b72db33f3b
--- /dev/null
+++ b/clang-tools-extra/clangd/unittests/tweaks/GenerateSetterTests.cpp
@@ -0,0 +1,162 @@
+//===-- GenerateSetterTests.cpp 
-------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Config.h"
+#include "TweakTesting.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace clangd {
+namespace {
+
+TWEAK_TEST(GenerateSetter);
+
+TEST_F(GenerateSetterTest, Availability) {
+  // available on class member:
+  EXPECT_AVAILABLE("struct S { int ^x, y; };");
+  EXPECT_AVAILABLE("class S { private: int ^x; };");
+  EXPECT_AVAILABLE("class S { protected: int ^x; };");
+  EXPECT_AVAILABLE("union S { int ^x; };");
+  EXPECT_AVAILABLE("struct S { int ^x = 0; };");
+  // available on forward member:
+  EXPECT_AVAILABLE("/*error-ok*/class Forward; class A { Forward ^f; };");
+  // available on pointer type:
+  EXPECT_AVAILABLE("class Forward; class A { Forward *^f; };");
+  // available on reference type:
+  EXPECT_AVAILABLE("class A { int &^f; };");
+
+  // unavailable outside class member:
+  EXPECT_UNAVAILABLE("^struct ^S ^{ int f^oo(); ^int x, y; };");
+  // unavailable if method already exists:
+  EXPECT_UNAVAILABLE("struct S { int setX(); int ^x, y; };");
+  // unavailable on constant type:
+  EXPECT_UNAVAILABLE("class S { const int ^x, y; };");
+  // unavailable on static member:
+  EXPECT_UNAVAILABLE("struct S { static int ^x; };");
+}
+
+TEST_F(GenerateSetterTest, Edits) {
+  auto RunSetterTest = [&](llvm::StringRef SetterPrefix,
+                           llvm::StringMap<std::string> Options,
+                           llvm::StringRef Input, llvm::StringRef Expected) {
+    Config Cfg;
+    if (!SetterPrefix.empty())
+      Cfg.Style.SetterPrefix = SetterPrefix.str();
+
+    for (auto &KV : Options)
+      Cfg.Diagnostics.ClangTidy.CheckOptions.insert_or_assign(
+          ("readability-identifier-naming." + KV.getKey()).str(),
+          KV.getValue());
+
+    WithContextValue WithCfg(Config::Key, std::move(Cfg));
+    EXPECT_EQ(apply(Input.str()), Expected.str());
+  };
+
+  Header = R"cpp(
+    struct Foo {
+      char a;
+      char b;
+      char c;
+    };
+    struct Bigfoo {
+      long a;
+      long b;
+      long c;
+      long d;
+    };
+  )cpp";
+
+  // Comply with style configuration:
+  RunSetterTest(
+      "put",
+      {
+          {"PublicMemberPrefix", "m_"},
+          {"PublicMemberSuffix", "_s"},
+          {"PublicMethodCase", "CamelCase"},
+      },
+      "struct S{ int m_m^ember_s;};",
+      "struct S{ void PutMember(int member) { m_member_s = member; }\n"
+      "int m_member_s;};");
+
+  RunSetterTest("set",
+                {
+                    {"PrivateMemberPrefix", "_"},
+                    {"PrivateMemberSuffix", ""},
+                    {"PublicMethodCase", "camelBack"},
+                },
+                "class S { int ^_member; };",
+                "class S { int _member; public:\n"
+                "void setMember(int member) { _member = member; }\n};");
+
+  // Use const-ref on non trivially copiable member:
+  RunSetterTest(
+      "",
+      {
+          {"PrivateMemberPrefix", "m_"},
+          {"PrivateMemberSuffix", ""},
+          {"PublicMethodCase", "camelBack"},
+      },
+      "class S { Bigfoo ^m_bigfoo; };",
+      "class S { Bigfoo m_bigfoo; public:\n"
+      "void setBigfoo(const Bigfoo &bigfoo) { m_bigfoo = bigfoo; }\n};");
+
+  // Use const-ref on non trivially copiable reference member (don't duplicate
+  // the reference):
+  RunSetterTest(
+      "",
+      {
+          {"PrivateMemberPrefix", "m_"},
+          {"PrivateMemberSuffix", ""},
+          {"PublicMethodCase", "camelBack"},
+      },
+      "class S { Bigfoo &^m_bigfoo; };",
+      "class S { Bigfoo &m_bigfoo; public:\n"
+      "void setBigfoo(const Bigfoo &bigfoo) { m_bigfoo = bigfoo; }\n};");
+
+  // Member prefix and suffix comply with style precedence:
+  RunSetterTest("",
+                {
+                    {"PrivateMemberPrefix", "pre_"},
+                    {"PrivateMemberSuffix", "_post"},
+                    {"MemberPrefix", "not_used"},
+                    {"MemberSuffix", "not_used"},
+                    {"MethodCase", "lower_case"},
+                },
+                "class S { public: S(); private: Foo *pre_foo^_post; };",
+                "class S { public: S(); void set_foo(Foo * foo) { "
+                "pre_foo_post = foo; }\n"
+                "private: Foo *pre_foo_post; };");
+
+  // Method case comply with style precedence:
+  RunSetterTest(
+      "set",
+      {
+          {"ProtectedMemberPrefix", "pre_"},
+          {"ProtectedMemberSuffix", "_post"},
+          {"PublicMethodCase", "Leading_upper_snake_case"},
+          {"MethodCase", "lower_case"},
+      },
+      "class S { public: void Set_foo(const Foo &foo); protected: Foo "
+      "&pre_foo_^post; };",
+      "unavailable");
+
+  // Don't comply to unrelated member suffix, then fallback to default setter
+  // prefix
+  RunSetterTest("",
+                {
+                    {"ProtectedMemberSuffix", "_post"},
+                },
+                "class S { public: S(); private: Foo foo^_post; };",
+                "class S { public: S(); void setFoo_post(Foo newfoo_post) { "
+                "foo_post = newfoo_post; }\n"
+                "private: Foo foo_post; };");
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
diff --git 
a/llvm/utils/gn/secondary/clang-tools-extra/clangd/refactor/tweaks/BUILD.gn 
b/llvm/utils/gn/secondary/clang-tools-extra/clangd/refactor/tweaks/BUILD.gn
index defa12c240deb..35647f9648552 100644
--- a/llvm/utils/gn/secondary/clang-tools-extra/clangd/refactor/tweaks/BUILD.gn
+++ b/llvm/utils/gn/secondary/clang-tools-extra/clangd/refactor/tweaks/BUILD.gn
@@ -27,6 +27,7 @@ source_set("tweaks") {
     "ExpandMacro.cpp",
     "ExtractFunction.cpp",
     "ExtractVariable.cpp",
+    "GenerateGetterTests.cpp"
     "MemberwiseConstructor.cpp",
     "ObjCLocalizeStringLiteral.cpp",
     "ObjCMemberwiseInitializer.cpp",
diff --git 
a/llvm/utils/gn/secondary/clang-tools-extra/clangd/unittests/BUILD.gn 
b/llvm/utils/gn/secondary/clang-tools-extra/clangd/unittests/BUILD.gn
index 8aba04a4fc47d..332fbe9f86744 100644
--- a/llvm/utils/gn/secondary/clang-tools-extra/clangd/unittests/BUILD.gn
+++ b/llvm/utils/gn/secondary/clang-tools-extra/clangd/unittests/BUILD.gn
@@ -142,6 +142,7 @@ unittest("ClangdTests") {
     "tweaks/ExpandMacroTests.cpp",
     "tweaks/ExtractFunctionTests.cpp",
     "tweaks/ExtractVariableTests.cpp",
+    "tweaks/GenerateGetterTests.cpp"
     "tweaks/MemberwiseConstructorTests.cpp",
     "tweaks/ObjCLocalizeStringLiteralTests.cpp",
     "tweaks/ObjCMemberwiseInitializerTests.cpp",

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to