[clang] Match edit refactoring rule (PR #105717)
Сергеев Игнатий via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 22 11:59:56 PDT 2024
https://github.com/IgnatSergeev created https://github.com/llvm/llvm-project/pull/105717
None
>From d631dae8cf543b8d02b7d2702d69b143a2874cd7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=98=D0=B3=D0=BD=D0=B0=D1=82=20=D0=A1=D0=B5=D1=80=D0=B3?=
=?UTF-8?q?=D0=B5=D0=B5=D0=B2?= <ignat.sergeev at softcom.su>
Date: Mon, 27 May 2024 19:43:11 +0000
Subject: [PATCH 1/3] Add source location requirement
Added source location requirement for refactoring engine
---
.../clang/Basic/DiagnosticRefactoringKinds.td | 2 ++
.../Tooling/Refactoring/RefactoringActionRule.h | 4 ++++
.../Refactoring/RefactoringActionRuleRequirements.h | 12 ++++++++++++
.../Refactoring/RefactoringActionRulesInternal.h | 5 +++++
.../Tooling/Refactoring/RefactoringRuleContext.h | 12 ++++++++++++
5 files changed, 35 insertions(+)
diff --git a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
index 5446b32efbdd47..da35eae93df9a9 100644
--- a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
+++ b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
@@ -28,6 +28,8 @@ def err_refactor_extract_simple_expression : Error<"the selected expression "
def err_refactor_extract_prohibited_expression : Error<"the selected "
"expression can't be extracted">;
+def err_refactor_no_location : Error<"refactoring action can't be initiated "
+ "without a location">;
}
} // end of Refactoring diagnostics
diff --git a/clang/include/clang/Tooling/Refactoring/RefactoringActionRule.h b/clang/include/clang/Tooling/Refactoring/RefactoringActionRule.h
index c6a6c4f6093a34..374f19d6d82338 100644
--- a/clang/include/clang/Tooling/Refactoring/RefactoringActionRule.h
+++ b/clang/include/clang/Tooling/Refactoring/RefactoringActionRule.h
@@ -56,6 +56,10 @@ class RefactoringActionRule : public RefactoringActionRuleBase {
/// to be fulfilled before refactoring can be performed.
virtual bool hasSelectionRequirement() = 0;
+ /// Returns true when the rule has a source location requirement that has
+ /// to be fulfilled before refactoring can be performed.
+ virtual bool hasLocationRequirement() = 0;
+
/// Traverses each refactoring option used by the rule and invokes the
/// \c visit callback in the consumer for each option.
///
diff --git a/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h b/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
index 1a318da3acca19..bdd2d98df73eb1 100644
--- a/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
+++ b/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
@@ -10,6 +10,7 @@
#define LLVM_CLANG_TOOLING_REFACTORING_REFACTORINGACTIONRULEREQUIREMENTS_H
#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Tooling/Refactoring/ASTSelection.h"
#include "clang/Tooling/Refactoring/RefactoringDiagnostic.h"
#include "clang/Tooling/Refactoring/RefactoringOption.h"
@@ -77,6 +78,17 @@ class CodeRangeASTSelectionRequirement : public ASTSelectionRequirement {
evaluate(RefactoringRuleContext &Context) const;
};
+/// A base class for any requirement that expects source code position (or the
+/// refactoring tool with the -location option).
+class SourceLocationRequirement : public RefactoringActionRuleRequirement {
+public:
+ Expected<SourceLocation> evaluate(RefactoringRuleContext &Context) const {
+ if (Context.getLocation().isValid())
+ return Context.getLocation();
+ return Context.createDiagnosticError(diag::err_refactor_no_location);
+ }
+};
+
/// A base class for any requirement that requires some refactoring options.
class RefactoringOptionsRequirement : public RefactoringActionRuleRequirement {
public:
diff --git a/clang/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h b/clang/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h
index 33194c401ea143..52afb012f4874c 100644
--- a/clang/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h
+++ b/clang/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h
@@ -139,6 +139,11 @@ createRefactoringActionRule(const RequirementTypes &... Requirements) {
RequirementTypes...>::value;
}
+ bool hasLocationRequirement() override {
+ return internal::HasBaseOf<SourceLocationRequirement,
+ RequirementTypes...>::value;
+ }
+
void visitRefactoringOptions(RefactoringOptionVisitor &Visitor) override {
internal::visitRefactoringOptions(
Visitor, Requirements,
diff --git a/clang/include/clang/Tooling/Refactoring/RefactoringRuleContext.h b/clang/include/clang/Tooling/Refactoring/RefactoringRuleContext.h
index 7d97f811f024e0..85bba662afcd28 100644
--- a/clang/include/clang/Tooling/Refactoring/RefactoringRuleContext.h
+++ b/clang/include/clang/Tooling/Refactoring/RefactoringRuleContext.h
@@ -30,6 +30,9 @@ namespace tooling {
///
/// - SelectionRange: an optional source selection ranges that can be used
/// to represent a selection in an editor.
+///
+/// - Location: an optional source location that can be used
+/// to represent a cursor in an editor.
class RefactoringRuleContext {
public:
RefactoringRuleContext(const SourceManager &SM) : SM(SM) {}
@@ -40,8 +43,14 @@ class RefactoringRuleContext {
/// refactoring engine. Can be invalid.
SourceRange getSelectionRange() const { return SelectionRange; }
+ /// Returns the current source location as set by the
+ /// refactoring engine. Can be invalid.
+ SourceLocation getLocation() const { return Location; }
+
void setSelectionRange(SourceRange R) { SelectionRange = R; }
+ void setLocation(SourceLocation L) { Location = L; }
+
bool hasASTContext() const { return AST; }
ASTContext &getASTContext() const {
@@ -73,6 +82,9 @@ class RefactoringRuleContext {
/// An optional source selection range that's commonly used to represent
/// a selection in an editor.
SourceRange SelectionRange;
+ /// An optional source location that's commonly used to represent
+ /// a cursor in an editor.
+ SourceLocation Location;
/// An optional AST for the translation unit on which a refactoring action
/// might operate on.
ASTContext *AST = nullptr;
>From 333b349c7b335c625745f1613b7dc05cc93718cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=98=D0=B3=D0=BD=D0=B0=D1=82=20=D0=A1=D0=B5=D1=80=D0=B3?=
=?UTF-8?q?=D0=B5=D0=B5=D0=B2?= <ignat.sergeev at softcom.su>
Date: Mon, 27 May 2024 20:26:52 +0000
Subject: [PATCH 2/3] Add source location argument
Added source location argument for clang-refactor cli
---
clang/tools/clang-refactor/ClangRefactor.cpp | 148 ++++++++++++++++++-
1 file changed, 142 insertions(+), 6 deletions(-)
diff --git a/clang/tools/clang-refactor/ClangRefactor.cpp b/clang/tools/clang-refactor/ClangRefactor.cpp
index 175a2b8234e9ac..1737ca9fc82a91 100644
--- a/clang/tools/clang-refactor/ClangRefactor.cpp
+++ b/clang/tools/clang-refactor/ClangRefactor.cpp
@@ -164,6 +164,84 @@ SourceSelectionArgument::fromString(StringRef Value) {
return nullptr;
}
+/// Stores the parsed `-location` argument.
+class SourceLocationArgument {
+public:
+ virtual ~SourceLocationArgument() {}
+
+ /// Parse the `-location` argument.
+ ///
+ /// \returns A valid argument when the parse succedeed, null otherwise.
+ static std::unique_ptr<SourceLocationArgument> fromString(StringRef Value);
+
+ /// Prints any additional state associated with the location argument to
+ /// the given output stream.
+ virtual void print(raw_ostream &OS) {}
+
+ /// Returns a replacement refactoring result consumer (if any) that should
+ /// consume the results of a refactoring operation.
+ ///
+ /// The replacement refactoring result consumer is used by \c
+ /// TestSourceLocationArgument to inject a test-specific result handling
+ /// logic into the refactoring operation. The test-specific consumer
+ /// ensures that the individual results in a particular test group are
+ /// identical.
+ virtual std::unique_ptr<ClangRefactorToolConsumerInterface>
+ createCustomConsumer() {
+ return nullptr;
+ }
+
+ /// Runs the given refactoring function for each specified location.
+ ///
+ /// \returns true if an error occurred, false otherwise.
+ virtual bool
+ forAllLocations(const SourceManager &SM,
+ llvm::function_ref<void(SourceLocation L)> Callback) = 0;
+};
+
+/// Stores the parsed -location=filename:line:column option.
+class SourceLocLocationArgument final : public SourceLocationArgument {
+public:
+ SourceLocLocationArgument(ParsedSourceLocation Location)
+ : Location(std::move(Location)) {}
+
+ bool forAllLocations(
+ const SourceManager &SM,
+ llvm::function_ref<void(SourceLocation L)> Callback) override {
+ auto FE = SM.getFileManager().getFile(Location.FileName);
+ FileID FID = FE ? SM.translateFile(*FE) : FileID();
+ if (!FE || FID.isInvalid()) {
+ llvm::errs() << "error: -location=" << Location.FileName
+ << ":... : given file is not in the target TU\n";
+ return true;
+ }
+
+ SourceLocation Loc = SM.getMacroArgExpandedLocation(
+ SM.translateLineCol(FID, Location.Line, Location.Column));
+ if (Loc.isInvalid()) {
+ llvm::errs() << "error: -location=" << Location.FileName << ':'
+ << Location.Line << ':' << Location.Column
+ << " : invalid source location\n";
+ return true;
+ }
+ Callback(Loc);
+ return false;
+ }
+
+private:
+ ParsedSourceLocation Location;
+};
+
+std::unique_ptr<SourceLocationArgument>
+SourceLocationArgument::fromString(StringRef Value) {
+ ParsedSourceLocation Location = ParsedSourceLocation::FromString(Value);
+ if (Location.FileName != "")
+ return std::make_unique<SourceLocLocationArgument>(std::move(Location));
+ llvm::errs() << "error: '-location' option must be specified using "
+ "<file>:<line>:<column>\n";
+ return nullptr;
+}
+
/// A container that stores the command-line options used by a single
/// refactoring option.
class RefactoringActionCommandLineOptions {
@@ -272,6 +350,17 @@ class RefactoringActionSubcommand : public cl::SubCommand {
break;
}
}
+ // Check if the location option is supported.
+ for (const auto &Rule : this->ActionRules) {
+ if (Rule->hasLocationRequirement()) {
+ Location = std::make_unique<cl::opt<std::string>>(
+ "location",
+ cl::desc("Location where refactoring should "
+ "be initiated( <file>:<line>:<column>)"),
+ cl::cat(Category), cl::sub(*this));
+ break;
+ }
+ }
// Create the refactoring options.
for (const auto &Rule : this->ActionRules) {
CommandLineRefactoringOptionCreator OptionCreator(Category, *this,
@@ -296,11 +385,28 @@ class RefactoringActionSubcommand : public cl::SubCommand {
return false;
}
+ /// Parses the "-location" command-line argument.
+ ///
+ /// \returns true on error, false otherwise.
+ bool parseLocationArgument() {
+ if (Location) {
+ ParsedLocation = SourceLocationArgument::fromString(*Location);
+ if (!ParsedLocation)
+ return true;
+ }
+ return false;
+ }
+
SourceSelectionArgument *getSelection() const {
assert(Selection && "selection not supported!");
return ParsedSelection.get();
}
+ SourceLocationArgument *getLocation() const {
+ assert(Location && "location not supported!");
+ return ParsedLocation.get();
+ }
+
const RefactoringActionCommandLineOptions &getOptions() const {
return Options;
}
@@ -309,7 +415,9 @@ class RefactoringActionSubcommand : public cl::SubCommand {
std::unique_ptr<RefactoringAction> Action;
RefactoringActionRules ActionRules;
std::unique_ptr<cl::opt<std::string>> Selection;
+ std::unique_ptr<cl::opt<std::string>> Location;
std::unique_ptr<SourceSelectionArgument> ParsedSelection;
+ std::unique_ptr<SourceLocationArgument> ParsedLocation;
RefactoringActionCommandLineOptions Options;
};
@@ -399,6 +507,7 @@ class ClangRefactorTool {
// consumer.
std::unique_ptr<ClangRefactorToolConsumerInterface> TestConsumer;
bool HasSelection = MatchingRule->hasSelectionRequirement();
+ bool HasLocation = MatchingRule->hasLocationRequirement();
if (HasSelection)
TestConsumer = SelectedSubcommand->getSelection()->createCustomConsumer();
ClangRefactorToolConsumerInterface *ActiveConsumer =
@@ -424,6 +533,19 @@ class ClangRefactorTool {
ActiveConsumer->endTU();
return;
}
+ if (HasLocation) {
+ assert(SelectedSubcommand->getLocation() && "Missing location argument?");
+ if (opts::Verbose)
+ SelectedSubcommand->getLocation()->print(llvm::outs());
+ if (SelectedSubcommand->getLocation()->forAllLocations(
+ Context.getSources(), [&](SourceLocation L) {
+ Context.setLocation(L);
+ InvokeRule(*ActiveConsumer);
+ }))
+ HasFailed = true;
+ ActiveConsumer->endTU();
+ return;
+ }
InvokeRule(*ActiveConsumer);
ActiveConsumer->endTU();
}
@@ -528,6 +650,12 @@ class ClangRefactorTool {
R.getEnd().print(llvm::outs(), Context.getSources());
llvm::outs() << "\n";
}
+ if (Context.getLocation().isValid()) {
+ SourceLocation L = Context.getLocation();
+ llvm::outs() << " -location=";
+ L.print(llvm::outs(), Context.getSources());
+ llvm::outs() << "\n";
+ }
}
llvm::Expected<RefactoringActionRule *>
@@ -539,16 +667,24 @@ class ClangRefactorTool {
CommandLineRefactoringOptionVisitor Visitor(Subcommand.getOptions());
Rule->visitRefactoringOptions(Visitor);
if (Visitor.getMissingRequiredOptions().empty()) {
- if (!Rule->hasSelectionRequirement()) {
- MatchingRules.push_back(Rule.get());
- } else {
+ bool HasMissingOptions = false;
+ if (Rule->hasSelectionRequirement()) {
Subcommand.parseSelectionArgument();
- if (Subcommand.getSelection()) {
- MatchingRules.push_back(Rule.get());
- } else {
+ if (!Subcommand.getSelection()) {
MissingOptions.insert("selection");
+ HasMissingOptions = true;
}
}
+ if (Rule->hasLocationRequirement()) {
+ Subcommand.parseLocationArgument();
+ if (!Subcommand.getLocation()) {
+ MissingOptions.insert("location");
+ HasMissingOptions = true;
+ }
+ }
+ if (!HasMissingOptions) {
+ MatchingRules.push_back(Rule.get());
+ }
}
for (const RefactoringOption *Opt : Visitor.getMissingRequiredOptions())
MissingOptions.insert(Opt->getName());
>From 6b7a480cf4d8c75cac99ba400a030761767f8f39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=98=D0=B3=D0=BD=D0=B0=D1=82=20=D0=A1=D0=B5=D1=80=D0=B3?=
=?UTF-8?q?=D0=B5=D0=B5=D0=B2?= <ignat.sergeev at softcom.su>
Date: Thu, 22 Aug 2024 11:13:56 +0000
Subject: [PATCH 3/3] Add edit matcher rule
Added matcher requirement, edit requirement, and edit match rule
---
.../clang/Basic/DiagnosticRefactoringKinds.td | 4 +
.../Tooling/Refactoring/Edit/EditMatchRule.h | 48 ++++++++++
.../RefactoringActionRuleRequirements.h | 89 ++++++++++++++++++-
clang/lib/Tooling/Refactoring/CMakeLists.txt | 2 +
.../Refactoring/Edit/EditMatchRule.cpp | 74 +++++++++++++++
5 files changed, 216 insertions(+), 1 deletion(-)
create mode 100644 clang/include/clang/Tooling/Refactoring/Edit/EditMatchRule.h
create mode 100644 clang/lib/Tooling/Refactoring/Edit/EditMatchRule.cpp
diff --git a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
index da35eae93df9a9..bb6af31dcffc81 100644
--- a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
+++ b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
@@ -30,6 +30,10 @@ def err_refactor_extract_prohibited_expression : Error<"the selected "
def err_refactor_no_location : Error<"refactoring action can't be initiated "
"without a location">;
+def err_refactor_no_location_match : Error<"refactoring action can't be initiated "
+ "without a matching location">;
+def err_refactor_no_ast_edit : Error<"refactoring action can't be initiated "
+ "without a correct ast edit">;
}
} // end of Refactoring diagnostics
diff --git a/clang/include/clang/Tooling/Refactoring/Edit/EditMatchRule.h b/clang/include/clang/Tooling/Refactoring/Edit/EditMatchRule.h
new file mode 100644
index 00000000000000..b102f770c31759
--- /dev/null
+++ b/clang/include/clang/Tooling/Refactoring/Edit/EditMatchRule.h
@@ -0,0 +1,48 @@
+//===--- EditMatchRule.h - Clang refactoring library ----------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTORING_EDIT_EDITMATCHRULE_H
+#define LLVM_CLANG_TOOLING_REFACTORING_EDIT_EDITMATCHRULE_H
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Refactoring/RefactoringActionRules.h"
+#include "clang/Tooling/Transformer/RewriteRule.h"
+
+namespace clang {
+namespace tooling {
+
+/// A "Edit Match" refactoring rule edits code around matches according to
+/// ASTEdit.
+class EditMatchRule final : public SourceChangeRefactoringRule {
+public:
+ /// Initiates the delete match refactoring operation.
+ ///
+ /// \param R MatchResult Match result to edit.
+ /// \param AE ASTEdit Edit to perform.
+ static Expected<EditMatchRule>
+ initiate(RefactoringRuleContext &Context,
+ ast_matchers::MatchFinder::MatchResult R, transformer::ASTEdit AE);
+
+ static const RefactoringDescriptor &describe();
+
+private:
+ EditMatchRule(ast_matchers::MatchFinder::MatchResult R,
+ transformer::ASTEdit AE)
+ : Result(std::move(R)), Edit(std::move(AE)) {}
+
+ Expected<AtomicChanges>
+ createSourceReplacements(RefactoringRuleContext &Context) override;
+
+ ast_matchers::MatchFinder::MatchResult Result;
+ transformer::ASTEdit Edit;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTORING_EDIT_EDITMATCHRULE_H
diff --git a/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h b/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
index bdd2d98df73eb1..ddbd4fdecea0fd 100644
--- a/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
+++ b/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
@@ -9,14 +9,18 @@
#ifndef LLVM_CLANG_TOOLING_REFACTORING_REFACTORINGACTIONRULEREQUIREMENTS_H
#define LLVM_CLANG_TOOLING_REFACTORING_REFACTORINGACTIONRULEREQUIREMENTS_H
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersMacros.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Tooling/Refactoring/ASTSelection.h"
#include "clang/Tooling/Refactoring/RefactoringDiagnostic.h"
#include "clang/Tooling/Refactoring/RefactoringOption.h"
#include "clang/Tooling/Refactoring/RefactoringRuleContext.h"
+#include "clang/Tooling/Transformer/RewriteRule.h"
#include "llvm/Support/Error.h"
-#include <type_traits>
namespace clang {
namespace tooling {
@@ -89,6 +93,89 @@ class SourceLocationRequirement : public RefactoringActionRuleRequirement {
}
};
+AST_POLYMORPHIC_MATCHER_P(hasPointWithin,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl),
+ FullSourceLoc, L) {
+ if (!L.hasManager()) {
+ return false;
+ }
+ const SourceRange &SR = Node.getSourceRange();
+ return L.getManager().isPointWithin(L, SR.getBegin(), SR.getEnd());
+}
+
+/// An AST location match is satisfied when there is match around given
+/// location. In case of several matches inner one is taken.
+///
+/// The requirement will be evaluated only once during the initiation and
+/// search of matching refactoring action rules.
+template <typename MatcherType>
+class ASTLocMatchRequirement : public SourceLocationRequirement {
+public:
+ static_assert(
+ std::is_same<ast_matchers::StatementMatcher, MatcherType>::value ||
+ std::is_same<ast_matchers::DeclarationMatcher, MatcherType>::value,
+ "Expected a Statement or Declaration matcher");
+
+ class LocMatchCallback : public ast_matchers::MatchFinder::MatchCallback {
+ public:
+ void run(const clang::ast_matchers::MatchFinder::MatchResult &R) override {
+ Result = std::make_unique<ast_matchers::MatchFinder::MatchResult>(R);
+ }
+ std::unique_ptr<ast_matchers::MatchFinder::MatchResult> Result;
+ };
+
+ Expected<ast_matchers::MatchFinder::MatchResult>
+ evaluate(RefactoringRuleContext &Context) const {
+ Expected<SourceLocation> Location =
+ SourceLocationRequirement::evaluate(Context);
+ if (!Location)
+ return Location.takeError();
+ MatcherType M = createWrapperMatcher(
+ FullSourceLoc(*Location, Context.getASTContext().getSourceManager()),
+ Matcher);
+
+ ast_matchers::MatchFinder MF;
+ LocMatchCallback Callback;
+ MF.addMatcher(M, &Callback);
+ MF.matchAST(Context.getASTContext());
+ if (!Callback.Result)
+ return Context.createDiagnosticError(
+ diag::err_refactor_no_location_match);
+ return *Callback.Result;
+ }
+
+ ASTLocMatchRequirement(MatcherType M) : Matcher(M) {}
+
+private:
+ ast_matchers::StatementMatcher
+ createWrapperMatcher(FullSourceLoc L,
+ ast_matchers::StatementMatcher M) const {
+ return ast_matchers::stmt(M, hasPointWithin(L));
+ }
+
+ ast_matchers::DeclarationMatcher
+ createWrapperMatcher(FullSourceLoc L,
+ ast_matchers::DeclarationMatcher M) const {
+ return ast_matchers::decl(M, hasPointWithin(L));
+ }
+
+ MatcherType Matcher;
+};
+
+/// Requirement that evaluates to the ASTEdit value given at its creation.
+class ASTEditRequirement : public RefactoringActionRuleRequirement {
+public:
+ Expected<transformer::ASTEdit>
+ evaluate(RefactoringRuleContext &Context) const {
+ return Edit;
+ }
+
+ ASTEditRequirement(transformer::ASTEdit AE) : Edit(std::move(AE)) {}
+
+private:
+ transformer::ASTEdit Edit;
+};
+
/// A base class for any requirement that requires some refactoring options.
class RefactoringOptionsRequirement : public RefactoringActionRuleRequirement {
public:
diff --git a/clang/lib/Tooling/Refactoring/CMakeLists.txt b/clang/lib/Tooling/Refactoring/CMakeLists.txt
index d3077be8810aad..97023b6d6b97bb 100644
--- a/clang/lib/Tooling/Refactoring/CMakeLists.txt
+++ b/clang/lib/Tooling/Refactoring/CMakeLists.txt
@@ -13,6 +13,7 @@ add_clang_library(clangToolingRefactoring
Rename/USRFinder.cpp
Rename/USRFindingAction.cpp
Rename/USRLocFinder.cpp
+ Edit/EditMatchRule.cpp
LINK_LIBS
clangAST
@@ -23,6 +24,7 @@ add_clang_library(clangToolingRefactoring
clangLex
clangRewrite
clangToolingCore
+ clangTransformer
DEPENDS
omp_gen
diff --git a/clang/lib/Tooling/Refactoring/Edit/EditMatchRule.cpp b/clang/lib/Tooling/Refactoring/Edit/EditMatchRule.cpp
new file mode 100644
index 00000000000000..8ea162d6dab90b
--- /dev/null
+++ b/clang/lib/Tooling/Refactoring/Edit/EditMatchRule.cpp
@@ -0,0 +1,74 @@
+//===--- EditMatchRule.cpp - Clang refactoring library --------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implements the "edit-match" refactoring rule that can edit matcher results
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactoring/Edit/EditMatchRule.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/DiagnosticRefactoring.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/Transformer/RewriteRule.h"
+#include "clang/Tooling/Transformer/SourceCode.h"
+
+using namespace clang;
+using namespace tooling;
+using namespace transformer;
+
+Expected<EditMatchRule>
+EditMatchRule::initiate(RefactoringRuleContext &Context,
+ ast_matchers::MatchFinder::MatchResult R, ASTEdit AE) {
+ return EditMatchRule(std::move(R), std::move(AE));
+}
+
+const RefactoringDescriptor &EditMatchRule::describe() {
+ static const RefactoringDescriptor Descriptor = {
+ "edit-match",
+ "Edit Match",
+ "Edits match result source code",
+ };
+ return Descriptor;
+}
+
+Expected<AtomicChanges>
+EditMatchRule::createSourceReplacements(RefactoringRuleContext &Context) {
+ ASTContext &AST = Context.getASTContext();
+ SourceManager &SM = AST.getSourceManager();
+
+ Expected<CharSourceRange> Range = Edit.TargetRange(Result);
+ if (!Range)
+ return std::move(Range.takeError());
+ std::optional<CharSourceRange> EditRange =
+ getFileRangeForEdit(std::move(*Range), AST);
+ if (!EditRange)
+ return Context.createDiagnosticError(diag::err_refactor_no_ast_edit);
+
+ AtomicChange Change(SM, EditRange->getBegin());
+ {
+ auto Replacement = Edit.Replacement->eval(Result);
+ if (!Replacement)
+ return std::move(Replacement.takeError());
+
+ switch (Edit.Kind) {
+ case EditKind::Range:
+ if (auto Err = Change.replace(SM, std::move(*EditRange),
+ std::move(*Replacement))) {
+ return std::move(Err);
+ }
+ break;
+ case EditKind::AddInclude:
+ Change.addHeader(std::move(*Replacement));
+ break;
+ }
+ }
+
+ return AtomicChanges{std::move(Change)};
+}
More information about the cfe-commits
mailing list