Skip to content

Commit d6a195f

Browse files
committed
Add merge range-selector
1 parent 420f62e commit d6a195f

File tree

4 files changed

+87
-1
lines changed

4 files changed

+87
-1
lines changed

clang/include/clang/Tooling/Transformer/RangeSelector.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ RangeSelector enclose(RangeSelector Begin, RangeSelector End);
3737
/// Convenience version of \c range where end-points are bound nodes.
3838
RangeSelector encloseNodes(std::string BeginID, std::string EndID);
3939

40+
/// Selects the merge of the two ranges, i.e. from min(First.begin,
41+
/// Second.begin) to max(First.end, Second.end).
42+
RangeSelector merge(RangeSelector First, RangeSelector Second);
43+
4044
/// DEPRECATED. Use `enclose`.
4145
inline RangeSelector range(RangeSelector Begin, RangeSelector End) {
4246
return enclose(std::move(Begin), std::move(End));

clang/lib/Tooling/Transformer/Parsing.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ getBinaryStringSelectors() {
108108
static const llvm::StringMap<RangeSelectorOp<RangeSelector, RangeSelector>> &
109109
getBinaryRangeSelectors() {
110110
static const llvm::StringMap<RangeSelectorOp<RangeSelector, RangeSelector>>
111-
M = {{"enclose", enclose}, {"between", between}};
111+
M = {{"enclose", enclose}, {"between", between}, {"merge", merge}};
112112
return M;
113113
}
114114

clang/lib/Tooling/Transformer/RangeSelector.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,56 @@ RangeSelector transformer::encloseNodes(std::string BeginID,
178178
return transformer::enclose(node(std::move(BeginID)), node(std::move(EndID)));
179179
}
180180

181+
RangeSelector transformer::merge(RangeSelector First, RangeSelector Second) {
182+
return [First,
183+
Second](const MatchResult &Result) -> Expected<CharSourceRange> {
184+
Expected<CharSourceRange> FirstRange = First(Result);
185+
if (!FirstRange)
186+
return FirstRange.takeError();
187+
Expected<CharSourceRange> SecondRange = Second(Result);
188+
if (!SecondRange)
189+
return SecondRange.takeError();
190+
// Result begin loc is the minimum of the begin locs of the two ranges.
191+
SourceLocation B = FirstRange->getBegin() < SecondRange->getBegin()
192+
? FirstRange->getBegin()
193+
: SecondRange->getBegin();
194+
if (FirstRange->isTokenRange() && SecondRange->isTokenRange()) {
195+
// Both ranges are token ranges. Just take the maximum of their end locs.
196+
SourceLocation E = FirstRange->getEnd() > SecondRange->getEnd()
197+
? FirstRange->getEnd()
198+
: SecondRange->getEnd();
199+
return CharSourceRange::getTokenRange(B, E);
200+
}
201+
SourceLocation FirstE = FirstRange->getEnd();
202+
if (FirstRange->isTokenRange()) {
203+
// The end of the first range is a token. Need to resolve the token to a
204+
// char range.
205+
CharSourceRange EndRange = Lexer::makeFileCharRange(
206+
CharSourceRange::getTokenRange(FirstRange->getEnd()),
207+
*Result.SourceManager, Result.Context->getLangOpts());
208+
if (EndRange.isInvalid())
209+
return invalidArgumentError(
210+
"merge: can't resolve first token range to valid source range");
211+
FirstE = EndRange.getEnd();
212+
}
213+
SourceLocation SecondE = SecondRange->getEnd();
214+
if (SecondRange->isTokenRange()) {
215+
// The end of the second range is a token. Need to resolve the token to a
216+
// char range.
217+
CharSourceRange EndRange = Lexer::makeFileCharRange(
218+
CharSourceRange::getTokenRange(SecondRange->getEnd()),
219+
*Result.SourceManager, Result.Context->getLangOpts());
220+
if (EndRange.isInvalid())
221+
return invalidArgumentError(
222+
"merge: can't resolve second token range to valid source range");
223+
SecondE = EndRange.getEnd();
224+
}
225+
// Result end loc is the maximum of the end locs of the two ranges.
226+
SourceLocation E = FirstE > SecondE ? FirstE : SecondE;
227+
return CharSourceRange::getCharRange(B, E);
228+
};
229+
}
230+
181231
RangeSelector transformer::member(std::string ID) {
182232
return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
183233
Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);

clang/unittests/Tooling/RangeSelectorTest.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,38 @@ TEST(RangeSelectorTest, EncloseOpGeneralParsed) {
327327
EXPECT_THAT_EXPECTED(select(*R, Match), HasValue("3, 7"));
328328
}
329329

330+
TEST(RangeSelectorTest, MergeOp) {
331+
StringRef Code = R"cc(
332+
int f(int x, int y, int z) { return 3; }
333+
int g() { return f(/* comment */ 3, 7 /* comment */, 9); }
334+
)cc";
335+
auto Matcher = callExpr(hasArgument(0, expr().bind("a0")),
336+
hasArgument(1, expr().bind("a1")),
337+
hasArgument(2, expr().bind("a2")));
338+
RangeSelector R = merge(node("a0"), node("a1"));
339+
TestMatch Match = matchCode(Code, Matcher);
340+
EXPECT_THAT_EXPECTED(select(R, Match), HasValue("3, 7"));
341+
R = merge(node("a2"), node("a1"));
342+
EXPECT_THAT_EXPECTED(select(R, Match), HasValue("7 /* comment */, 9"));
343+
}
344+
345+
TEST(RangeSelectorTest, MergeOpParsed) {
346+
StringRef Code = R"cc(
347+
int f(int x, int y, int z) { return 3; }
348+
int g() { return f(/* comment */ 3, 7 /* comment */, 9); }
349+
)cc";
350+
auto Matcher = callExpr(hasArgument(0, expr().bind("a0")),
351+
hasArgument(1, expr().bind("a1")),
352+
hasArgument(2, expr().bind("a2")));
353+
auto R = parseRangeSelector(R"rs(merge(node("a0"), node("a1")))rs");
354+
ASSERT_THAT_EXPECTED(R, llvm::Succeeded());
355+
TestMatch Match = matchCode(Code, Matcher);
356+
EXPECT_THAT_EXPECTED(select(*R, Match), HasValue("3, 7"));
357+
R = parseRangeSelector(R"rs(merge(node("a2"), node("a1")))rs");
358+
ASSERT_THAT_EXPECTED(R, llvm::Succeeded());
359+
EXPECT_THAT_EXPECTED(select(*R, Match), HasValue("7 /* comment */, 9"));
360+
}
361+
330362
TEST(RangeSelectorTest, NodeOpStatement) {
331363
StringRef Code = "int f() { return 3; }";
332364
TestMatch Match = matchCode(Code, returnStmt().bind("id"));

0 commit comments

Comments
 (0)