[clang] 522ee29 - Allow newlines in AST Matchers in clang-query files

Evgenii Stepanov via cfe-commits cfe-commits at lists.llvm.org
Thu Dec 26 18:10:29 PST 2019


Reverted in
https://github.com/llvm/llvm-project/commit/5ca97d0defeed38feec2352692f6bb80297d6712

On Thu, Dec 26, 2019 at 12:41 PM Stephen Kelly via cfe-commits
<cfe-commits at lists.llvm.org> wrote:
>
>
> Author: Stephen Kelly
> Date: 2019-12-26T20:40:33Z
> New Revision: 522ee29a4fb3814db604b585c8637247477ec057
>
> URL: https://github.com/llvm/llvm-project/commit/522ee29a4fb3814db604b585c8637247477ec057
> DIFF: https://github.com/llvm/llvm-project/commit/522ee29a4fb3814db604b585c8637247477ec057.diff
>
> LOG: Allow newlines in AST Matchers in clang-query files
>
> Reviewers: aaron.ballman
>
> Subscribers: cfe-commits
>
> Tags: #clang
>
> Differential Revision: https://reviews.llvm.org/D71842
>
> Added:
>
>
> Modified:
>     clang-tools-extra/clang-query/Query.cpp
>     clang-tools-extra/clang-query/Query.h
>     clang-tools-extra/clang-query/QueryParser.cpp
>     clang-tools-extra/clang-query/tool/ClangQuery.cpp
>     clang-tools-extra/unittests/clang-query/QueryParserTest.cpp
>     clang/include/clang/ASTMatchers/Dynamic/Parser.h
>     clang/lib/ASTMatchers/Dynamic/Parser.cpp
>     clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
>
> Removed:
>
>
>
> ################################################################################
> diff  --git a/clang-tools-extra/clang-query/Query.cpp b/clang-tools-extra/clang-query/Query.cpp
> index 675fd968f46e..8eafc5eed750 100644
> --- a/clang-tools-extra/clang-query/Query.cpp
> +++ b/clang-tools-extra/clang-query/Query.cpp
> @@ -101,9 +101,24 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
>      Finder.matchAST(AST->getASTContext());
>
>      if (QS.PrintMatcher) {
> -      std::string prefixText = "Matcher: ";
> -      OS << "\n  " << prefixText << Source << "\n";
> -      OS << "  " << std::string(prefixText.size() + Source.size(), '=') << '\n';
> +      SmallVector<StringRef, 4> Lines;
> +      Source.split(Lines, "\n");
> +      auto FirstLine = Lines[0];
> +      Lines.erase(Lines.begin(), Lines.begin() + 1);
> +      while (!Lines.empty() && Lines.back().empty()) {
> +        Lines.resize(Lines.size() - 1);
> +      }
> +      unsigned MaxLength = FirstLine.size();
> +      std::string PrefixText = "Matcher: ";
> +      OS << "\n  " << PrefixText << FirstLine;
> +
> +      for (auto Line : Lines) {
> +        OS << "\n" << std::string(PrefixText.size() + 2, ' ') << Line;
> +        MaxLength = std::max<int>(MaxLength, Line.rtrim().size());
> +      }
> +
> +      OS << "\n"
> +         << "  " << std::string(PrefixText.size() + MaxLength, '=') << "\n\n";
>      }
>
>      for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
>
> diff  --git a/clang-tools-extra/clang-query/Query.h b/clang-tools-extra/clang-query/Query.h
> index 56af486984ee..78bcbc79cdf8 100644
> --- a/clang-tools-extra/clang-query/Query.h
> +++ b/clang-tools-extra/clang-query/Query.h
> @@ -44,6 +44,7 @@ struct Query : llvm::RefCountedBase<Query> {
>    /// \return false if an error occurs, otherwise return true.
>    virtual bool run(llvm::raw_ostream &OS, QuerySession &QS) const = 0;
>
> +  StringRef RemainingContent;
>    const QueryKind Kind;
>  };
>
>
> diff  --git a/clang-tools-extra/clang-query/QueryParser.cpp b/clang-tools-extra/clang-query/QueryParser.cpp
> index 4da2f5da79d4..db4b9a4b0530 100644
> --- a/clang-tools-extra/clang-query/QueryParser.cpp
> +++ b/clang-tools-extra/clang-query/QueryParser.cpp
> @@ -26,7 +26,10 @@ namespace query {
>  // is found before End, return StringRef().  Begin is adjusted to exclude the
>  // lexed region.
>  StringRef QueryParser::lexWord() {
> -  Line = Line.ltrim();
> +  Line = Line.drop_while([this](char c) {
> +    // Don't trim newlines.
> +    return StringRef(" \t\v\f\r").contains(c);
> +  });
>
>    if (Line.empty())
>      // Even though the Line is empty, it contains a pointer and
> @@ -34,12 +37,12 @@ StringRef QueryParser::lexWord() {
>      // code completion.
>      return Line;
>
> -  if (Line.front() == '#') {
> -    Line = {};
> -    return StringRef();
> -  }
> +  StringRef Word;
> +  if (Line.front() == '#')
> +    Word = Line.substr(0, 1);
> +  else
> +    Word = Line.take_until(isWhitespace);
>
> -  StringRef Word = Line.take_until(isWhitespace);
>    Line = Line.drop_front(Word.size());
>    return Word;
>  }
> @@ -125,9 +128,25 @@ template <typename QueryType> QueryRef QueryParser::parseSetOutputKind() {
>  }
>
>  QueryRef QueryParser::endQuery(QueryRef Q) {
> -  const StringRef Extra = Line;
> -  if (!lexWord().empty())
> -    return new InvalidQuery("unexpected extra input: '" + Extra + "'");
> +  StringRef Extra = Line;
> +  StringRef ExtraTrimmed = Extra.drop_while(
> +      [](char c) { return StringRef(" \t\v\f\r").contains(c); });
> +
> +  if ((!ExtraTrimmed.empty() && ExtraTrimmed[0] == '\n') ||
> +      (ExtraTrimmed.size() >= 2 && ExtraTrimmed[0] == '\r' &&
> +       ExtraTrimmed[1] == '\n'))
> +    Q->RemainingContent = Extra;
> +  else {
> +    StringRef TrailingWord = lexWord();
> +    if (!TrailingWord.empty() && TrailingWord.front() == '#') {
> +      Line = Line.drop_until([](char c) { return c == '\n'; });
> +      Line = Line.drop_while([](char c) { return c == '\n'; });
> +      return endQuery(Q);
> +    }
> +    if (!TrailingWord.empty()) {
> +      return new InvalidQuery("unexpected extra input: '" + Extra + "'");
> +    }
> +  }
>    return Q;
>  }
>
> @@ -193,7 +212,11 @@ QueryRef QueryParser::doParse() {
>    switch (QKind) {
>    case PQK_Comment:
>    case PQK_NoOp:
> -    return new NoOpQuery;
> +    Line = Line.drop_until([](char c) { return c == '\n'; });
> +    Line = Line.drop_while([](char c) { return c == '\n'; });
> +    if (Line.empty())
> +      return new NoOpQuery;
> +    return doParse();
>
>    case PQK_Help:
>      return endQuery(new HelpQuery);
> @@ -217,7 +240,9 @@ QueryRef QueryParser::doParse() {
>        return makeInvalidQueryFromDiagnostics(Diag);
>      }
>
> -    return new LetQuery(Name, Value);
> +    auto *Q = new LetQuery(Name, Value);
> +    Q->RemainingContent = Line;
> +    return Q;
>    }
>
>    case PQK_Match: {
> @@ -226,12 +251,17 @@ QueryRef QueryParser::doParse() {
>
>      Diagnostics Diag;
>      auto MatcherSource = Line.trim();
> +    auto OrigMatcherSource = MatcherSource;
>      Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
>          MatcherSource, nullptr, &QS.NamedValues, &Diag);
>      if (!Matcher) {
>        return makeInvalidQueryFromDiagnostics(Diag);
>      }
> -    return new MatchQuery(MatcherSource, *Matcher);
> +    auto ActualSource = OrigMatcherSource.slice(0, OrigMatcherSource.size() -
> +                                                       MatcherSource.size());
> +    auto *Q = new MatchQuery(ActualSource, *Matcher);
> +    Q->RemainingContent = MatcherSource;
> +    return Q;
>    }
>
>    case PQK_Set: {
>
> diff  --git a/clang-tools-extra/clang-query/tool/ClangQuery.cpp b/clang-tools-extra/clang-query/tool/ClangQuery.cpp
> index 80e1c602796c..5cfa0acf9120 100644
> --- a/clang-tools-extra/clang-query/tool/ClangQuery.cpp
> +++ b/clang-tools-extra/clang-query/tool/ClangQuery.cpp
> @@ -69,13 +69,16 @@ bool runCommandsInFile(const char *ExeName, std::string const &FileName,
>      llvm::errs() << ExeName << ": cannot open " << FileName << "\n";
>      return 1;
>    }
> -  while (Input.good()) {
> -    std::string Line;
> -    std::getline(Input, Line);
>
> -    QueryRef Q = QueryParser::parse(Line, QS);
> +  std::string FileContent((std::istreambuf_iterator<char>(Input)),
> +                          std::istreambuf_iterator<char>());
> +
> +  StringRef FileContentRef(FileContent);
> +  while (!FileContentRef.empty()) {
> +    QueryRef Q = QueryParser::parse(FileContentRef, QS);
>      if (!Q->run(llvm::outs(), QS))
>        return true;
> +    FileContentRef = Q->RemainingContent;
>    }
>    return false;
>  }
>
> diff  --git a/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp b/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp
> index 01c65452b03f..4725789f29f2 100644
> --- a/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp
> +++ b/clang-tools-extra/unittests/clang-query/QueryParserTest.cpp
> @@ -230,3 +230,104 @@ TEST_F(QueryParserTest, Complete) {
>    EXPECT_EQ("et ", Comps[0].TypedText);
>    EXPECT_EQ("let", Comps[0].DisplayText);
>  }
> +
> +TEST_F(QueryParserTest, Multiline) {
> +
> +  // Single string with multiple commands
> +  QueryRef Q = parse(R"matcher(
> +set bind-root false
> +set output dump
> +    )matcher");
> +
> +  ASSERT_TRUE(isa<SetQuery<bool>>(Q));
> +
> +  Q = parse(Q->RemainingContent);
> +  ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));
> +
> +  // Missing newline
> +  Q = parse(R"matcher(
> +set bind-root false set output dump
> +    )matcher");
> +
> +  ASSERT_TRUE(isa<InvalidQuery>(Q));
> +  EXPECT_EQ("unexpected extra input: ' set output dump\n    '",
> +            cast<InvalidQuery>(Q)->ErrStr);
> +
> +  // Commands which do their own parsing
> +  Q = parse(R"matcher(
> +let fn functionDecl(hasName("foo"))
> +match callExpr(callee(functionDecl()))
> +    )matcher");
> +
> +  ASSERT_TRUE(isa<LetQuery>(Q));
> +
> +  Q = parse(Q->RemainingContent);
> +  ASSERT_TRUE(isa<MatchQuery>(Q));
> +
> +  // Multi-line matcher
> +  Q = parse(R"matcher(
> +match callExpr(callee(
> +    functionDecl().bind("fn")
> +    ))
> +
> +    )matcher");
> +
> +  ASSERT_TRUE(isa<MatchQuery>(Q));
> +
> +  // Comment locations
> +  Q = parse(R"matcher(
> +#nospacecomment
> +# Leading comment
> +match callExpr ( # Trailing comment
> +            # Comment alone on line
> +
> +            callee(
> +            functionDecl(
> +            ).bind(
> +            "fn"
> +            )
> +            )) # Comment trailing close
> +# Comment after match
> +    )matcher");
> +
> +  ASSERT_TRUE(isa<MatchQuery>(Q));
> +
> +  // \r\n
> +  Q = parse("set bind-root false\r\nset output dump");
> +
> +  ASSERT_TRUE(isa<SetQuery<bool>>(Q));
> +
> +  Q = parse(Q->RemainingContent);
> +  ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));
> +
> +  // Leading and trailing space in lines
> +  Q = parse("  set bind-root false  \r\n  set output dump  ");
> +
> +  ASSERT_TRUE(isa<SetQuery<bool>>(Q));
> +
> +  Q = parse(Q->RemainingContent);
> +  ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));
> +
> +  // Incomplete commands
> +  Q = parse("set\nbind-root false");
> +
> +  ASSERT_TRUE(isa<InvalidQuery>(Q));
> +  EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
> +
> +  Q = parse("set bind-root\nfalse");
> +
> +  ASSERT_TRUE(isa<InvalidQuery>(Q));
> +  EXPECT_EQ("expected 'true' or 'false', got ''",
> +            cast<InvalidQuery>(Q)->ErrStr);
> +
> +  Q = parse(R"matcher(
> +match callExpr
> +(
> +)
> +    )matcher");
> +
> +  ASSERT_TRUE(isa<InvalidQuery>(Q));
> +  EXPECT_EQ("1:9: Error parsing matcher. Found token <NewLine> "
> +            "while looking for '('.",
> +            cast<InvalidQuery>(Q)->ErrStr);
> +}
>
> diff  --git a/clang/include/clang/ASTMatchers/Dynamic/Parser.h b/clang/include/clang/ASTMatchers/Dynamic/Parser.h
> index 15e0aa7ecd27..70bbe816accd 100644
> --- a/clang/include/clang/ASTMatchers/Dynamic/Parser.h
> +++ b/clang/include/clang/ASTMatchers/Dynamic/Parser.h
> @@ -164,16 +164,14 @@ class Parser {
>    ///   description of the error.
>    ///   The caller takes ownership of the DynTypedMatcher object returned.
>    static llvm::Optional<DynTypedMatcher>
> -  parseMatcherExpression(StringRef MatcherCode, Sema *S,
> -                         const NamedValueMap *NamedValues,
> -                         Diagnostics *Error);
> +  parseMatcherExpression(StringRef &MatcherCode, Sema *S,
> +                         const NamedValueMap *NamedValues, Diagnostics *Error);
>    static llvm::Optional<DynTypedMatcher>
> -  parseMatcherExpression(StringRef MatcherCode, Sema *S,
> -                         Diagnostics *Error) {
> +  parseMatcherExpression(StringRef &MatcherCode, Sema *S, Diagnostics *Error) {
>      return parseMatcherExpression(MatcherCode, S, nullptr, Error);
>    }
>    static llvm::Optional<DynTypedMatcher>
> -  parseMatcherExpression(StringRef MatcherCode, Diagnostics *Error) {
> +  parseMatcherExpression(StringRef &MatcherCode, Diagnostics *Error) {
>      return parseMatcherExpression(MatcherCode, nullptr, Error);
>    }
>
> @@ -189,14 +187,14 @@ class Parser {
>    /// \param NamedValues A map of precomputed named values.  This provides
>    ///   the dictionary for the <NamedValue> rule of the grammar.
>    ///   If null, it is ignored.
> -  static bool parseExpression(StringRef Code, Sema *S,
> +  static bool parseExpression(StringRef &Code, Sema *S,
>                                const NamedValueMap *NamedValues,
>                                VariantValue *Value, Diagnostics *Error);
> -  static bool parseExpression(StringRef Code, Sema *S,
> -                              VariantValue *Value, Diagnostics *Error) {
> +  static bool parseExpression(StringRef &Code, Sema *S, VariantValue *Value,
> +                              Diagnostics *Error) {
>      return parseExpression(Code, S, nullptr, Value, Error);
>    }
> -  static bool parseExpression(StringRef Code, VariantValue *Value,
> +  static bool parseExpression(StringRef &Code, VariantValue *Value,
>                                Diagnostics *Error) {
>      return parseExpression(Code, nullptr, Value, Error);
>    }
> @@ -213,14 +211,14 @@ class Parser {
>    /// \return The list of completions, which may be empty if there are no
>    /// available completions or if an error occurred.
>    static std::vector<MatcherCompletion>
> -  completeExpression(StringRef Code, unsigned CompletionOffset, Sema *S,
> +  completeExpression(StringRef &Code, unsigned CompletionOffset, Sema *S,
>                       const NamedValueMap *NamedValues);
>    static std::vector<MatcherCompletion>
> -  completeExpression(StringRef Code, unsigned CompletionOffset, Sema *S) {
> +  completeExpression(StringRef &Code, unsigned CompletionOffset, Sema *S) {
>      return completeExpression(Code, CompletionOffset, S, nullptr);
>    }
>    static std::vector<MatcherCompletion>
> -  completeExpression(StringRef Code, unsigned CompletionOffset) {
> +  completeExpression(StringRef &Code, unsigned CompletionOffset) {
>      return completeExpression(Code, CompletionOffset, nullptr);
>    }
>
>
> diff  --git a/clang/lib/ASTMatchers/Dynamic/Parser.cpp b/clang/lib/ASTMatchers/Dynamic/Parser.cpp
> index e3b00b46832c..1781f2a6439f 100644
> --- a/clang/lib/ASTMatchers/Dynamic/Parser.cpp
> +++ b/clang/lib/ASTMatchers/Dynamic/Parser.cpp
> @@ -38,6 +38,7 @@ struct Parser::TokenInfo {
>    /// Different possible tokens.
>    enum TokenKind {
>      TK_Eof,
> +    TK_NewLine,
>      TK_OpenParen,
>      TK_CloseParen,
>      TK_Comma,
> @@ -65,12 +66,12 @@ const char* const Parser::TokenInfo::ID_Bind = "bind";
>  /// Simple tokenizer for the parser.
>  class Parser::CodeTokenizer {
>  public:
> -  explicit CodeTokenizer(StringRef MatcherCode, Diagnostics *Error)
> +  explicit CodeTokenizer(StringRef &MatcherCode, Diagnostics *Error)
>        : Code(MatcherCode), StartOfLine(MatcherCode), Error(Error) {
>      NextToken = getNextToken();
>    }
>
> -  CodeTokenizer(StringRef MatcherCode, Diagnostics *Error,
> +  CodeTokenizer(StringRef &MatcherCode, Diagnostics *Error,
>                  unsigned CodeCompletionOffset)
>        : Code(MatcherCode), StartOfLine(MatcherCode), Error(Error),
>          CodeCompletionLocation(MatcherCode.data() + CodeCompletionOffset) {
> @@ -87,6 +88,19 @@ class Parser::CodeTokenizer {
>      return ThisToken;
>    }
>
> +  TokenInfo SkipNewlines() {
> +    while (NextToken.Kind == TokenInfo::TK_NewLine)
> +      NextToken = getNextToken();
> +    return NextToken;
> +  }
> +
> +  TokenInfo consumeNextTokenIgnoreNewlines() {
> +    SkipNewlines();
> +    if (NextToken.Kind == TokenInfo::TK_Eof)
> +      return NextToken;
> +    return consumeNextToken();
> +  }
> +
>    TokenInfo::TokenKind nextTokenKind() const { return NextToken.Kind; }
>
>  private:
> @@ -110,9 +124,8 @@ class Parser::CodeTokenizer {
>
>      switch (Code[0]) {
>      case '#':
> -      Result.Kind = TokenInfo::TK_Eof;
> -      Result.Text = "";
> -      return Result;
> +      Code = Code.drop_until([](char c) { return c == '\n'; });
> +      return getNextToken();
>      case ',':
>        Result.Kind = TokenInfo::TK_Comma;
>        Result.Text = Code.substr(0, 1);
> @@ -123,6 +136,13 @@ class Parser::CodeTokenizer {
>        Result.Text = Code.substr(0, 1);
>        Code = Code.drop_front();
>        break;
> +    case '\n':
> +      ++Line;
> +      StartOfLine = Code.drop_front();
> +      Result.Kind = TokenInfo::TK_NewLine;
> +      Result.Text = Code.substr(0, 1);
> +      Code = Code.drop_front();
> +      break;
>      case '(':
>        Result.Kind = TokenInfo::TK_OpenParen;
>        Result.Text = Code.substr(0, 1);
> @@ -277,13 +297,10 @@ class Parser::CodeTokenizer {
>
>    /// Consume all leading whitespace from \c Code.
>    void consumeWhitespace() {
> -    while (!Code.empty() && isWhitespace(Code[0])) {
> -      if (Code[0] == '\n') {
> -        ++Line;
> -        StartOfLine = Code.drop_front();
> -      }
> -      Code = Code.drop_front();
> -    }
> +    Code = Code.drop_while([this](char c) {
> +      // Don't trim newlines.
> +      return StringRef(" \t\v\f\r").contains(c);
> +    });
>    }
>
>    SourceLocation currentLocation() {
> @@ -293,7 +310,7 @@ class Parser::CodeTokenizer {
>      return Location;
>    }
>
> -  StringRef Code;
> +  StringRef &Code;
>    StringRef StartOfLine;
>    unsigned Line = 1;
>    Diagnostics *Error;
> @@ -337,6 +354,13 @@ struct Parser::ScopedContextEntry {
>  bool Parser::parseIdentifierPrefixImpl(VariantValue *Value) {
>    const TokenInfo NameToken = Tokenizer->consumeNextToken();
>
> +  if (Tokenizer->nextTokenKind() == TokenInfo::TK_NewLine) {
> +    Error->addError(Tokenizer->peekNextToken().Range,
> +                    Error->ET_ParserNoOpenParen)
> +        << "NewLine";
> +    return false;
> +  }
> +
>    if (Tokenizer->nextTokenKind() != TokenInfo::TK_OpenParen) {
>      // Parse as a named value.
>      if (const VariantValue NamedValue =
> @@ -368,6 +392,7 @@ bool Parser::parseIdentifierPrefixImpl(VariantValue *Value) {
>      // unknown named value.
>      if ((Tokenizer->nextTokenKind() == TokenInfo::TK_Comma ||
>           Tokenizer->nextTokenKind() == TokenInfo::TK_CloseParen ||
> +         Tokenizer->nextTokenKind() == TokenInfo::TK_NewLine ||
>           Tokenizer->nextTokenKind() == TokenInfo::TK_Eof) &&
>          !S->lookupMatcherCtor(NameToken.Text)) {
>        Error->addError(NameToken.Range, Error->ET_RegistryValueNotFound)
> @@ -377,6 +402,8 @@ bool Parser::parseIdentifierPrefixImpl(VariantValue *Value) {
>      // Otherwise, fallback to the matcher parser.
>    }
>
> +  Tokenizer->SkipNewlines();
> +
>    // Parse as a matcher expression.
>    return parseMatcherExpressionImpl(NameToken, Value);
>  }
> @@ -392,8 +419,8 @@ bool Parser::parseBindID(std::string &BindID) {
>    }
>
>    const TokenInfo OpenToken = Tokenizer->consumeNextToken();
> -  const TokenInfo IDToken = Tokenizer->consumeNextToken();
> -  const TokenInfo CloseToken = Tokenizer->consumeNextToken();
> +  const TokenInfo IDToken = Tokenizer->consumeNextTokenIgnoreNewlines();
> +  const TokenInfo CloseToken = Tokenizer->consumeNextTokenIgnoreNewlines();
>
>    // TODO: We could use
> diff erent error codes for each/some to be more
>    //       explicit about the syntax error.
> @@ -443,6 +470,8 @@ bool Parser::parseMatcherExpressionImpl(const TokenInfo &NameToken,
>    std::vector<ParserValue> Args;
>    TokenInfo EndToken;
>
> +  Tokenizer->SkipNewlines();
> +
>    {
>      ScopedContextEntry SCE(this, Ctor ? *Ctor : nullptr);
>
> @@ -466,12 +495,14 @@ bool Parser::parseMatcherExpressionImpl(const TokenInfo &NameToken,
>                                 NameToken.Text, NameToken.Range,
>                                 Args.size() + 1);
>        ParserValue ArgValue;
> +      Tokenizer->SkipNewlines();
>        ArgValue.Text = Tokenizer->peekNextToken().Text;
>        ArgValue.Range = Tokenizer->peekNextToken().Range;
>        if (!parseExpressionImpl(&ArgValue.Value)) {
>          return false;
>        }
>
> +      Tokenizer->SkipNewlines();
>        Args.push_back(ArgValue);
>        SCE.nextArg();
>      }
> @@ -531,7 +562,7 @@ std::vector<MatcherCompletion> Parser::getNamedValueCompletions(
>  }
>
>  void Parser::addExpressionCompletions() {
> -  const TokenInfo CompToken = Tokenizer->consumeNextToken();
> +  const TokenInfo CompToken = Tokenizer->consumeNextTokenIgnoreNewlines();
>    assert(CompToken.Kind == TokenInfo::TK_CodeCompletion);
>
>    // We cannot complete code if there is an invalid element on the context
> @@ -575,7 +606,9 @@ bool Parser::parseExpressionImpl(VariantValue *Value) {
>    case TokenInfo::TK_Error:
>      // This error was already reported by the tokenizer.
>      return false;
> -
> +  case TokenInfo::TK_NewLine:
> +    assert(!"Newline should never be found here");
> +    return false;
>    case TokenInfo::TK_OpenParen:
>    case TokenInfo::TK_CloseParen:
>    case TokenInfo::TK_Comma:
> @@ -624,13 +657,14 @@ std::vector<MatcherCompletion> Parser::RegistrySema::getMatcherCompletions(
>    return Registry::getMatcherCompletions(AcceptedTypes);
>  }
>
> -bool Parser::parseExpression(StringRef Code, Sema *S,
> +bool Parser::parseExpression(StringRef &Code, Sema *S,
>                               const NamedValueMap *NamedValues,
>                               VariantValue *Value, Diagnostics *Error) {
>    CodeTokenizer Tokenizer(Code, Error);
>    if (!Parser(&Tokenizer, S, NamedValues, Error).parseExpressionImpl(Value))
>      return false;
> -  if (Tokenizer.peekNextToken().Kind != TokenInfo::TK_Eof) {
> +  auto NT = Tokenizer.peekNextToken();
> +  if (NT.Kind != TokenInfo::TK_Eof && NT.Kind != TokenInfo::TK_NewLine) {
>      Error->addError(Tokenizer.peekNextToken().Range,
>                      Error->ET_ParserTrailingCode);
>      return false;
> @@ -639,7 +673,7 @@ bool Parser::parseExpression(StringRef Code, Sema *S,
>  }
>
>  std::vector<MatcherCompletion>
> -Parser::completeExpression(StringRef Code, unsigned CompletionOffset, Sema *S,
> +Parser::completeExpression(StringRef &Code, unsigned CompletionOffset, Sema *S,
>                             const NamedValueMap *NamedValues) {
>    Diagnostics Error;
>    CodeTokenizer Tokenizer(Code, &Error, CompletionOffset);
> @@ -659,7 +693,7 @@ Parser::completeExpression(StringRef Code, unsigned CompletionOffset, Sema *S,
>  }
>
>  llvm::Optional<DynTypedMatcher>
> -Parser::parseMatcherExpression(StringRef Code, Sema *S,
> +Parser::parseMatcherExpression(StringRef &Code, Sema *S,
>                                 const NamedValueMap *NamedValues,
>                                 Diagnostics *Error) {
>    VariantValue Value;
>
> diff  --git a/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
> index db16ca418756..67fc70790296 100644
> --- a/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
> +++ b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
> @@ -207,10 +207,12 @@ Parser::NamedValueMap getTestNamedValues() {
>
>  TEST(ParserTest, FullParserTest) {
>    Diagnostics Error;
> -  llvm::Optional<DynTypedMatcher> VarDecl(Parser::parseMatcherExpression(
> +
> +  StringRef Code =
>        "varDecl(hasInitializer(binaryOperator(hasLHS(integerLiteral()),"
> -      "                                      hasOperatorName(\"+\"))))",
> -      &Error));
> +      "                                      hasOperatorName(\"+\"))))";
> +  llvm::Optional<DynTypedMatcher> VarDecl(
> +      Parser::parseMatcherExpression(Code, &Error));
>    EXPECT_EQ("", Error.toStringFull());
>    Matcher<Decl> M = VarDecl->unconditionalConvertTo<Decl>();
>    EXPECT_TRUE(matches("int x = 1 + false;", M));
> @@ -218,8 +220,9 @@ TEST(ParserTest, FullParserTest) {
>    EXPECT_FALSE(matches("int x = 1 - false;", M));
>    EXPECT_FALSE(matches("int x = true - 1;", M));
>
> -  llvm::Optional<DynTypedMatcher> HasParameter(Parser::parseMatcherExpression(
> -      "functionDecl(hasParameter(1, hasName(\"x\")))", &Error));
> +  Code = "functionDecl(hasParameter(1, hasName(\"x\")))";
> +  llvm::Optional<DynTypedMatcher> HasParameter(
> +      Parser::parseMatcherExpression(Code, &Error));
>    EXPECT_EQ("", Error.toStringFull());
>    M = HasParameter->unconditionalConvertTo<Decl>();
>
> @@ -228,20 +231,18 @@ TEST(ParserTest, FullParserTest) {
>
>    // Test named values.
>    auto NamedValues = getTestNamedValues();
> +
> +  Code = "functionDecl(hasParamA, hasParameter(1, hasName(nameX)))";
>    llvm::Optional<DynTypedMatcher> HasParameterWithNamedValues(
> -      Parser::parseMatcherExpression(
> -          "functionDecl(hasParamA, hasParameter(1, hasName(nameX)))",
> -          nullptr, &NamedValues, &Error));
> +      Parser::parseMatcherExpression(Code, nullptr, &NamedValues, &Error));
>    EXPECT_EQ("", Error.toStringFull());
>    M = HasParameterWithNamedValues->unconditionalConvertTo<Decl>();
>
>    EXPECT_TRUE(matches("void f(int a, int x);", M));
>    EXPECT_FALSE(matches("void f(int x, int a);", M));
>
> -
> -  EXPECT_TRUE(!Parser::parseMatcherExpression(
> -                   "hasInitializer(\n    binaryOperator(hasLHS(\"A\")))",
> -                   &Error).hasValue());
> +  Code = "hasInitializer(\n    binaryOperator(hasLHS(\"A\")))";
> +  EXPECT_TRUE(!Parser::parseMatcherExpression(Code, &Error).hasValue());
>    EXPECT_EQ("1:1: Error parsing argument 1 for matcher hasInitializer.\n"
>              "2:5: Error parsing argument 1 for matcher binaryOperator.\n"
>              "2:20: Error building matcher hasLHS.\n"
> @@ -252,9 +253,11 @@ TEST(ParserTest, FullParserTest) {
>
>  TEST(ParserTest, VariadicMatchTest) {
>    Diagnostics Error;
> -  llvm::Optional<DynTypedMatcher> OM(Parser::parseMatcherExpression(
> -      "stmt(objcMessageExpr(hasAnySelector(\"methodA\", \"methodB:\")))",
> -      &Error));
> +
> +  StringRef Code =
> +      "stmt(objcMessageExpr(hasAnySelector(\"methodA\", \"methodB:\")))";
> +  llvm::Optional<DynTypedMatcher> OM(
> +      Parser::parseMatcherExpression(Code, &Error));
>    EXPECT_EQ("", Error.toStringFull());
>    auto M = OM->unconditionalConvertTo<Stmt>();
>    EXPECT_TRUE(matchesObjC("@interface I @end "
> @@ -324,15 +327,132 @@ TEST(ParserTest, OverloadErrors) {
>              ParseWithError("callee(\"A\")"));
>  }
>
> +TEST(ParserTest, ParseMultiline) {
> +  StringRef Code;
> +
> +  llvm::Optional<DynTypedMatcher> M;
> +  {
> +    Code = R"matcher(varDecl(
> +  hasName("foo")
> +  )
> +)matcher";
> +    Diagnostics Error;
> +    EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error).hasValue());
> +  }
> +
> +  {
> +    Code = R"matcher(varDecl(
> +  # Internal comment
> +  hasName("foo") # Internal comment
> +# Internal comment
> +  )
> +)matcher";
> +    Diagnostics Error;
> +    EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error).hasValue());
> +  }
> +
> +  {
> +    Code = R"matcher(decl().bind(
> +  "paramName")
> +)matcher";
> +    Diagnostics Error;
> +    EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error).hasValue());
> +  }
> +
> +  {
> +    Code = R"matcher(decl().bind(
> +  "paramName"
> +  )
> +)matcher";
> +    Diagnostics Error;
> +    EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error).hasValue());
> +  }
> +
> +  {
> +    Code = R"matcher(decl(decl()
> +, decl()))matcher";
> +    Diagnostics Error;
> +    EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error).hasValue());
> +  }
> +
> +  {
> +    Code = R"matcher(decl(decl(),
> +decl()))matcher";
> +    Diagnostics Error;
> +    EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error).hasValue());
> +  }
> +
> +  {
> +    Code = "namedDecl(hasName(\"n\"\n))";
> +    Diagnostics Error;
> +    EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error).hasValue());
> +  }
> +
> +  {
> +    Diagnostics Error;
> +
> +    auto NamedValues = getTestNamedValues();
> +
> +    Code = R"matcher(hasParamA.bind
> +  ("paramName")
> +)matcher";
> +    M = Parser::parseMatcherExpression(Code, nullptr, &NamedValues, &Error);
> +    EXPECT_FALSE(M.hasValue());
> +    EXPECT_EQ("1:15: Malformed bind() expression.", Error.toStringFull());
> +  }
> +
> +  {
> +    Diagnostics Error;
> +
> +    auto NamedValues = getTestNamedValues();
> +
> +    Code = R"matcher(hasParamA.
> +  bind("paramName")
> +)matcher";
> +    M = Parser::parseMatcherExpression(Code, nullptr, &NamedValues, &Error);
> +    EXPECT_FALSE(M.hasValue());
> +    EXPECT_EQ("1:11: Malformed bind() expression.", Error.toStringFull());
> +  }
> +
> +  {
> +    Diagnostics Error;
> +
> +    Code = R"matcher(varDecl
> +()
> +)matcher";
> +    M = Parser::parseMatcherExpression(Code, nullptr, nullptr, &Error);
> +    EXPECT_FALSE(M.hasValue());
> +    EXPECT_EQ("1:8: Error parsing matcher. Found token "
> +              "<NewLine> while looking for '('.",
> +              Error.toStringFull());
> +  }
> +
> +  // Correct line/column numbers
> +  {
> +    Diagnostics Error;
> +
> +    Code = R"matcher(varDecl(
> +  doesNotExist()
> +  )
> +)matcher";
> +    M = Parser::parseMatcherExpression(Code, nullptr, nullptr, &Error);
> +    EXPECT_FALSE(M.hasValue());
> +    EXPECT_EQ(R"error(1:1: Error parsing argument 1 for matcher varDecl.
> +2:3: Matcher not found: doesNotExist)error",
> +              Error.toStringFull());
> +  }
> +}
> +
>  TEST(ParserTest, CompletionRegistry) {
> -  std::vector<MatcherCompletion> Comps =
> -      Parser::completeExpression("while", 5);
> +  StringRef Code = "while";
> +  std::vector<MatcherCompletion> Comps = Parser::completeExpression(Code, 5);
>    ASSERT_EQ(1u, Comps.size());
>    EXPECT_EQ("Stmt(", Comps[0].TypedText);
>    EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)",
>              Comps[0].MatcherDecl);
>
> -  Comps = Parser::completeExpression("whileStmt().", 12);
> +  Code = "whileStmt().";
> +  Comps = Parser::completeExpression(Code, 12);
>    ASSERT_EQ(1u, Comps.size());
>    EXPECT_EQ("bind(\"", Comps[0].TypedText);
>    EXPECT_EQ("bind", Comps[0].MatcherDecl);
> @@ -380,9 +500,9 @@ TEST(ParserTest, ParseBindOnLet) {
>    Diagnostics Error;
>
>    {
> +    StringRef Code = "hasParamA.bind(\"parmABinding\")";
>      llvm::Optional<DynTypedMatcher> TopLevelLetBinding(
> -        Parser::parseMatcherExpression("hasParamA.bind(\"parmABinding\")",
> -                                       nullptr, &NamedValues, &Error));
> +        Parser::parseMatcherExpression(Code, nullptr, &NamedValues, &Error));
>      EXPECT_EQ("", Error.toStringFull());
>      auto M = TopLevelLetBinding->unconditionalConvertTo<Decl>();
>
> @@ -395,10 +515,9 @@ TEST(ParserTest, ParseBindOnLet) {
>    }
>
>    {
> +    StringRef Code = "functionDecl(hasParamA.bind(\"parmABinding\"))";
>      llvm::Optional<DynTypedMatcher> NestedLetBinding(
> -        Parser::parseMatcherExpression(
> -            "functionDecl(hasParamA.bind(\"parmABinding\"))", nullptr,
> -            &NamedValues, &Error));
> +        Parser::parseMatcherExpression(Code, nullptr, &NamedValues, &Error));
>      EXPECT_EQ("", Error.toStringFull());
>      auto M = NestedLetBinding->unconditionalConvertTo<Decl>();
>
>
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


More information about the cfe-commits mailing list