Skip to content

Commit 240cffa

Browse files
author
Игнат Сергеев
committed
Add edit match rule
Added matcher requirement, edit generator requirement, and edit match rule
1 parent aa769f0 commit 240cffa

File tree

5 files changed

+218
-1
lines changed

5 files changed

+218
-1
lines changed

clang/include/clang/Basic/DiagnosticRefactoringKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ def err_refactor_extract_prohibited_expression : Error<"the selected "
3030

3131
def err_refactor_no_location : Error<"refactoring action can't be initiated "
3232
"without a location">;
33+
def err_refactor_no_location_match : Error<"refactoring action can't be initiated "
34+
"without a matching location">;
35+
def err_refactor_invalid_edit_generator : Error<"refactoring action can't be initiated "
36+
"without a correct edit generator">;
3337
}
3438

3539
} // end of Refactoring diagnostics
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===--- EditMatchRule.h - Clang refactoring library ----------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLING_REFACTORING_EDIT_EDITMATCHRULE_H
10+
#define LLVM_CLANG_TOOLING_REFACTORING_EDIT_EDITMATCHRULE_H
11+
12+
#include "clang/ASTMatchers/ASTMatchFinder.h"
13+
#include "clang/Tooling/Refactoring/RefactoringActionRules.h"
14+
#include "clang/Tooling/Transformer/RewriteRule.h"
15+
16+
namespace clang {
17+
namespace tooling {
18+
19+
/// A "Edit Match" refactoring rule edits code around matches according to
20+
/// EditGenerator.
21+
class EditMatchRule final : public SourceChangeRefactoringRule {
22+
public:
23+
/// Initiates the delete match refactoring operation.
24+
///
25+
/// \param R MatchResult Match result to edit.
26+
/// \param EG EditGenerator Edit to perform.
27+
static Expected<EditMatchRule>
28+
initiate(RefactoringRuleContext &Context,
29+
ast_matchers::MatchFinder::MatchResult R,
30+
transformer::EditGenerator EG);
31+
32+
static const RefactoringDescriptor &describe();
33+
34+
private:
35+
EditMatchRule(ast_matchers::MatchFinder::MatchResult R,
36+
transformer::EditGenerator EG)
37+
: Result(std::move(R)), EditGenerator(std::move(EG)) {}
38+
39+
Expected<AtomicChanges>
40+
createSourceReplacements(RefactoringRuleContext &Context) override;
41+
42+
ast_matchers::MatchFinder::MatchResult Result;
43+
transformer::EditGenerator EditGenerator;
44+
};
45+
46+
} // end namespace tooling
47+
} // end namespace clang
48+
49+
#endif // LLVM_CLANG_TOOLING_REFACTORING_EDIT_EDITMATCHRULE_H

clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99
#ifndef LLVM_CLANG_TOOLING_REFACTORING_REFACTORINGACTIONRULEREQUIREMENTS_H
1010
#define LLVM_CLANG_TOOLING_REFACTORING_REFACTORINGACTIONRULEREQUIREMENTS_H
1111

12+
#include "clang/AST/ASTTypeTraits.h"
13+
#include "clang/ASTMatchers/ASTMatchFinder.h"
14+
#include "clang/ASTMatchers/ASTMatchers.h"
15+
#include "clang/ASTMatchers/ASTMatchersMacros.h"
1216
#include "clang/Basic/LLVM.h"
1317
#include "clang/Basic/SourceLocation.h"
1418
#include "clang/Tooling/Refactoring/ASTSelection.h"
1519
#include "clang/Tooling/Refactoring/RefactoringDiagnostic.h"
1620
#include "clang/Tooling/Refactoring/RefactoringOption.h"
1721
#include "clang/Tooling/Refactoring/RefactoringRuleContext.h"
22+
#include "clang/Tooling/Transformer/RewriteRule.h"
1823
#include "llvm/Support/Error.h"
19-
#include <type_traits>
2024

2125
namespace clang {
2226
namespace tooling {
@@ -89,6 +93,90 @@ class SourceLocationRequirement : public RefactoringActionRuleRequirement {
8993
}
9094
};
9195

96+
AST_POLYMORPHIC_MATCHER_P(hasPointWithin,
97+
AST_POLYMORPHIC_SUPPORTED_TYPES(Stmt, Decl),
98+
FullSourceLoc, L) {
99+
if (!L.hasManager()) {
100+
return false;
101+
}
102+
const SourceRange &SR = Node.getSourceRange();
103+
return L.getManager().isPointWithin(L, SR.getBegin(), SR.getEnd());
104+
}
105+
106+
/// An AST location match is satisfied when there is match around given
107+
/// location. In case of several matches inner one is taken.
108+
///
109+
/// The requirement will be evaluated only once during the initiation and
110+
/// search of matching refactoring action rules.
111+
template <typename MatcherType>
112+
class ASTLocMatchRequirement : public SourceLocationRequirement {
113+
public:
114+
static_assert(
115+
std::is_same<ast_matchers::StatementMatcher, MatcherType>::value ||
116+
std::is_same<ast_matchers::DeclarationMatcher, MatcherType>::value,
117+
"Expected a Statement or Declaration matcher");
118+
119+
class LocMatchCallback : public ast_matchers::MatchFinder::MatchCallback {
120+
public:
121+
void run(const clang::ast_matchers::MatchFinder::MatchResult &R) override {
122+
Result = std::make_unique<ast_matchers::MatchFinder::MatchResult>(R);
123+
}
124+
std::unique_ptr<ast_matchers::MatchFinder::MatchResult> Result;
125+
};
126+
127+
Expected<ast_matchers::MatchFinder::MatchResult>
128+
evaluate(RefactoringRuleContext &Context) const {
129+
Expected<SourceLocation> Location =
130+
SourceLocationRequirement::evaluate(Context);
131+
if (!Location)
132+
return Location.takeError();
133+
MatcherType M = createWrapperMatcher(
134+
FullSourceLoc(*Location, Context.getASTContext().getSourceManager()),
135+
Matcher);
136+
137+
ast_matchers::MatchFinder MF;
138+
LocMatchCallback Callback;
139+
MF.addMatcher(M, &Callback);
140+
MF.matchAST(Context.getASTContext());
141+
if (!Callback.Result)
142+
return Context.createDiagnosticError(
143+
diag::err_refactor_no_location_match);
144+
return *Callback.Result;
145+
}
146+
147+
ASTLocMatchRequirement(MatcherType M) : Matcher(M) {}
148+
149+
private:
150+
ast_matchers::StatementMatcher
151+
createWrapperMatcher(FullSourceLoc L,
152+
ast_matchers::StatementMatcher M) const {
153+
return ast_matchers::stmt(M, hasPointWithin(L)).bind(transformer::RootID);
154+
}
155+
156+
ast_matchers::DeclarationMatcher
157+
createWrapperMatcher(FullSourceLoc L,
158+
ast_matchers::DeclarationMatcher M) const {
159+
return ast_matchers::decl(M, hasPointWithin(L)).bind(transformer::RootID);
160+
}
161+
162+
MatcherType Matcher;
163+
};
164+
165+
/// Requirement that evaluates to the EditGenerator value given at its creation.
166+
class EditGeneratorRequirement : public RefactoringActionRuleRequirement {
167+
public:
168+
Expected<transformer::EditGenerator>
169+
evaluate(RefactoringRuleContext &Context) const {
170+
return EditGenerator;
171+
}
172+
173+
EditGeneratorRequirement(transformer::EditGenerator EG)
174+
: EditGenerator(std::move(EG)) {}
175+
176+
private:
177+
transformer::EditGenerator EditGenerator;
178+
};
179+
92180
/// A base class for any requirement that requires some refactoring options.
93181
class RefactoringOptionsRequirement : public RefactoringActionRuleRequirement {
94182
public:

clang/lib/Tooling/Refactoring/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ add_clang_library(clangToolingRefactoring
1313
Rename/USRFinder.cpp
1414
Rename/USRFindingAction.cpp
1515
Rename/USRLocFinder.cpp
16+
Edit/EditMatchRule.cpp
1617

1718
LINK_LIBS
1819
clangAST
@@ -23,6 +24,7 @@ add_clang_library(clangToolingRefactoring
2324
clangLex
2425
clangRewrite
2526
clangToolingCore
27+
clangTransformer
2628

2729
DEPENDS
2830
omp_gen
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//===--- EditMatchRule.cpp - Clang refactoring library --------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
///
9+
/// \file
10+
/// Implements the "edit-match" refactoring rule that can edit matcher results
11+
///
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "clang/Tooling/Refactoring/Edit/EditMatchRule.h"
15+
#include "clang/AST/ASTContext.h"
16+
#include "clang/ASTMatchers/ASTMatchFinder.h"
17+
#include "clang/Basic/DiagnosticRefactoring.h"
18+
#include "clang/Rewrite/Core/Rewriter.h"
19+
#include "clang/Tooling/Refactoring/AtomicChange.h"
20+
#include "clang/Tooling/Transformer/RewriteRule.h"
21+
#include "clang/Tooling/Transformer/SourceCode.h"
22+
23+
using namespace clang;
24+
using namespace tooling;
25+
using namespace transformer;
26+
27+
Expected<EditMatchRule>
28+
EditMatchRule::initiate(RefactoringRuleContext &Context,
29+
ast_matchers::MatchFinder::MatchResult R,
30+
transformer::EditGenerator EG) {
31+
return EditMatchRule(std::move(R), std::move(EG));
32+
}
33+
34+
const RefactoringDescriptor &EditMatchRule::describe() {
35+
static const RefactoringDescriptor Descriptor = {
36+
"edit-match",
37+
"Edit Match",
38+
"Edits match result source code",
39+
};
40+
return Descriptor;
41+
}
42+
43+
Expected<AtomicChanges>
44+
EditMatchRule::createSourceReplacements(RefactoringRuleContext &Context) {
45+
ASTContext &AST = Context.getASTContext();
46+
SourceManager &SM = AST.getSourceManager();
47+
Expected<SmallVector<transformer::Edit, 1>> Edits = EditGenerator(Result);
48+
49+
if (!Edits) {
50+
return std::move(Edits.takeError());
51+
}
52+
if (Edits->empty())
53+
return Context.createDiagnosticError(
54+
diag::err_refactor_invalid_edit_generator);
55+
56+
AtomicChange Change(SM, Edits->front().Range.getBegin());
57+
{
58+
for (const auto &Edit : *Edits) {
59+
switch (Edit.Kind) {
60+
case EditKind::Range:
61+
if (auto Err = Change.replace(SM, std::move(Edit.Range),
62+
std::move(Edit.Replacement))) {
63+
return std::move(Err);
64+
}
65+
break;
66+
case EditKind::AddInclude:
67+
Change.addHeader(std::move(Edit.Replacement));
68+
break;
69+
}
70+
}
71+
}
72+
73+
return AtomicChanges{std::move(Change)};
74+
}

0 commit comments

Comments
 (0)