Skip to content

Commit 8452a11

Browse files
authored
[libTooling] Fix constructExprArgs for direct-init and implicit construction (#139990)
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 60e5ecd commit 8452a11

File tree

2 files changed

+92
-27
lines changed

2 files changed

+92
-27
lines changed

clang/lib/Tooling/Transformer/RangeSelector.cpp

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -281,49 +281,68 @@ 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 (int i = CE.getNumArgs() - 1; i >= 0; --i) {
297+
const Expr *Arg = CE.getArg(i);
298+
if (isa<CXXDefaultArgExpr>(Arg))
299+
continue;
300+
return Lexer::getLocForEndOfToken(Arg->getEndLoc(), 0, SM, LangOpts);
301+
}
302+
return {};
303+
}
304+
305+
// Returns the range of the source between the call's parentheses/braces.
306+
CharSourceRange getCallArgumentsRange(const MatchResult &Result,
307+
const CallExpr &CE) {
308+
const SourceLocation RLoc = CE.getRParenLoc();
312309
return CharSourceRange::getCharRange(
313310
findArgStartDelimiter(CE, RLoc, *Result.SourceManager,
314311
Result.Context->getLangOpts())
315312
.getLocWithOffset(1),
316313
RLoc);
317314
}
315+
316+
// Returns the range of the source between the construct expr's
317+
// parentheses/braces.
318+
CharSourceRange getConstructArgumentsRange(const MatchResult &Result,
319+
const CXXConstructExpr &CE) {
320+
if (SourceRange R = CE.getParenOrBraceRange(); R.isValid()) {
321+
return CharSourceRange::getCharRange(
322+
Lexer::getLocForEndOfToken(R.getBegin(), 0, *Result.SourceManager,
323+
Result.Context->getLangOpts()),
324+
R.getEnd());
325+
}
326+
327+
if (CE.getNumArgs() > 0) {
328+
return CharSourceRange::getCharRange(
329+
CE.getArg(0)->getBeginLoc(),
330+
findLastArgEnd(CE, *Result.SourceManager,
331+
Result.Context->getLangOpts()));
332+
}
333+
334+
return {};
335+
}
336+
318337
} // namespace
319338

320339
RangeSelector transformer::callArgs(std::string ID) {
321-
return RelativeSelector<CallExpr, getArgumentsRange<CallExpr>>(std::move(ID));
340+
return RelativeSelector<CallExpr, getCallArgumentsRange>(std::move(ID));
322341
}
323342

324343
RangeSelector transformer::constructExprArgs(std::string ID) {
325-
return RelativeSelector<CXXConstructExpr,
326-
getArgumentsRange<CXXConstructExpr>>(std::move(ID));
344+
return RelativeSelector<CXXConstructExpr, getConstructArgumentsRange>(
345+
std::move(ID));
327346
}
328347

329348
namespace {

clang/unittests/Tooling/RangeSelectorTest.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,52 @@ 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(
736+
Code,
737+
cxxConstructExpr(ignoringElidableConstructorCall(cxxConstructExpr()))
738+
.bind(ID));
739+
EXPECT_THAT_EXPECTED(select(constructExprArgs(ID), Match), HasValue("1"));
740+
}
741+
696742
TEST(RangeSelectorTest, StatementsOp) {
697743
StringRef Code = R"cc(
698744
void g();

0 commit comments

Comments
 (0)