Skip to content

Commit 9f50c29

Browse files
committed
[libTooling] Fix constructExprArgs for direct-init and implicit construction
Use `getParenOrBraceRange()` to get the location of the opening paren or braces instead of searching backwards from the first argument. For implicit construction, get the range surrounding the first and last arguments.
1 parent 741fef3 commit 9f50c29

File tree

2 files changed

+88
-27
lines changed

2 files changed

+88
-27
lines changed

clang/lib/Tooling/Transformer/RangeSelector.cpp

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -281,49 +281,67 @@ RangeSelector transformer::statements(std::string ID) {
281281

282282
namespace {
283283

284-
SourceLocation getRLoc(const CallExpr &E) { return E.getRParenLoc(); }
285-
286-
SourceLocation getRLoc(const CXXConstructExpr &E) {
287-
return E.getParenOrBraceRange().getEnd();
288-
}
289-
290-
tok::TokenKind getStartToken(const CallExpr &E) {
291-
return tok::TokenKind::l_paren;
292-
}
293-
294-
tok::TokenKind getStartToken(const CXXConstructExpr &E) {
295-
return isa<CXXTemporaryObjectExpr>(E) ? tok::TokenKind::l_paren
296-
: tok::TokenKind::l_brace;
297-
}
298-
299-
template <typename ExprWithArgs>
300-
SourceLocation findArgStartDelimiter(const ExprWithArgs &E, SourceLocation RLoc,
284+
SourceLocation findArgStartDelimiter(const CallExpr &E, SourceLocation RLoc,
301285
const SourceManager &SM,
302286
const LangOptions &LangOpts) {
303287
SourceLocation Loc = E.getNumArgs() == 0 ? RLoc : E.getArg(0)->getBeginLoc();
304-
return findPreviousTokenKind(Loc, SM, LangOpts, getStartToken(E));
288+
return findPreviousTokenKind(Loc, SM, LangOpts, tok::TokenKind::l_paren);
305289
}
306-
// Returns the range of the source between the call's or construct expr's
307-
// parentheses/braces.
308-
template <typename ExprWithArgs>
309-
CharSourceRange getArgumentsRange(const MatchResult &Result,
310-
const ExprWithArgs &CE) {
311-
const SourceLocation RLoc = getRLoc(CE);
290+
291+
// Returns the location after the last argument of the construct expr. Returns
292+
// an invalid location if there are no arguments.
293+
SourceLocation findLastArgEnd(const CXXConstructExpr &CE,
294+
const SourceManager &SM,
295+
const LangOptions &LangOpts) {
296+
for (auto *E : llvm::reverse(CE.arguments())) {
297+
if (isa<CXXDefaultArgExpr>(E))
298+
continue;
299+
return Lexer::getLocForEndOfToken(E->getEndLoc(), 0, SM, LangOpts);
300+
}
301+
return {};
302+
}
303+
304+
// Returns the range of the source between the call's parentheses/braces.
305+
CharSourceRange getCallArgumentsRange(const MatchResult &Result,
306+
const CallExpr &CE) {
307+
const SourceLocation RLoc = CE.getRParenLoc();
312308
return CharSourceRange::getCharRange(
313309
findArgStartDelimiter(CE, RLoc, *Result.SourceManager,
314310
Result.Context->getLangOpts())
315311
.getLocWithOffset(1),
316312
RLoc);
317313
}
314+
315+
// Returns the range of the source between the construct expr's
316+
// parentheses/braces.
317+
CharSourceRange getConstructArgumentsRange(const MatchResult &Result,
318+
const CXXConstructExpr &CE) {
319+
if (SourceRange R = CE.getParenOrBraceRange(); R.isValid()) {
320+
return CharSourceRange::getCharRange(
321+
Lexer::getLocForEndOfToken(R.getBegin(), 0, *Result.SourceManager,
322+
Result.Context->getLangOpts()),
323+
R.getEnd());
324+
}
325+
326+
if (CE.getNumArgs() > 0) {
327+
return CharSourceRange::getCharRange(
328+
CE.getArg(0)->getBeginLoc(),
329+
findLastArgEnd(CE, *Result.SourceManager,
330+
Result.Context->getLangOpts()));
331+
}
332+
333+
return {};
334+
}
335+
318336
} // namespace
319337

320338
RangeSelector transformer::callArgs(std::string ID) {
321-
return RelativeSelector<CallExpr, getArgumentsRange<CallExpr>>(std::move(ID));
339+
return RelativeSelector<CallExpr, getCallArgumentsRange>(std::move(ID));
322340
}
323341

324342
RangeSelector transformer::constructExprArgs(std::string ID) {
325-
return RelativeSelector<CXXConstructExpr,
326-
getArgumentsRange<CXXConstructExpr>>(std::move(ID));
343+
return RelativeSelector<CXXConstructExpr, getConstructArgumentsRange>(
344+
std::move(ID));
327345
}
328346

329347
namespace {

clang/unittests/Tooling/RangeSelectorTest.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,49 @@ TEST(RangeSelectorTest, ConstructExprNoArgs) {
693693
EXPECT_THAT_EXPECTED(select(constructExprArgs(ID), Match), HasValue(""));
694694
}
695695

696+
TEST(RangeSelectorTest, ConstructExprArgsDirectInitialization) {
697+
const StringRef Code = R"cc(
698+
struct C {
699+
C(int, int);
700+
};
701+
void f() {
702+
C c(1, 2);
703+
}
704+
)cc";
705+
const char *ID = "id";
706+
TestMatch Match = matchCode(Code, cxxConstructExpr().bind(ID));
707+
EXPECT_THAT_EXPECTED(select(constructExprArgs(ID), Match), HasValue("1, 2"));
708+
}
709+
710+
TEST(RangeSelectorTest, ConstructExprArgsDirectBraceInitialization) {
711+
const StringRef Code = R"cc(
712+
struct C {
713+
C(int, int);
714+
};
715+
void f() {
716+
C c{1, 2};
717+
}
718+
)cc";
719+
const char *ID = "id";
720+
TestMatch Match = matchCode(Code, cxxConstructExpr().bind(ID));
721+
EXPECT_THAT_EXPECTED(select(constructExprArgs(ID), Match), HasValue("1, 2"));
722+
}
723+
724+
TEST(RangeSelectorTest, ConstructExprArgsImplicitConstruction) {
725+
const StringRef Code = R"cc(
726+
struct C {
727+
C(int, int = 42);
728+
};
729+
void sink(C);
730+
void f() {
731+
sink(1);
732+
}
733+
)cc";
734+
const char *ID = "id";
735+
TestMatch Match = matchCode(Code, cxxConstructExpr().bind(ID));
736+
EXPECT_THAT_EXPECTED(select(constructExprArgs(ID), Match), HasValue("1"));
737+
}
738+
696739
TEST(RangeSelectorTest, StatementsOp) {
697740
StringRef Code = R"cc(
698741
void g();

0 commit comments

Comments
 (0)