[clang] 04b69d9 - Add clang-query support for mapAnyOf

Stephen Kelly via cfe-commits cfe-commits at lists.llvm.org
Sun Feb 7 07:46:44 PST 2021


Author: Stephen Kelly
Date: 2021-02-07T15:40:15Z
New Revision: 04b69d9a6013cbc39321452fa09e24fed4c98bc7

URL: https://github.com/llvm/llvm-project/commit/04b69d9a6013cbc39321452fa09e24fed4c98bc7
DIFF: https://github.com/llvm/llvm-project/commit/04b69d9a6013cbc39321452fa09e24fed4c98bc7.diff

LOG: Add clang-query support for mapAnyOf

Differential Revision: https://reviews.llvm.org/D94880

Added: 
    

Modified: 
    clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h
    clang/include/clang/ASTMatchers/Dynamic/Parser.h
    clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp
    clang/lib/ASTMatchers/Dynamic/Parser.cpp
    clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h b/clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h
index f095dcdd60b0..10625311c1a5 100644
--- a/clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h
+++ b/clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h
@@ -66,6 +66,8 @@ class Diagnostics {
     ET_RegistryAmbiguousOverload = 5,
     ET_RegistryValueNotFound = 6,
     ET_RegistryUnknownEnumWithReplace = 7,
+    ET_RegistryNonNodeMatcher = 8,
+    ET_RegistryMatcherNoWithSupport = 9,
 
     ET_ParserStringError = 100,
     ET_ParserNoOpenParen = 101,
@@ -77,7 +79,9 @@ class Diagnostics {
     ET_ParserMalformedBindExpr = 107,
     ET_ParserTrailingCode = 108,
     ET_ParserNumberError = 109,
-    ET_ParserOverloadedType = 110
+    ET_ParserOverloadedType = 110,
+    ET_ParserMalformedChainedExpr = 111,
+    ET_ParserFailedToBuildMatcher = 112
   };
 
   /// Helper stream class.

diff  --git a/clang/include/clang/ASTMatchers/Dynamic/Parser.h b/clang/include/clang/ASTMatchers/Dynamic/Parser.h
index 384e88ff5edf..af370d83782a 100644
--- a/clang/include/clang/ASTMatchers/Dynamic/Parser.h
+++ b/clang/include/clang/ASTMatchers/Dynamic/Parser.h
@@ -100,6 +100,14 @@ class Parser {
     virtual llvm::Optional<MatcherCtor>
     lookupMatcherCtor(StringRef MatcherName) = 0;
 
+    virtual bool isBuilderMatcher(MatcherCtor) const = 0;
+
+    virtual ASTNodeKind nodeMatcherType(MatcherCtor) const = 0;
+
+    virtual internal::MatcherDescriptorPtr
+    buildMatcherCtor(MatcherCtor, SourceRange NameRange,
+                     ArrayRef<ParserValue> Args, Diagnostics *Error) const = 0;
+
     /// Compute the list of completion types for \p Context.
     ///
     /// Each element of \p Context represents a matcher invocation, going from
@@ -142,6 +150,15 @@ class Parser {
     std::vector<ArgKind> getAcceptedCompletionTypes(
         llvm::ArrayRef<std::pair<MatcherCtor, unsigned>> Context) override;
 
+    bool isBuilderMatcher(MatcherCtor Ctor) const override;
+
+    ASTNodeKind nodeMatcherType(MatcherCtor) const override;
+
+    internal::MatcherDescriptorPtr
+    buildMatcherCtor(MatcherCtor, SourceRange NameRange,
+                     ArrayRef<ParserValue> Args,
+                     Diagnostics *Error) const override;
+
     std::vector<MatcherCompletion>
     getMatcherCompletions(llvm::ArrayRef<ArgKind> AcceptedTypes) override;
   };
@@ -233,6 +250,8 @@ class Parser {
 
   bool parseBindID(std::string &BindID);
   bool parseExpressionImpl(VariantValue *Value);
+  bool parseMatcherBuilder(MatcherCtor Ctor, const TokenInfo &NameToken,
+                           const TokenInfo &OpenToken, VariantValue *Value);
   bool parseMatcherExpressionImpl(const TokenInfo &NameToken,
                                   const TokenInfo &OpenToken,
                                   llvm::Optional<MatcherCtor> Ctor,

diff  --git a/clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp b/clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp
index 88c2279afb2e..ba2f49e6b623 100644
--- a/clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp
@@ -100,6 +100,10 @@ static StringRef errorTypeToFormatString(Diagnostics::ErrorType Type) {
     return "Value not found: $0";
   case Diagnostics::ET_RegistryUnknownEnumWithReplace:
     return "Unknown value '$1' for arg $0; did you mean '$2'";
+  case Diagnostics::ET_RegistryNonNodeMatcher:
+    return "Matcher not a node matcher: $0";
+  case Diagnostics::ET_RegistryMatcherNoWithSupport:
+    return "Matcher does not support with call.";
 
   case Diagnostics::ET_ParserStringError:
     return "Error parsing string token: <$0>";
@@ -123,6 +127,10 @@ static StringRef errorTypeToFormatString(Diagnostics::ErrorType Type) {
     return "Error parsing numeric literal: <$0>";
   case Diagnostics::ET_ParserOverloadedType:
     return "Input value has unresolved overloaded type: $0";
+  case Diagnostics::ET_ParserMalformedChainedExpr:
+    return "Period not followed by valid chained call.";
+  case Diagnostics::ET_ParserFailedToBuildMatcher:
+    return "Failed to build matcher: $0.";
 
   case Diagnostics::ET_None:
     return "<N/A>";

diff  --git a/clang/lib/ASTMatchers/Dynamic/Parser.cpp b/clang/lib/ASTMatchers/Dynamic/Parser.cpp
index efe581284fb9..c6a77bb6c2e0 100644
--- a/clang/lib/ASTMatchers/Dynamic/Parser.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Parser.cpp
@@ -52,6 +52,7 @@ struct Parser::TokenInfo {
 
   /// Some known identifiers.
   static const char* const ID_Bind;
+  static const char *const ID_With;
 
   TokenInfo() = default;
 
@@ -62,6 +63,7 @@ struct Parser::TokenInfo {
 };
 
 const char* const Parser::TokenInfo::ID_Bind = "bind";
+const char *const Parser::TokenInfo::ID_With = "with";
 
 /// Simple tokenizer for the parser.
 class Parser::CodeTokenizer {
@@ -367,14 +369,26 @@ bool Parser::parseIdentifierPrefixImpl(VariantValue *Value) {
 
       std::string BindID;
       Tokenizer->consumeNextToken();
-      TokenInfo BindToken = Tokenizer->consumeNextToken();
-      if (BindToken.Kind == TokenInfo::TK_CodeCompletion) {
-        addCompletion(BindToken, MatcherCompletion("bind(\"", "bind", 1));
+      TokenInfo ChainCallToken = Tokenizer->consumeNextToken();
+      if (ChainCallToken.Kind == TokenInfo::TK_CodeCompletion) {
+        addCompletion(ChainCallToken, MatcherCompletion("bind(\"", "bind", 1));
         return false;
       }
-      if (BindToken.Kind != TokenInfo::TK_Ident ||
-        BindToken.Text != TokenInfo::ID_Bind) {
-        Error->addError(BindToken.Range, Error->ET_ParserMalformedBindExpr);
+
+      if (ChainCallToken.Kind != TokenInfo::TK_Ident ||
+          (ChainCallToken.Text != TokenInfo::ID_Bind &&
+           ChainCallToken.Text != TokenInfo::ID_With)) {
+        Error->addError(ChainCallToken.Range,
+                        Error->ET_ParserMalformedChainedExpr);
+        return false;
+      }
+      if (ChainCallToken.Text == TokenInfo::ID_With) {
+
+        Diagnostics::Context Ctx(Diagnostics::Context::ConstructMatcher, Error,
+                                 NameToken.Text, NameToken.Range);
+
+        Error->addError(ChainCallToken.Range,
+                        Error->ET_RegistryMatcherNoWithSupport);
         return false;
       }
       if (!parseBindID(BindID))
@@ -454,6 +468,160 @@ bool Parser::parseBindID(std::string &BindID) {
   return true;
 }
 
+bool Parser::parseMatcherBuilder(MatcherCtor Ctor, const TokenInfo &NameToken,
+                                 const TokenInfo &OpenToken,
+                                 VariantValue *Value) {
+  std::vector<ParserValue> Args;
+  TokenInfo EndToken;
+
+  Tokenizer->SkipNewlines();
+
+  {
+    ScopedContextEntry SCE(this, Ctor);
+
+    while (Tokenizer->nextTokenKind() != TokenInfo::TK_Eof) {
+      if (Tokenizer->nextTokenKind() == TokenInfo::TK_CloseParen) {
+        // End of args.
+        EndToken = Tokenizer->consumeNextToken();
+        break;
+      }
+      if (!Args.empty()) {
+        // We must find a , token to continue.
+        TokenInfo CommaToken = Tokenizer->consumeNextToken();
+        if (CommaToken.Kind != TokenInfo::TK_Comma) {
+          Error->addError(CommaToken.Range, Error->ET_ParserNoComma)
+              << CommaToken.Text;
+          return false;
+        }
+      }
+
+      Diagnostics::Context Ctx(Diagnostics::Context::MatcherArg, Error,
+                               NameToken.Text, NameToken.Range,
+                               Args.size() + 1);
+      ParserValue ArgValue;
+      Tokenizer->SkipNewlines();
+
+      if (Tokenizer->peekNextToken().Kind == TokenInfo::TK_CodeCompletion) {
+        addExpressionCompletions();
+        return false;
+      }
+
+      TokenInfo NodeMatcherToken = Tokenizer->consumeNextToken();
+
+      if (NodeMatcherToken.Kind != TokenInfo::TK_Ident) {
+        Error->addError(NameToken.Range, Error->ET_ParserFailedToBuildMatcher)
+            << NameToken.Text;
+        return false;
+      }
+
+      ArgValue.Text = NodeMatcherToken.Text;
+      ArgValue.Range = NodeMatcherToken.Range;
+
+      llvm::Optional<MatcherCtor> MappedMatcher =
+          S->lookupMatcherCtor(ArgValue.Text);
+
+      if (!MappedMatcher) {
+        Error->addError(NodeMatcherToken.Range,
+                        Error->ET_RegistryMatcherNotFound)
+            << NodeMatcherToken.Text;
+        return false;
+      }
+
+      ASTNodeKind NK = S->nodeMatcherType(*MappedMatcher);
+
+      if (NK.isNone()) {
+        Error->addError(NodeMatcherToken.Range,
+                        Error->ET_RegistryNonNodeMatcher)
+            << NodeMatcherToken.Text;
+        return false;
+      }
+
+      ArgValue.Value = NK;
+
+      Tokenizer->SkipNewlines();
+      Args.push_back(ArgValue);
+
+      SCE.nextArg();
+    }
+  }
+
+  if (EndToken.Kind == TokenInfo::TK_Eof) {
+    Error->addError(OpenToken.Range, Error->ET_ParserNoCloseParen);
+    return false;
+  }
+
+  internal::MatcherDescriptorPtr BuiltCtor =
+      S->buildMatcherCtor(Ctor, NameToken.Range, Args, Error);
+
+  if (!BuiltCtor.get()) {
+    Error->addError(NameToken.Range, Error->ET_ParserFailedToBuildMatcher)
+        << NameToken.Text;
+    return false;
+  }
+
+  std::string BindID;
+  if (Tokenizer->peekNextToken().Kind == TokenInfo::TK_Period) {
+    Tokenizer->consumeNextToken();
+    TokenInfo ChainCallToken = Tokenizer->consumeNextToken();
+    if (ChainCallToken.Kind == TokenInfo::TK_CodeCompletion) {
+      addCompletion(ChainCallToken, MatcherCompletion("bind(\"", "bind", 1));
+      addCompletion(ChainCallToken, MatcherCompletion("with(", "with", 1));
+      return false;
+    }
+    if (ChainCallToken.Kind != TokenInfo::TK_Ident ||
+        (ChainCallToken.Text != TokenInfo::ID_Bind &&
+         ChainCallToken.Text != TokenInfo::ID_With)) {
+      Error->addError(ChainCallToken.Range,
+                      Error->ET_ParserMalformedChainedExpr);
+      return false;
+    }
+    if (ChainCallToken.Text == TokenInfo::ID_Bind) {
+      if (!parseBindID(BindID))
+        return false;
+      Diagnostics::Context Ctx(Diagnostics::Context::ConstructMatcher, Error,
+                               NameToken.Text, NameToken.Range);
+      SourceRange MatcherRange = NameToken.Range;
+      MatcherRange.End = ChainCallToken.Range.End;
+      VariantMatcher Result = S->actOnMatcherExpression(
+          BuiltCtor.get(), MatcherRange, BindID, {}, Error);
+      if (Result.isNull())
+        return false;
+
+      *Value = Result;
+      return true;
+    } else if (ChainCallToken.Text == TokenInfo::ID_With) {
+      Tokenizer->SkipNewlines();
+
+      if (Tokenizer->nextTokenKind() != TokenInfo::TK_OpenParen) {
+        StringRef ErrTxt = Tokenizer->nextTokenKind() == TokenInfo::TK_Eof
+                               ? StringRef("EOF")
+                               : Tokenizer->peekNextToken().Text;
+        Error->addError(Tokenizer->peekNextToken().Range,
+                        Error->ET_ParserNoOpenParen)
+            << ErrTxt;
+        return false;
+      }
+
+      TokenInfo WithOpenToken = Tokenizer->consumeNextToken();
+
+      return parseMatcherExpressionImpl(NameToken, WithOpenToken,
+                                        BuiltCtor.get(), Value);
+    }
+  }
+
+  Diagnostics::Context Ctx(Diagnostics::Context::ConstructMatcher, Error,
+                           NameToken.Text, NameToken.Range);
+  SourceRange MatcherRange = NameToken.Range;
+  MatcherRange.End = EndToken.Range.End;
+  VariantMatcher Result = S->actOnMatcherExpression(
+      BuiltCtor.get(), MatcherRange, BindID, {}, Error);
+  if (Result.isNull())
+    return false;
+
+  *Value = Result;
+  return true;
+}
+
 /// Parse and validate a matcher expression.
 /// \return \c true on success, in which case \c Value has the matcher parsed.
 ///   If the input is malformed, or some argument has an error, it
@@ -468,6 +636,9 @@ bool Parser::parseMatcherExpressionImpl(const TokenInfo &NameToken,
     // Do not return here. We need to continue to give completion suggestions.
   }
 
+  if (Ctor && *Ctor && S->isBuilderMatcher(*Ctor))
+    return parseMatcherBuilder(*Ctor, NameToken, OpenToken, Value);
+
   std::vector<ParserValue> Args;
   TokenInfo EndToken;
 
@@ -517,14 +688,29 @@ bool Parser::parseMatcherExpressionImpl(const TokenInfo &NameToken,
   std::string BindID;
   if (Tokenizer->peekNextToken().Kind == TokenInfo::TK_Period) {
     Tokenizer->consumeNextToken();
-    TokenInfo BindToken = Tokenizer->consumeNextToken();
-    if (BindToken.Kind == TokenInfo::TK_CodeCompletion) {
-      addCompletion(BindToken, MatcherCompletion("bind(\"", "bind", 1));
+    TokenInfo ChainCallToken = Tokenizer->consumeNextToken();
+    if (ChainCallToken.Kind == TokenInfo::TK_CodeCompletion) {
+      addCompletion(ChainCallToken, MatcherCompletion("bind(\"", "bind", 1));
+      return false;
+    }
+
+    if (ChainCallToken.Kind != TokenInfo::TK_Ident) {
+      Error->addError(ChainCallToken.Range,
+                      Error->ET_ParserMalformedChainedExpr);
+      return false;
+    }
+    if (ChainCallToken.Text == TokenInfo::ID_With) {
+
+      Diagnostics::Context Ctx(Diagnostics::Context::ConstructMatcher, Error,
+                               NameToken.Text, NameToken.Range);
+
+      Error->addError(ChainCallToken.Range,
+                      Error->ET_RegistryMatcherNoWithSupport);
       return false;
     }
-    if (BindToken.Kind != TokenInfo::TK_Ident ||
-      BindToken.Text != TokenInfo::ID_Bind) {
-      Error->addError(BindToken.Range, Error->ET_ParserMalformedBindExpr);
+    if (ChainCallToken.Text != TokenInfo::ID_Bind) {
+      Error->addError(ChainCallToken.Range,
+                      Error->ET_ParserMalformedChainedExpr);
       return false;
     }
     if (!parseBindID(BindID))
@@ -668,6 +854,21 @@ std::vector<MatcherCompletion> Parser::RegistrySema::getMatcherCompletions(
   return Registry::getMatcherCompletions(AcceptedTypes);
 }
 
+bool Parser::RegistrySema::isBuilderMatcher(MatcherCtor Ctor) const {
+  return Registry::isBuilderMatcher(Ctor);
+}
+
+ASTNodeKind Parser::RegistrySema::nodeMatcherType(MatcherCtor Ctor) const {
+  return Registry::nodeMatcherType(Ctor);
+}
+
+internal::MatcherDescriptorPtr
+Parser::RegistrySema::buildMatcherCtor(MatcherCtor Ctor, SourceRange NameRange,
+                                       ArrayRef<ParserValue> Args,
+                                       Diagnostics *Error) const {
+  return Registry::buildMatcherCtor(Ctor, NameRange, Args, Error);
+}
+
 bool Parser::parseExpression(StringRef &Code, Sema *S,
                              const NamedValueMap *NamedValues,
                              VariantValue *Value, Diagnostics *Error) {

diff  --git a/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
index 6fc720ed982f..744be79d87ff 100644
--- a/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
+++ b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
@@ -32,6 +32,17 @@ class MockSema : public Parser::Sema {
     return M.getID().second;
   }
 
+  bool isBuilderMatcher(MatcherCtor) const override { return false; }
+
+  ASTNodeKind nodeMatcherType(MatcherCtor) const override { return {}; }
+
+  internal::MatcherDescriptorPtr
+  buildMatcherCtor(MatcherCtor, SourceRange NameRange,
+                   ArrayRef<ParserValue> Args,
+                   Diagnostics *Error) const override {
+    return internal::MatcherDescriptorPtr{nullptr};
+  }
+
   void parse(StringRef Code) {
     Diagnostics Error;
     VariantValue Value;
@@ -329,7 +340,7 @@ TEST(ParserTest, Errors) {
             "1:5: Invalid token <(> found when looking for a value.",
             ParseWithError("Foo(("));
   EXPECT_EQ("1:7: Expected end of code.", ParseWithError("expr()a"));
-  EXPECT_EQ("1:11: Malformed bind() expression.",
+  EXPECT_EQ("1:11: Period not followed by valid chained call.",
             ParseWithError("isArrow().biind"));
   EXPECT_EQ("1:15: Malformed bind() expression.",
             ParseWithError("isArrow().bind"));
@@ -340,6 +351,20 @@ TEST(ParserTest, Errors) {
   EXPECT_EQ("1:1: Error building matcher isArrow.\n"
             "1:1: Matcher does not support binding.",
             ParseWithError("isArrow().bind(\"foo\")"));
+  EXPECT_EQ("1:1: Error building matcher isArrow.\n"
+            "1:11: Matcher does not support with call.",
+            ParseWithError("isArrow().with"));
+  EXPECT_EQ(
+      "1:22: Error parsing matcher. Found token <EOF> while looking for '('.",
+      ParseWithError("mapAnyOf(ifStmt).with"));
+  EXPECT_EQ(
+      "1:22: Error parsing matcher. Found end-of-code while looking for ')'.",
+      ParseWithError("mapAnyOf(ifStmt).with("));
+  EXPECT_EQ("1:1: Failed to build matcher: mapAnyOf.",
+            ParseWithError("mapAnyOf()"));
+  EXPECT_EQ("1:1: Error parsing argument 1 for matcher mapAnyOf.\n1:1: Failed "
+            "to build matcher: mapAnyOf.",
+            ParseWithError("mapAnyOf(\"foo\")"));
   EXPECT_EQ("Input value has unresolved overloaded type: "
             "Matcher<DoStmt|ForStmt|WhileStmt|CXXForRangeStmt|FunctionDecl>",
             ParseMatcherWithError("hasBody(stmt())"));
@@ -470,7 +495,8 @@ decl()))matcher";
 )matcher";
     M = Parser::parseMatcherExpression(Code, nullptr, &NamedValues, &Error);
     EXPECT_FALSE(M.hasValue());
-    EXPECT_EQ("1:11: Malformed bind() expression.", Error.toStringFull());
+    EXPECT_EQ("1:11: Period not followed by valid chained call.",
+              Error.toStringFull());
   }
 
   {


        


More information about the cfe-commits mailing list