Skip to content

Commit 1eb49cd

Browse files
author
git apple-llvm automerger
committed
Merge commit '386fd2c170a7' from llvm.org/master into apple/master
2 parents ff34f05 + 386fd2c commit 1eb49cd

File tree

4 files changed

+174
-0
lines changed

4 files changed

+174
-0
lines changed

clang/include/clang/ASTMatchers/ASTMatchers.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,26 @@ AST_POLYMORPHIC_MATCHER_P(isExpansionInFileMatching,
300300
return RE.match(Filename);
301301
}
302302

303+
/// Matches statements that are (transitively) expanded from the named macro.
304+
/// Does not match if only part of the statement is expanded from that macro or
305+
/// if different parts of the the statement are expanded from different
306+
/// appearances of the macro.
307+
///
308+
/// FIXME: Change to be a polymorphic matcher that works on any syntactic
309+
/// node. There's nothing `Stmt`-specific about it.
310+
AST_MATCHER_P(clang::Stmt, isExpandedFromMacro, llvm::StringRef, MacroName) {
311+
// Verifies that the statement' beginning and ending are both expanded from
312+
// the same instance of the given macro.
313+
auto& Context = Finder->getASTContext();
314+
auto B =
315+
internal::getExpansionLocOfMacro(MacroName, Node.getBeginLoc(), Context);
316+
if (!B) return false;
317+
auto E =
318+
internal::getExpansionLocOfMacro(MacroName, Node.getEndLoc(), Context);
319+
if (!E) return false;
320+
return *B == *E;
321+
}
322+
303323
/// Matches declarations.
304324
///
305325
/// Examples matches \c X, \c C, and the friend declaration inside \c C;

clang/include/clang/ASTMatchers/ASTMatchersInternal.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,6 +1872,13 @@ CompoundStmtMatcher<StmtExpr>::get(const StmtExpr &Node) {
18721872
return Node.getSubStmt();
18731873
}
18741874

1875+
/// If \p Loc is (transitively) expanded from macro \p MacroName, returns the
1876+
/// location (in the chain of expansions) at which \p MacroName was
1877+
/// expanded. Since the macro may have been expanded inside a series of
1878+
/// expansions, that location may itself be a MacroID.
1879+
llvm::Optional<SourceLocation>
1880+
getExpansionLocOfMacro(StringRef MacroName, SourceLocation Loc,
1881+
const ASTContext &Context);
18751882
} // namespace internal
18761883

18771884
} // namespace ast_matchers

clang/lib/ASTMatchers/ASTMatchersInternal.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "clang/AST/PrettyPrinter.h"
2020
#include "clang/ASTMatchers/ASTMatchers.h"
2121
#include "clang/Basic/LLVM.h"
22+
#include "clang/Lex/Lexer.h"
2223
#include "llvm/ADT/ArrayRef.h"
2324
#include "llvm/ADT/IntrusiveRefCntPtr.h"
2425
#include "llvm/ADT/None.h"
@@ -595,6 +596,38 @@ bool HasNameMatcher::matchesNode(const NamedDecl &Node) const {
595596
return matchesNodeFullFast(Node);
596597
}
597598

599+
// Checks whether \p Loc points to a token with source text of \p TokenText.
600+
static bool isTokenAtLoc(const SourceManager &SM, const LangOptions &LangOpts,
601+
StringRef Text, SourceLocation Loc) {
602+
llvm::SmallString<16> Buffer;
603+
bool Invalid = false;
604+
// Since `Loc` may point into an expansion buffer, which has no corresponding
605+
// source, we need to look at the spelling location to read the actual source.
606+
StringRef TokenText = clang::Lexer::getSpelling(
607+
SM.getSpellingLoc(Loc), Buffer, SM, LangOpts, &Invalid);
608+
return !Invalid && Text == TokenText;
609+
}
610+
611+
llvm::Optional<SourceLocation>
612+
getExpansionLocOfMacro(StringRef MacroName, SourceLocation Loc,
613+
const ASTContext &Context) {
614+
auto& SM = Context.getSourceManager();
615+
const auto& LangOpts = Context.getLangOpts();
616+
while (Loc.isMacroID()) {
617+
auto Expansion = SM.getSLocEntry(SM.getFileID(Loc)).getExpansion();
618+
if (Expansion.isMacroArgExpansion())
619+
// Check macro argument for an expansion of the given macro. For example,
620+
// `F(G(3))`, where `MacroName` is `G`.
621+
if (auto ArgLoc = getExpansionLocOfMacro(
622+
MacroName, Expansion.getSpellingLoc(), Context))
623+
return ArgLoc;
624+
Loc = Expansion.getExpansionLocStart();
625+
if (isTokenAtLoc(SM, LangOpts, MacroName, Loc))
626+
return Loc;
627+
}
628+
return llvm::None;
629+
}
630+
598631
} // end namespace internal
599632

600633
const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCAutoreleasePoolStmt>

clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,120 @@
1818
namespace clang {
1919
namespace ast_matchers {
2020

21+
TEST(IsExpandedFromMacro, ShouldMatchInFile) {
22+
std::string input = R"cc(
23+
#define MY_MACRO(a) (4 + (a))
24+
void Test() { MY_MACRO(4); }
25+
)cc";
26+
EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("MY_MACRO"))));
27+
}
28+
29+
TEST(IsExpandedFromMacro, ShouldMatchNested) {
30+
std::string input = R"cc(
31+
#define MY_MACRO(a) (4 + (a))
32+
#define WRAPPER(a) MY_MACRO(a)
33+
void Test() { WRAPPER(4); }
34+
)cc";
35+
EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("MY_MACRO"))));
36+
}
37+
38+
TEST(IsExpandedFromMacro, ShouldMatchIntermediate) {
39+
std::string input = R"cc(
40+
#define IMPL(a) (4 + (a))
41+
#define MY_MACRO(a) IMPL(a)
42+
#define WRAPPER(a) MY_MACRO(a)
43+
void Test() { WRAPPER(4); }
44+
)cc";
45+
EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("MY_MACRO"))));
46+
}
47+
48+
TEST(IsExpandedFromMacro, ShouldMatchTransitive) {
49+
std::string input = R"cc(
50+
#define MY_MACRO(a) (4 + (a))
51+
#define WRAPPER(a) MY_MACRO(a)
52+
void Test() { WRAPPER(4); }
53+
)cc";
54+
EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("WRAPPER"))));
55+
}
56+
57+
TEST(IsExpandedFromMacro, ShouldMatchArgument) {
58+
std::string input = R"cc(
59+
#define MY_MACRO(a) (4 + (a))
60+
void Test() {
61+
int x = 5;
62+
MY_MACRO(x);
63+
}
64+
)cc";
65+
EXPECT_TRUE(matches(input, declRefExpr(isExpandedFromMacro("MY_MACRO"))));
66+
}
67+
68+
// Like IsExpandedFromMacroShouldMatchArgumentMacro, but the argument is itself
69+
// a macro.
70+
TEST(IsExpandedFromMacro, ShouldMatchArgumentMacroExpansion) {
71+
std::string input = R"cc(
72+
#define MY_MACRO(a) (4 + (a))
73+
#define IDENTITY(a) (a)
74+
void Test() {
75+
IDENTITY(MY_MACRO(2));
76+
}
77+
)cc";
78+
EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("IDENTITY"))));
79+
}
80+
81+
TEST(IsExpandedFromMacro, ShouldMatchWhenInArgument) {
82+
std::string input = R"cc(
83+
#define MY_MACRO(a) (4 + (a))
84+
#define IDENTITY(a) (a)
85+
void Test() {
86+
IDENTITY(MY_MACRO(2));
87+
}
88+
)cc";
89+
EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("MY_MACRO"))));
90+
}
91+
92+
TEST(IsExpandedFromMacro, ShouldMatchObjectMacro) {
93+
std::string input = R"cc(
94+
#define PLUS (2 + 2)
95+
void Test() {
96+
PLUS;
97+
}
98+
)cc";
99+
EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("PLUS"))));
100+
}
101+
102+
TEST(IsExpandedFromMacro, ShouldNotMatchBeginOnly) {
103+
std::string input = R"cc(
104+
#define ONE_PLUS 1+
105+
void Test() { ONE_PLUS 4; }
106+
)cc";
107+
EXPECT_TRUE(
108+
notMatches(input, binaryOperator(isExpandedFromMacro("ONE_PLUS"))));
109+
}
110+
111+
TEST(IsExpandedFromMacro, ShouldNotMatchEndOnly) {
112+
std::string input = R"cc(
113+
#define PLUS_ONE +1
114+
void Test() { 4 PLUS_ONE; }
115+
)cc";
116+
EXPECT_TRUE(
117+
notMatches(input, binaryOperator(isExpandedFromMacro("PLUS_ONE"))));
118+
}
119+
120+
TEST(IsExpandedFromMacro, ShouldNotMatchDifferentMacro) {
121+
std::string input = R"cc(
122+
#define MY_MACRO(a) (4 + (a))
123+
void Test() { MY_MACRO(4); }
124+
)cc";
125+
EXPECT_TRUE(notMatches(input, binaryOperator(isExpandedFromMacro("OTHER"))));
126+
}
127+
128+
TEST(IsExpandedFromMacro, ShouldNotMatchDifferentInstances) {
129+
std::string input = R"cc(
130+
#define FOUR 4
131+
void Test() { FOUR + FOUR; }
132+
)cc";
133+
EXPECT_TRUE(notMatches(input, binaryOperator(isExpandedFromMacro("FOUR"))));
134+
}
21135

22136
TEST(AllOf, AllOverloadsWork) {
23137
const char Program[] =

0 commit comments

Comments
 (0)