-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[clang-refactor] Add Matcher Edit refactoring rule #123782
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[clang-refactor] Add Matcher Edit refactoring rule #123782
Conversation
Added source location requirement for refactoring engine
Added source location argument for clang-refactor cli
Added matcher requirement, edit generator requirement, and edit match rule
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-clang Author: Сергеев Игнатий (IgnatSergeev) ChangesModified capabilities of refactoring engine and clang-refactor tool:
Patch is 22.38 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/123782.diff 9 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
index e060fffc7280a7..9eacb76cd9dadc 100644
--- a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
+++ b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
@@ -28,6 +28,12 @@ def err_refactor_extract_simple_expression : Error<"the selected expression "
def err_refactor_extract_prohibited_expression : Error<"the selected "
"expression cannot be extracted">;
+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_invalid_edit_generator : Error<"refactoring action can't be initiated "
+ "without a correct edit generator">;
}
} // 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..46e5aa54c0bee5
--- /dev/null
+++ b/clang/include/clang/Tooling/Refactoring/Edit/EditMatchRule.h
@@ -0,0 +1,49 @@
+//===--- 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
+/// EditGenerator.
+class EditMatchRule final : public SourceChangeRefactoringRule {
+public:
+ /// Initiates the delete match refactoring operation.
+ ///
+ /// \param R MatchResult Match result to edit.
+ /// \param EG EditGenerator Edit to perform.
+ static Expected<EditMatchRule>
+ initiate(RefactoringRuleContext &Context,
+ ast_matchers::MatchFinder::MatchResult R,
+ transformer::EditGenerator EG);
+
+ static const RefactoringDescriptor &describe();
+
+private:
+ EditMatchRule(ast_matchers::MatchFinder::MatchResult R,
+ transformer::EditGenerator EG)
+ : Result(std::move(R)), EditGenerator(std::move(EG)) {}
+
+ Expected<AtomicChanges>
+ createSourceReplacements(RefactoringRuleContext &Context) override;
+
+ ast_matchers::MatchFinder::MatchResult Result;
+ transformer::EditGenerator EditGenerator;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTORING_EDIT_EDITMATCHRULE_H
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..4ad7fbbef31620 100644
--- a/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
+++ b/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
@@ -9,13 +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 {
@@ -77,6 +82,101 @@ 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);
+ }
+};
+
+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)).bind(transformer::RootID);
+ }
+
+ ast_matchers::DeclarationMatcher
+ createWrapperMatcher(FullSourceLoc L,
+ ast_matchers::DeclarationMatcher M) const {
+ return ast_matchers::decl(M, hasPointWithin(L)).bind(transformer::RootID);
+ }
+
+ MatcherType Matcher;
+};
+
+/// Requirement that evaluates to the EditGenerator value given at its creation.
+class EditGeneratorRequirement : public RefactoringActionRuleRequirement {
+public:
+ Expected<transformer::EditGenerator>
+ evaluate(RefactoringRuleContext &Context) const {
+ return EditGenerator;
+ }
+
+ EditGeneratorRequirement(transformer::EditGenerator EG)
+ : EditGenerator(std::move(EG)) {}
+
+private:
+ transformer::EditGenerator EditGenerator;
+};
+
/// 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;
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..55c94b39237778
--- /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/Refactoring/AtomicChange.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,
+ transformer::EditGenerator EG) {
+ return EditMatchRule(std::move(R), std::move(EG));
+}
+
+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<SmallVector<transformer::Edit, 1>> Edits = EditGenerator(Result);
+
+ if (!Edits) {
+ return std::move(Edits.takeError());
+ }
+ if (Edits->empty())
+ return Context.createDiagnosticError(
+ diag::err_refactor_invalid_edit_generator);
+
+ AtomicChange Change(SM, Edits->front().Range.getBegin());
+ {
+ for (const auto &Edit : *Edits) {
+ switch (Edit.Kind) {
+ case EditKind::Range:
+ if (auto Err = Change.replace(SM, std::move(Edit.Range),
+ std::move(Edit.Replacement))) {
+ return std::move(Err);
+ }
+ break;
+ case EditKind::AddInclude:
+ Change.addHeader(std::move(Edit.Replacement));
+ break;
+ }
+ }
+ }
+
+ return AtomicChanges{std::move(Change)};
+}
diff --git a/clang/tools/clang-refactor/ClangRefactor.cpp b/clang/tools/clang-refactor/ClangRefactor.cpp
index 968f0594085d40..937e605a3dafbb 100644
--- a/clang/tools/clang-refactor/ClangRefactor.cpp
+++ b/clang/tools/clang-refactor/ClangRefactor.cpp
@@ -164,6 +164,85 @@ SourceSelectionArgument::fromString(StringRef Value) {
return nullptr;
}
+/// Stores the parsed `-location` argument.
+class AbstractSourceLocationArgument {
+public:
+ virtual ~AbstractSourceLocationArgument() {}
+
+ /// Parse the `-location` argument.
+ ///
+ /// \returns A valid argument when the parse succedeed, null otherwise.
+ static std::unique_ptr<AbstractSourceLocationArgument>
+ 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 SourceLocationArgument final : public AbstractSourceLocationArgument {
+public:
+ SourceLocationArgument(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<AbstractSourceLocationArgument>
+AbstractSourceLocationArgument::fromString(StringRef Value) {
+ ParsedSourceLocation Location = ParsedSourceLocation::FromString(Value);
+ if (Location.FileName != "")
+ return std::make_unique<SourceLocationArgument>(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 +351,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 +386,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 = AbstractSourceLocationArgument::fromString(*Location);
+ if (!ParsedLocation)
+ return true;
+ }
+ return false;
+ }
+
SourceSelectionArgument *getSelection() const {
assert(Selection && "selection not supported!");
return ParsedSelection.get();
}
+ AbstractSourceLocationArgument *getLocation() const {
+ assert(Location && "location not supported!");
+ return ParsedLocation.get();
+ }
+
const RefactoringActionCommandLineOptions &getOptions() const {
return Options;
}
@@ -309,7 +416,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<AbstractSourceLocationArgument> ParsedLocation;
RefactoringActionCommandLineOptions Options;
};
@@ -399,6 +508,7 @@ class ClangRefactorTool {
// consumer.
std::unique_ptr<ClangRefactorToolConsumerInterface> TestConsumer;
bool HasSelection = MatchingRule->hasSelectionRequirement();
+ bool HasLocation = MatchingRule->hasLocationRequirement();
if (HasSelection)
TestConsumer = Selected...
[truncated]
|
@IgnatSergeev, thanks for posting this PR! First question, where is this new code tested? Do you know the status of automated testing for this LLVM refactoring support software and the According to: unit, integration and regression tests can be added under I don't see any existing tests for the
But do see some hits under
There is no mention at all about tests under Looking at the GitHub Actions defined for this GitHub repo at: and it is not clear which of these GHAs run run actual tests. (Looks like most of those GHAs are just doing some PR automation and not really testing anything.) |
Speaking about testing infrastructure, as you pointed out clang-refactor has tests under clang/test (and actually clang/unittests), tests for clang and tools that are based on it (including clang-refactor) are separated from llvm tests. They could be run with these cmake targets: check-clang and check-clang-unit (or you could trigger both llvm and clang tests with check-all target). Speaking about test's content, as much as I understand, clang-refactor tool has tests for:
It has some unit tests, but they mostly test ability to create Refactoring Rules, rather than test existing ones (clang/unittests/Tooling/RefactoringActionRulesTest.cpp) CLI options are tested through Refactoring Actions, that use them, with requirement of specific test support in the tool itself (clang/tools/clang-refactor/TestSupport.cpp). In current state, this PR adds new CLI option and Refactoring Rules, but has no new Refactoring Actions (so there is no way to test anything). But as I mentioned in closed PR, I have some dummy Refactoring Actions, that are based on new Refactoring Rules, with tests covering them, and test support for new CLI option. I could add every piece of code related to those Refactoring Actions and tests here, and later remove them if needed. P. S. I'm using some Refactoring Engine terms here, if you are not sure, I recommend reading these docs https://clang.llvm.org/docs/RefactoringEngine.html |
According to PR workflows, llvm uses external CI platform (buildkite). |
@IgnatSergeev, thanks for the detailed information on the clang-refactor testing infrastructure. We will try to run these tests and dig in deeper to see how the testing system works.
No need for unnecessary work. We just appreciate your willingness to help answer our questions 😊 I have been looking for other PRs that extend clang-refactor, but it is hard to find them. There is not even a GitHub Issue label
Thanks! We have been looking that over. Do you know why so few refactorings are supported in the It seems that LLMV/Clang LibTool could support an endless set of refactorings (due to arbitrary AST changes), but there seems to be very limited user-ready refactorings supported by tools in the llvm-project repo itself. I know that major companies are using LLVM/Clang LibTool to do large refctorings. Are these companies mostly just developing their own proprietary in-house refactoring tools based on LLVM/Clang LibTool? |
@bartlettroscoe, sorry for not responding, at some point, i forgot checking my github notifications You were right about the amount of refactorings that LibTool could support So i would recommend reading about them, and searching through their code, maybe you could find some of the refactorings already implemented for those tools |
@IgnatSergeev, thanks for the reply!
So is this really transforming the AST? According to: it says:
Just an FYI, we have found that the Apple fork of the llvm-project repo appears to have some additional C++ refactoring tooling that is used by the XCode IDE: For example, there appears to be an "Extract Function" refactor that might actually be able to refactor real C++ code the exectuable: We have not done an in-depth evaluation of this capability yet, but it looks promising.
Are people using clang-transformer for more than just clang-tidy "FixIt" checks? I have done some initial searches and I can't seem to find anything other than just clang-tidy FixIts. The question is how his does this clang transformer mapping of AST to source code work for larger chucks of C++ code, like for "Extract Function"? FYI: We are starting to look at the LLNL ROSE Outliner tool: which is supposed to be able to implement a robust C++ "Extract Function" refactoring. Or, perhaps everyone is waiting for better AI to be able to refactor C++ automatically? |
Well yeah, you are right, it replaces it with text, but what i ment, is that it supports replacements with EditGenerators, which are really expressive (a few words about them https://clang.llvm.org/docs/ClangTransformerTutorial.html#editgenerators-advanced) But i would say that even clang-refactor uses source code transformations, it doesn't change AST
The link you provided is to a test tool, but i see how you can adapt this to some tool And after a brief look of that file, i think it uses source code transformations too RenamingAction is based on Refactoring library (the same that clang-refactor uses) and it uses the same source code transformations (https://github.com/swiftlang/llvm-project/blob/next/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp) RefactoringOperation (https://github.com/swiftlang/llvm-project/blob/next/clang/include/clang/Tooling/Refactor/RefactoringOperation.h) uses RefactoringResult that also uses source code transformations through RefactoringReplacement (https://github.com/swiftlang/llvm-project/blob/next/clang/include/clang/Tooling/Refactor/RefactoringReplacement.h#L37) I hope it makes sense
At first glance could be what you want
Well, you could write a matcher that matches if current node is in specific source range That just added extracted function But this tool is mostly used for local transformations, as there are much more of those, and they are commonly met
Actually, that's an interesting idea But to be honest, i think that there are't many "users" for large refactoring tools, so when they are needed people write small portion of their own transformations, thus they don't get to open source |
Hello @IgnatSergeev, thanks for your response! I am looping in my colleague @achauphan who has been working with the LLVM Clang refactoring tooling ...
I just find that so surprising. It is so hard to even safely change the name of an overloaded function across a large C++ code base. And performing Extract Function can be very tedious to do it correctly with minimal inputs/outputs, dealing with multiple returns, etc. Is this a chicken and the egg problem? I mean, do they not use the refactoring tools because the refactoring tools don't exist or are not reliable so they don't use them? Or is it because they don't do refactoring as a regular practice? I asked ChatGPT 4o the question:
and it provided the following summary response (see here):
All it would take is one or two bad refactors from a refactoring tool and I think many people will just give up. I have never had access to a working refactoring tool for C++ so I have zero experience with such tooling. |
Modified capabilities of refactoring engine and clang-refactor tool: