[clang] 386fd2c - [clang] Add matcher to identify macro expansions.
Yitzhak Mandelbaum via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 4 08:44:02 PST 2020
Author: Yitzhak Mandelbaum
Date: 2020-02-04T11:43:06-05:00
New Revision: 386fd2c170a78798a097650b8714183d5a9d93c6
URL: https://github.com/llvm/llvm-project/commit/386fd2c170a78798a097650b8714183d5a9d93c6
DIFF: https://github.com/llvm/llvm-project/commit/386fd2c170a78798a097650b8714183d5a9d93c6.diff
LOG: [clang] Add matcher to identify macro expansions.
Summary:
This revision adds a matcher `isExpandedFromMacro` that determines whether a
statement is (transitively) expanded from a given macro.
Reviewers: gribozavr
Subscribers: cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D73965
Added:
Modified:
clang/include/clang/ASTMatchers/ASTMatchers.h
clang/include/clang/ASTMatchers/ASTMatchersInternal.h
clang/lib/ASTMatchers/ASTMatchersInternal.cpp
clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 2bd24ef4cf6b..ec969b42c6ae 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -300,6 +300,26 @@ AST_POLYMORPHIC_MATCHER_P(isExpansionInFileMatching,
return RE.match(Filename);
}
+/// Matches statements that are (transitively) expanded from the named macro.
+/// Does not match if only part of the statement is expanded from that macro or
+/// if
diff erent parts of the the statement are expanded from
diff erent
+/// appearances of the macro.
+///
+/// FIXME: Change to be a polymorphic matcher that works on any syntactic
+/// node. There's nothing `Stmt`-specific about it.
+AST_MATCHER_P(clang::Stmt, isExpandedFromMacro, llvm::StringRef, MacroName) {
+ // Verifies that the statement' beginning and ending are both expanded from
+ // the same instance of the given macro.
+ auto& Context = Finder->getASTContext();
+ auto B =
+ internal::getExpansionLocOfMacro(MacroName, Node.getBeginLoc(), Context);
+ if (!B) return false;
+ auto E =
+ internal::getExpansionLocOfMacro(MacroName, Node.getEndLoc(), Context);
+ if (!E) return false;
+ return *B == *E;
+}
+
/// Matches declarations.
///
/// Examples matches \c X, \c C, and the friend declaration inside \c C;
diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
index 55c86302b93d..696eca1aa643 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -1872,6 +1872,13 @@ CompoundStmtMatcher<StmtExpr>::get(const StmtExpr &Node) {
return Node.getSubStmt();
}
+/// If \p Loc is (transitively) expanded from macro \p MacroName, returns the
+/// location (in the chain of expansions) at which \p MacroName was
+/// expanded. Since the macro may have been expanded inside a series of
+/// expansions, that location may itself be a MacroID.
+llvm::Optional<SourceLocation>
+getExpansionLocOfMacro(StringRef MacroName, SourceLocation Loc,
+ const ASTContext &Context);
} // namespace internal
} // namespace ast_matchers
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 03b56fae9038..53f6e1d1d278 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -19,6 +19,7 @@
#include "clang/AST/PrettyPrinter.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/LLVM.h"
+#include "clang/Lex/Lexer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/None.h"
@@ -595,6 +596,38 @@ bool HasNameMatcher::matchesNode(const NamedDecl &Node) const {
return matchesNodeFullFast(Node);
}
+// Checks whether \p Loc points to a token with source text of \p TokenText.
+static bool isTokenAtLoc(const SourceManager &SM, const LangOptions &LangOpts,
+ StringRef Text, SourceLocation Loc) {
+ llvm::SmallString<16> Buffer;
+ bool Invalid = false;
+ // Since `Loc` may point into an expansion buffer, which has no corresponding
+ // source, we need to look at the spelling location to read the actual source.
+ StringRef TokenText = clang::Lexer::getSpelling(
+ SM.getSpellingLoc(Loc), Buffer, SM, LangOpts, &Invalid);
+ return !Invalid && Text == TokenText;
+}
+
+llvm::Optional<SourceLocation>
+getExpansionLocOfMacro(StringRef MacroName, SourceLocation Loc,
+ const ASTContext &Context) {
+ auto& SM = Context.getSourceManager();
+ const auto& LangOpts = Context.getLangOpts();
+ while (Loc.isMacroID()) {
+ auto Expansion = SM.getSLocEntry(SM.getFileID(Loc)).getExpansion();
+ if (Expansion.isMacroArgExpansion())
+ // Check macro argument for an expansion of the given macro. For example,
+ // `F(G(3))`, where `MacroName` is `G`.
+ if (auto ArgLoc = getExpansionLocOfMacro(
+ MacroName, Expansion.getSpellingLoc(), Context))
+ return ArgLoc;
+ Loc = Expansion.getExpansionLocStart();
+ if (isTokenAtLoc(SM, LangOpts, MacroName, Loc))
+ return Loc;
+ }
+ return llvm::None;
+}
+
} // end namespace internal
const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCAutoreleasePoolStmt>
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index 479680987fd0..2d6ce84b13ff 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -18,6 +18,120 @@
namespace clang {
namespace ast_matchers {
+TEST(IsExpandedFromMacro, ShouldMatchInFile) {
+ std::string input = R"cc(
+#define MY_MACRO(a) (4 + (a))
+ void Test() { MY_MACRO(4); }
+ )cc";
+ EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("MY_MACRO"))));
+}
+
+TEST(IsExpandedFromMacro, ShouldMatchNested) {
+ std::string input = R"cc(
+#define MY_MACRO(a) (4 + (a))
+#define WRAPPER(a) MY_MACRO(a)
+ void Test() { WRAPPER(4); }
+ )cc";
+ EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("MY_MACRO"))));
+}
+
+TEST(IsExpandedFromMacro, ShouldMatchIntermediate) {
+ std::string input = R"cc(
+#define IMPL(a) (4 + (a))
+#define MY_MACRO(a) IMPL(a)
+#define WRAPPER(a) MY_MACRO(a)
+ void Test() { WRAPPER(4); }
+ )cc";
+ EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("MY_MACRO"))));
+}
+
+TEST(IsExpandedFromMacro, ShouldMatchTransitive) {
+ std::string input = R"cc(
+#define MY_MACRO(a) (4 + (a))
+#define WRAPPER(a) MY_MACRO(a)
+ void Test() { WRAPPER(4); }
+ )cc";
+ EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("WRAPPER"))));
+}
+
+TEST(IsExpandedFromMacro, ShouldMatchArgument) {
+ std::string input = R"cc(
+#define MY_MACRO(a) (4 + (a))
+ void Test() {
+ int x = 5;
+ MY_MACRO(x);
+ }
+ )cc";
+ EXPECT_TRUE(matches(input, declRefExpr(isExpandedFromMacro("MY_MACRO"))));
+}
+
+// Like IsExpandedFromMacroShouldMatchArgumentMacro, but the argument is itself
+// a macro.
+TEST(IsExpandedFromMacro, ShouldMatchArgumentMacroExpansion) {
+ std::string input = R"cc(
+#define MY_MACRO(a) (4 + (a))
+#define IDENTITY(a) (a)
+ void Test() {
+ IDENTITY(MY_MACRO(2));
+ }
+ )cc";
+ EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("IDENTITY"))));
+}
+
+TEST(IsExpandedFromMacro, ShouldMatchWhenInArgument) {
+ std::string input = R"cc(
+#define MY_MACRO(a) (4 + (a))
+#define IDENTITY(a) (a)
+ void Test() {
+ IDENTITY(MY_MACRO(2));
+ }
+ )cc";
+ EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("MY_MACRO"))));
+}
+
+TEST(IsExpandedFromMacro, ShouldMatchObjectMacro) {
+ std::string input = R"cc(
+#define PLUS (2 + 2)
+ void Test() {
+ PLUS;
+ }
+ )cc";
+ EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("PLUS"))));
+}
+
+TEST(IsExpandedFromMacro, ShouldNotMatchBeginOnly) {
+ std::string input = R"cc(
+#define ONE_PLUS 1+
+ void Test() { ONE_PLUS 4; }
+ )cc";
+ EXPECT_TRUE(
+ notMatches(input, binaryOperator(isExpandedFromMacro("ONE_PLUS"))));
+}
+
+TEST(IsExpandedFromMacro, ShouldNotMatchEndOnly) {
+ std::string input = R"cc(
+#define PLUS_ONE +1
+ void Test() { 4 PLUS_ONE; }
+ )cc";
+ EXPECT_TRUE(
+ notMatches(input, binaryOperator(isExpandedFromMacro("PLUS_ONE"))));
+}
+
+TEST(IsExpandedFromMacro, ShouldNotMatchDifferentMacro) {
+ std::string input = R"cc(
+#define MY_MACRO(a) (4 + (a))
+ void Test() { MY_MACRO(4); }
+ )cc";
+ EXPECT_TRUE(notMatches(input, binaryOperator(isExpandedFromMacro("OTHER"))));
+}
+
+TEST(IsExpandedFromMacro, ShouldNotMatchDifferentInstances) {
+ std::string input = R"cc(
+#define FOUR 4
+ void Test() { FOUR + FOUR; }
+ )cc";
+ EXPECT_TRUE(notMatches(input, binaryOperator(isExpandedFromMacro("FOUR"))));
+}
TEST(AllOf, AllOverloadsWork) {
const char Program[] =
More information about the cfe-commits
mailing list