[clang] [libTooling] Fix `constructExprArgs` for direct-init and implicit construction (PR #139990)
Eric Li via cfe-commits
cfe-commits at lists.llvm.org
Thu May 15 07:24:10 PDT 2025
https://github.com/tJener updated https://github.com/llvm/llvm-project/pull/139990
>From c7f23d4280de89810606b5c34d7fb8d03d7a9c3f Mon Sep 17 00:00:00 2001
From: Eric Li <li.zhe.hua at gmail.com>
Date: Wed, 14 May 2025 21:21:07 -0400
Subject: [PATCH 1/3] [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.
---
.../lib/Tooling/Transformer/RangeSelector.cpp | 73 ++++++++++++-------
clang/unittests/Tooling/RangeSelectorTest.cpp | 43 +++++++++++
2 files changed, 89 insertions(+), 27 deletions(-)
diff --git a/clang/lib/Tooling/Transformer/RangeSelector.cpp b/clang/lib/Tooling/Transformer/RangeSelector.cpp
index e84ddde74a707..73e6109da178e 100644
--- a/clang/lib/Tooling/Transformer/RangeSelector.cpp
+++ b/clang/lib/Tooling/Transformer/RangeSelector.cpp
@@ -281,49 +281,68 @@ RangeSelector transformer::statements(std::string ID) {
namespace {
-SourceLocation getRLoc(const CallExpr &E) { return E.getRParenLoc(); }
-
-SourceLocation getRLoc(const CXXConstructExpr &E) {
- return E.getParenOrBraceRange().getEnd();
-}
-
-tok::TokenKind getStartToken(const CallExpr &E) {
- return tok::TokenKind::l_paren;
-}
-
-tok::TokenKind getStartToken(const CXXConstructExpr &E) {
- return isa<CXXTemporaryObjectExpr>(E) ? tok::TokenKind::l_paren
- : tok::TokenKind::l_brace;
-}
-
-template <typename ExprWithArgs>
-SourceLocation findArgStartDelimiter(const ExprWithArgs &E, SourceLocation RLoc,
+SourceLocation findArgStartDelimiter(const CallExpr &E, SourceLocation RLoc,
const SourceManager &SM,
const LangOptions &LangOpts) {
SourceLocation Loc = E.getNumArgs() == 0 ? RLoc : E.getArg(0)->getBeginLoc();
- return findPreviousTokenKind(Loc, SM, LangOpts, getStartToken(E));
+ return findPreviousTokenKind(Loc, SM, LangOpts, tok::TokenKind::l_paren);
}
-// Returns the range of the source between the call's or construct expr's
-// parentheses/braces.
-template <typename ExprWithArgs>
-CharSourceRange getArgumentsRange(const MatchResult &Result,
- const ExprWithArgs &CE) {
- const SourceLocation RLoc = getRLoc(CE);
+
+// Returns the location after the last argument of the construct expr. Returns
+// an invalid location if there are no arguments.
+SourceLocation findLastArgEnd(const CXXConstructExpr &CE,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ for (int i = CE.getNumArgs() - 1; i >= 0; --i) {
+ const Expr *Arg = CE.getArg(i);
+ if (isa<CXXDefaultArgExpr>(Arg))
+ continue;
+ return Lexer::getLocForEndOfToken(Arg->getEndLoc(), 0, SM, LangOpts);
+ }
+ return {};
+}
+
+// Returns the range of the source between the call's parentheses/braces.
+CharSourceRange getCallArgumentsRange(const MatchResult &Result,
+ const CallExpr &CE) {
+ const SourceLocation RLoc = CE.getRParenLoc();
return CharSourceRange::getCharRange(
findArgStartDelimiter(CE, RLoc, *Result.SourceManager,
Result.Context->getLangOpts())
.getLocWithOffset(1),
RLoc);
}
+
+// Returns the range of the source between the construct expr's
+// parentheses/braces.
+CharSourceRange getConstructArgumentsRange(const MatchResult &Result,
+ const CXXConstructExpr &CE) {
+ if (SourceRange R = CE.getParenOrBraceRange(); R.isValid()) {
+ return CharSourceRange::getCharRange(
+ Lexer::getLocForEndOfToken(R.getBegin(), 0, *Result.SourceManager,
+ Result.Context->getLangOpts()),
+ R.getEnd());
+ }
+
+ if (CE.getNumArgs() > 0) {
+ return CharSourceRange::getCharRange(
+ CE.getArg(0)->getBeginLoc(),
+ findLastArgEnd(CE, *Result.SourceManager,
+ Result.Context->getLangOpts()));
+ }
+
+ return {};
+}
+
} // namespace
RangeSelector transformer::callArgs(std::string ID) {
- return RelativeSelector<CallExpr, getArgumentsRange<CallExpr>>(std::move(ID));
+ return RelativeSelector<CallExpr, getCallArgumentsRange>(std::move(ID));
}
RangeSelector transformer::constructExprArgs(std::string ID) {
- return RelativeSelector<CXXConstructExpr,
- getArgumentsRange<CXXConstructExpr>>(std::move(ID));
+ return RelativeSelector<CXXConstructExpr, getConstructArgumentsRange>(
+ std::move(ID));
}
namespace {
diff --git a/clang/unittests/Tooling/RangeSelectorTest.cpp b/clang/unittests/Tooling/RangeSelectorTest.cpp
index 1bccf899f3f19..7abaa73b09e2c 100644
--- a/clang/unittests/Tooling/RangeSelectorTest.cpp
+++ b/clang/unittests/Tooling/RangeSelectorTest.cpp
@@ -693,6 +693,49 @@ TEST(RangeSelectorTest, ConstructExprNoArgs) {
EXPECT_THAT_EXPECTED(select(constructExprArgs(ID), Match), HasValue(""));
}
+TEST(RangeSelectorTest, ConstructExprArgsDirectInitialization) {
+ const StringRef Code = R"cc(
+ struct C {
+ C(int, int);
+ };
+ void f() {
+ C c(1, 2);
+ }
+ )cc";
+ const char *ID = "id";
+ TestMatch Match = matchCode(Code, cxxConstructExpr().bind(ID));
+ EXPECT_THAT_EXPECTED(select(constructExprArgs(ID), Match), HasValue("1, 2"));
+}
+
+TEST(RangeSelectorTest, ConstructExprArgsDirectBraceInitialization) {
+ const StringRef Code = R"cc(
+ struct C {
+ C(int, int);
+ };
+ void f() {
+ C c{1, 2};
+ }
+ )cc";
+ const char *ID = "id";
+ TestMatch Match = matchCode(Code, cxxConstructExpr().bind(ID));
+ EXPECT_THAT_EXPECTED(select(constructExprArgs(ID), Match), HasValue("1, 2"));
+}
+
+TEST(RangeSelectorTest, ConstructExprArgsImplicitConstruction) {
+ const StringRef Code = R"cc(
+ struct C {
+ C(int, int = 42);
+ };
+ void sink(C);
+ void f() {
+ sink(1);
+ }
+ )cc";
+ const char *ID = "id";
+ TestMatch Match = matchCode(Code, cxxConstructExpr().bind(ID));
+ EXPECT_THAT_EXPECTED(select(constructExprArgs(ID), Match), HasValue("1"));
+}
+
TEST(RangeSelectorTest, StatementsOp) {
StringRef Code = R"cc(
void g();
>From 8a275025290baadc2d24ca6a0dbab84df2601e53 Mon Sep 17 00:00:00 2001
From: Eric Li <li.zhe.hua at gmail.com>
Date: Thu, 15 May 2025 09:28:43 -0400
Subject: [PATCH 2/3] fixup! [libTooling] Fix `constructExprArgs` for
direct-init and implicit construction
---
clang/unittests/Tooling/RangeSelectorTest.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/clang/unittests/Tooling/RangeSelectorTest.cpp b/clang/unittests/Tooling/RangeSelectorTest.cpp
index 7abaa73b09e2c..30fce8f1e21c9 100644
--- a/clang/unittests/Tooling/RangeSelectorTest.cpp
+++ b/clang/unittests/Tooling/RangeSelectorTest.cpp
@@ -50,6 +50,9 @@ template <typename M> TestMatch matchCode(StringRef Code, M Matcher) {
auto Matches = ast_matchers::match(Matcher, Context);
// We expect a single, exact match.
assert(Matches.size() != 0 && "no matches found");
+ if (Matches.size() != 1) {
+ Context.getTranslationUnitDecl()->dumpColor();
+ }
assert(Matches.size() == 1 && "too many matches");
return TestMatch{std::move(ASTUnit), MatchResult(Matches[0], &Context)};
>From 5c57e88d3326ad9af0967c8a978bb594477937c2 Mon Sep 17 00:00:00 2001
From: Eric Li <li.zhe.hua at gmail.com>
Date: Thu, 15 May 2025 10:23:46 -0400
Subject: [PATCH 3/3] fixup! [libTooling] Fix `constructExprArgs` for
direct-init and implicit construction
---
clang/unittests/Tooling/RangeSelectorTest.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/unittests/Tooling/RangeSelectorTest.cpp b/clang/unittests/Tooling/RangeSelectorTest.cpp
index 30fce8f1e21c9..12f7a8c5223e7 100644
--- a/clang/unittests/Tooling/RangeSelectorTest.cpp
+++ b/clang/unittests/Tooling/RangeSelectorTest.cpp
@@ -50,9 +50,6 @@ template <typename M> TestMatch matchCode(StringRef Code, M Matcher) {
auto Matches = ast_matchers::match(Matcher, Context);
// We expect a single, exact match.
assert(Matches.size() != 0 && "no matches found");
- if (Matches.size() != 1) {
- Context.getTranslationUnitDecl()->dumpColor();
- }
assert(Matches.size() == 1 && "too many matches");
return TestMatch{std::move(ASTUnit), MatchResult(Matches[0], &Context)};
@@ -735,7 +732,10 @@ TEST(RangeSelectorTest, ConstructExprArgsImplicitConstruction) {
}
)cc";
const char *ID = "id";
- TestMatch Match = matchCode(Code, cxxConstructExpr().bind(ID));
+ TestMatch Match = matchCode(
+ Code,
+ cxxConstructExpr(ignoringElidableConstructorCall(cxxConstructExpr()))
+ .bind(ID));
EXPECT_THAT_EXPECTED(select(constructExprArgs(ID), Match), HasValue("1"));
}
More information about the cfe-commits
mailing list