[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