forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			781 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			781 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- CommentParser.cpp - Doxygen comment parser -----------------------===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "clang/AST/CommentParser.h"
 | 
						|
#include "clang/AST/CommentCommandTraits.h"
 | 
						|
#include "clang/AST/CommentDiagnostic.h"
 | 
						|
#include "clang/AST/CommentSema.h"
 | 
						|
#include "clang/Basic/CharInfo.h"
 | 
						|
#include "clang/Basic/SourceManager.h"
 | 
						|
#include "llvm/Support/ErrorHandling.h"
 | 
						|
 | 
						|
namespace clang {
 | 
						|
 | 
						|
static inline bool isWhitespace(llvm::StringRef S) {
 | 
						|
  for (StringRef::const_iterator I = S.begin(), E = S.end(); I != E; ++I) {
 | 
						|
    if (!isWhitespace(*I))
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
namespace comments {
 | 
						|
 | 
						|
/// Re-lexes a sequence of tok::text tokens.
 | 
						|
class TextTokenRetokenizer {
 | 
						|
  llvm::BumpPtrAllocator &Allocator;
 | 
						|
  Parser &P;
 | 
						|
 | 
						|
  /// This flag is set when there are no more tokens we can fetch from lexer.
 | 
						|
  bool NoMoreInterestingTokens;
 | 
						|
 | 
						|
  /// Token buffer: tokens we have processed and lookahead.
 | 
						|
  SmallVector<Token, 16> Toks;
 | 
						|
 | 
						|
  /// A position in \c Toks.
 | 
						|
  struct Position {
 | 
						|
    const char *BufferStart;
 | 
						|
    const char *BufferEnd;
 | 
						|
    const char *BufferPtr;
 | 
						|
    SourceLocation BufferStartLoc;
 | 
						|
    unsigned CurToken;
 | 
						|
  };
 | 
						|
 | 
						|
  /// Current position in Toks.
 | 
						|
  Position Pos;
 | 
						|
 | 
						|
  bool isEnd() const {
 | 
						|
    return Pos.CurToken >= Toks.size();
 | 
						|
  }
 | 
						|
 | 
						|
  /// Sets up the buffer pointers to point to current token.
 | 
						|
  void setupBuffer() {
 | 
						|
    assert(!isEnd());
 | 
						|
    const Token &Tok = Toks[Pos.CurToken];
 | 
						|
 | 
						|
    Pos.BufferStart = Tok.getText().begin();
 | 
						|
    Pos.BufferEnd = Tok.getText().end();
 | 
						|
    Pos.BufferPtr = Pos.BufferStart;
 | 
						|
    Pos.BufferStartLoc = Tok.getLocation();
 | 
						|
  }
 | 
						|
 | 
						|
  SourceLocation getSourceLocation() const {
 | 
						|
    const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
 | 
						|
    return Pos.BufferStartLoc.getLocWithOffset(CharNo);
 | 
						|
  }
 | 
						|
 | 
						|
  char peek() const {
 | 
						|
    assert(!isEnd());
 | 
						|
    assert(Pos.BufferPtr != Pos.BufferEnd);
 | 
						|
    return *Pos.BufferPtr;
 | 
						|
  }
 | 
						|
 | 
						|
  void consumeChar() {
 | 
						|
    assert(!isEnd());
 | 
						|
    assert(Pos.BufferPtr != Pos.BufferEnd);
 | 
						|
    Pos.BufferPtr++;
 | 
						|
    if (Pos.BufferPtr == Pos.BufferEnd) {
 | 
						|
      Pos.CurToken++;
 | 
						|
      if (isEnd() && !addToken())
 | 
						|
        return;
 | 
						|
 | 
						|
      assert(!isEnd());
 | 
						|
      setupBuffer();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /// Add a token.
 | 
						|
  /// Returns true on success, false if there are no interesting tokens to
 | 
						|
  /// fetch from lexer.
 | 
						|
  bool addToken() {
 | 
						|
    if (NoMoreInterestingTokens)
 | 
						|
      return false;
 | 
						|
 | 
						|
    if (P.Tok.is(tok::newline)) {
 | 
						|
      // If we see a single newline token between text tokens, skip it.
 | 
						|
      Token Newline = P.Tok;
 | 
						|
      P.consumeToken();
 | 
						|
      if (P.Tok.isNot(tok::text)) {
 | 
						|
        P.putBack(Newline);
 | 
						|
        NoMoreInterestingTokens = true;
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (P.Tok.isNot(tok::text)) {
 | 
						|
      NoMoreInterestingTokens = true;
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    Toks.push_back(P.Tok);
 | 
						|
    P.consumeToken();
 | 
						|
    if (Toks.size() == 1)
 | 
						|
      setupBuffer();
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  void consumeWhitespace() {
 | 
						|
    while (!isEnd()) {
 | 
						|
      if (isWhitespace(peek()))
 | 
						|
        consumeChar();
 | 
						|
      else
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  void formTokenWithChars(Token &Result,
 | 
						|
                          SourceLocation Loc,
 | 
						|
                          const char *TokBegin,
 | 
						|
                          unsigned TokLength,
 | 
						|
                          StringRef Text) {
 | 
						|
    Result.setLocation(Loc);
 | 
						|
    Result.setKind(tok::text);
 | 
						|
    Result.setLength(TokLength);
 | 
						|
#ifndef NDEBUG
 | 
						|
    Result.TextPtr = "<UNSET>";
 | 
						|
    Result.IntVal = 7;
 | 
						|
#endif
 | 
						|
    Result.setText(Text);
 | 
						|
  }
 | 
						|
 | 
						|
public:
 | 
						|
  TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P):
 | 
						|
      Allocator(Allocator), P(P), NoMoreInterestingTokens(false) {
 | 
						|
    Pos.CurToken = 0;
 | 
						|
    addToken();
 | 
						|
  }
 | 
						|
 | 
						|
  /// Extract a word -- sequence of non-whitespace characters.
 | 
						|
  bool lexWord(Token &Tok) {
 | 
						|
    if (isEnd())
 | 
						|
      return false;
 | 
						|
 | 
						|
    Position SavedPos = Pos;
 | 
						|
 | 
						|
    consumeWhitespace();
 | 
						|
    SmallString<32> WordText;
 | 
						|
    const char *WordBegin = Pos.BufferPtr;
 | 
						|
    SourceLocation Loc = getSourceLocation();
 | 
						|
    while (!isEnd()) {
 | 
						|
      const char C = peek();
 | 
						|
      if (!isWhitespace(C)) {
 | 
						|
        WordText.push_back(C);
 | 
						|
        consumeChar();
 | 
						|
      } else
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    const unsigned Length = WordText.size();
 | 
						|
    if (Length == 0) {
 | 
						|
      Pos = SavedPos;
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    char *TextPtr = Allocator.Allocate<char>(Length + 1);
 | 
						|
 | 
						|
    memcpy(TextPtr, WordText.c_str(), Length + 1);
 | 
						|
    StringRef Text = StringRef(TextPtr, Length);
 | 
						|
 | 
						|
    formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
 | 
						|
    if (isEnd())
 | 
						|
      return false;
 | 
						|
 | 
						|
    Position SavedPos = Pos;
 | 
						|
 | 
						|
    consumeWhitespace();
 | 
						|
    SmallString<32> WordText;
 | 
						|
    const char *WordBegin = Pos.BufferPtr;
 | 
						|
    SourceLocation Loc = getSourceLocation();
 | 
						|
    bool Error = false;
 | 
						|
    if (!isEnd()) {
 | 
						|
      const char C = peek();
 | 
						|
      if (C == OpenDelim) {
 | 
						|
        WordText.push_back(C);
 | 
						|
        consumeChar();
 | 
						|
      } else
 | 
						|
        Error = true;
 | 
						|
    }
 | 
						|
    char C = '\0';
 | 
						|
    while (!Error && !isEnd()) {
 | 
						|
      C = peek();
 | 
						|
      WordText.push_back(C);
 | 
						|
      consumeChar();
 | 
						|
      if (C == CloseDelim)
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    if (!Error && C != CloseDelim)
 | 
						|
      Error = true;
 | 
						|
 | 
						|
    if (Error) {
 | 
						|
      Pos = SavedPos;
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    const unsigned Length = WordText.size();
 | 
						|
    char *TextPtr = Allocator.Allocate<char>(Length + 1);
 | 
						|
 | 
						|
    memcpy(TextPtr, WordText.c_str(), Length + 1);
 | 
						|
    StringRef Text = StringRef(TextPtr, Length);
 | 
						|
 | 
						|
    formTokenWithChars(Tok, Loc, WordBegin,
 | 
						|
                       Pos.BufferPtr - WordBegin, Text);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  /// Put back tokens that we didn't consume.
 | 
						|
  void putBackLeftoverTokens() {
 | 
						|
    if (isEnd())
 | 
						|
      return;
 | 
						|
 | 
						|
    bool HavePartialTok = false;
 | 
						|
    Token PartialTok;
 | 
						|
    if (Pos.BufferPtr != Pos.BufferStart) {
 | 
						|
      formTokenWithChars(PartialTok, getSourceLocation(),
 | 
						|
                         Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
 | 
						|
                         StringRef(Pos.BufferPtr,
 | 
						|
                                   Pos.BufferEnd - Pos.BufferPtr));
 | 
						|
      HavePartialTok = true;
 | 
						|
      Pos.CurToken++;
 | 
						|
    }
 | 
						|
 | 
						|
    P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
 | 
						|
    Pos.CurToken = Toks.size();
 | 
						|
 | 
						|
    if (HavePartialTok)
 | 
						|
      P.putBack(PartialTok);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
 | 
						|
               const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
 | 
						|
               const CommandTraits &Traits):
 | 
						|
    L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
 | 
						|
    Traits(Traits) {
 | 
						|
  consumeToken();
 | 
						|
}
 | 
						|
 | 
						|
void Parser::parseParamCommandArgs(ParamCommandComment *PC,
 | 
						|
                                   TextTokenRetokenizer &Retokenizer) {
 | 
						|
  Token Arg;
 | 
						|
  // Check if argument looks like direction specification: [dir]
 | 
						|
  // e.g., [in], [out], [in,out]
 | 
						|
  if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
 | 
						|
    S.actOnParamCommandDirectionArg(PC,
 | 
						|
                                    Arg.getLocation(),
 | 
						|
                                    Arg.getEndLocation(),
 | 
						|
                                    Arg.getText());
 | 
						|
 | 
						|
  if (Retokenizer.lexWord(Arg))
 | 
						|
    S.actOnParamCommandParamNameArg(PC,
 | 
						|
                                    Arg.getLocation(),
 | 
						|
                                    Arg.getEndLocation(),
 | 
						|
                                    Arg.getText());
 | 
						|
}
 | 
						|
 | 
						|
void Parser::parseTParamCommandArgs(TParamCommandComment *TPC,
 | 
						|
                                    TextTokenRetokenizer &Retokenizer) {
 | 
						|
  Token Arg;
 | 
						|
  if (Retokenizer.lexWord(Arg))
 | 
						|
    S.actOnTParamCommandParamNameArg(TPC,
 | 
						|
                                     Arg.getLocation(),
 | 
						|
                                     Arg.getEndLocation(),
 | 
						|
                                     Arg.getText());
 | 
						|
}
 | 
						|
 | 
						|
void Parser::parseBlockCommandArgs(BlockCommandComment *BC,
 | 
						|
                                   TextTokenRetokenizer &Retokenizer,
 | 
						|
                                   unsigned NumArgs) {
 | 
						|
  typedef BlockCommandComment::Argument Argument;
 | 
						|
  Argument *Args =
 | 
						|
      new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs];
 | 
						|
  unsigned ParsedArgs = 0;
 | 
						|
  Token Arg;
 | 
						|
  while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
 | 
						|
    Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(),
 | 
						|
                                            Arg.getEndLocation()),
 | 
						|
                                Arg.getText());
 | 
						|
    ParsedArgs++;
 | 
						|
  }
 | 
						|
 | 
						|
  S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs));
 | 
						|
}
 | 
						|
 | 
						|
BlockCommandComment *Parser::parseBlockCommand() {
 | 
						|
  assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
 | 
						|
 | 
						|
  ParamCommandComment *PC = nullptr;
 | 
						|
  TParamCommandComment *TPC = nullptr;
 | 
						|
  BlockCommandComment *BC = nullptr;
 | 
						|
  const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
 | 
						|
  CommandMarkerKind CommandMarker =
 | 
						|
      Tok.is(tok::backslash_command) ? CMK_Backslash : CMK_At;
 | 
						|
  if (Info->IsParamCommand) {
 | 
						|
    PC = S.actOnParamCommandStart(Tok.getLocation(),
 | 
						|
                                  Tok.getEndLocation(),
 | 
						|
                                  Tok.getCommandID(),
 | 
						|
                                  CommandMarker);
 | 
						|
  } else if (Info->IsTParamCommand) {
 | 
						|
    TPC = S.actOnTParamCommandStart(Tok.getLocation(),
 | 
						|
                                    Tok.getEndLocation(),
 | 
						|
                                    Tok.getCommandID(),
 | 
						|
                                    CommandMarker);
 | 
						|
  } else {
 | 
						|
    BC = S.actOnBlockCommandStart(Tok.getLocation(),
 | 
						|
                                  Tok.getEndLocation(),
 | 
						|
                                  Tok.getCommandID(),
 | 
						|
                                  CommandMarker);
 | 
						|
  }
 | 
						|
  consumeToken();
 | 
						|
 | 
						|
  if (isTokBlockCommand()) {
 | 
						|
    // Block command ahead.  We can't nest block commands, so pretend that this
 | 
						|
    // command has an empty argument.
 | 
						|
    ParagraphComment *Paragraph = S.actOnParagraphComment(None);
 | 
						|
    if (PC) {
 | 
						|
      S.actOnParamCommandFinish(PC, Paragraph);
 | 
						|
      return PC;
 | 
						|
    } else if (TPC) {
 | 
						|
      S.actOnTParamCommandFinish(TPC, Paragraph);
 | 
						|
      return TPC;
 | 
						|
    } else {
 | 
						|
      S.actOnBlockCommandFinish(BC, Paragraph);
 | 
						|
      return BC;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (PC || TPC || Info->NumArgs > 0) {
 | 
						|
    // In order to parse command arguments we need to retokenize a few
 | 
						|
    // following text tokens.
 | 
						|
    TextTokenRetokenizer Retokenizer(Allocator, *this);
 | 
						|
 | 
						|
    if (PC)
 | 
						|
      parseParamCommandArgs(PC, Retokenizer);
 | 
						|
    else if (TPC)
 | 
						|
      parseTParamCommandArgs(TPC, Retokenizer);
 | 
						|
    else
 | 
						|
      parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs);
 | 
						|
 | 
						|
    Retokenizer.putBackLeftoverTokens();
 | 
						|
  }
 | 
						|
 | 
						|
  // If there's a block command ahead, we will attach an empty paragraph to
 | 
						|
  // this command.
 | 
						|
  bool EmptyParagraph = false;
 | 
						|
  if (isTokBlockCommand())
 | 
						|
    EmptyParagraph = true;
 | 
						|
  else if (Tok.is(tok::newline)) {
 | 
						|
    Token PrevTok = Tok;
 | 
						|
    consumeToken();
 | 
						|
    EmptyParagraph = isTokBlockCommand();
 | 
						|
    putBack(PrevTok);
 | 
						|
  }
 | 
						|
 | 
						|
  ParagraphComment *Paragraph;
 | 
						|
  if (EmptyParagraph)
 | 
						|
    Paragraph = S.actOnParagraphComment(None);
 | 
						|
  else {
 | 
						|
    BlockContentComment *Block = parseParagraphOrBlockCommand();
 | 
						|
    // Since we have checked for a block command, we should have parsed a
 | 
						|
    // paragraph.
 | 
						|
    Paragraph = cast<ParagraphComment>(Block);
 | 
						|
  }
 | 
						|
 | 
						|
  if (PC) {
 | 
						|
    S.actOnParamCommandFinish(PC, Paragraph);
 | 
						|
    return PC;
 | 
						|
  } else if (TPC) {
 | 
						|
    S.actOnTParamCommandFinish(TPC, Paragraph);
 | 
						|
    return TPC;
 | 
						|
  } else {
 | 
						|
    S.actOnBlockCommandFinish(BC, Paragraph);
 | 
						|
    return BC;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
InlineCommandComment *Parser::parseInlineCommand() {
 | 
						|
  assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
 | 
						|
 | 
						|
  const Token CommandTok = Tok;
 | 
						|
  consumeToken();
 | 
						|
 | 
						|
  TextTokenRetokenizer Retokenizer(Allocator, *this);
 | 
						|
 | 
						|
  Token ArgTok;
 | 
						|
  bool ArgTokValid = Retokenizer.lexWord(ArgTok);
 | 
						|
 | 
						|
  InlineCommandComment *IC;
 | 
						|
  if (ArgTokValid) {
 | 
						|
    IC = S.actOnInlineCommand(CommandTok.getLocation(),
 | 
						|
                              CommandTok.getEndLocation(),
 | 
						|
                              CommandTok.getCommandID(),
 | 
						|
                              ArgTok.getLocation(),
 | 
						|
                              ArgTok.getEndLocation(),
 | 
						|
                              ArgTok.getText());
 | 
						|
  } else {
 | 
						|
    IC = S.actOnInlineCommand(CommandTok.getLocation(),
 | 
						|
                              CommandTok.getEndLocation(),
 | 
						|
                              CommandTok.getCommandID());
 | 
						|
 | 
						|
    Diag(CommandTok.getEndLocation().getLocWithOffset(1),
 | 
						|
         diag::warn_doc_inline_contents_no_argument)
 | 
						|
        << CommandTok.is(tok::at_command)
 | 
						|
        << Traits.getCommandInfo(CommandTok.getCommandID())->Name
 | 
						|
        << SourceRange(CommandTok.getLocation(), CommandTok.getEndLocation());
 | 
						|
  }
 | 
						|
 | 
						|
  Retokenizer.putBackLeftoverTokens();
 | 
						|
 | 
						|
  return IC;
 | 
						|
}
 | 
						|
 | 
						|
HTMLStartTagComment *Parser::parseHTMLStartTag() {
 | 
						|
  assert(Tok.is(tok::html_start_tag));
 | 
						|
  HTMLStartTagComment *HST =
 | 
						|
      S.actOnHTMLStartTagStart(Tok.getLocation(),
 | 
						|
                               Tok.getHTMLTagStartName());
 | 
						|
  consumeToken();
 | 
						|
 | 
						|
  SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
 | 
						|
  while (true) {
 | 
						|
    switch (Tok.getKind()) {
 | 
						|
    case tok::html_ident: {
 | 
						|
      Token Ident = Tok;
 | 
						|
      consumeToken();
 | 
						|
      if (Tok.isNot(tok::html_equals)) {
 | 
						|
        Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
 | 
						|
                                                       Ident.getHTMLIdent()));
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      Token Equals = Tok;
 | 
						|
      consumeToken();
 | 
						|
      if (Tok.isNot(tok::html_quoted_string)) {
 | 
						|
        Diag(Tok.getLocation(),
 | 
						|
             diag::warn_doc_html_start_tag_expected_quoted_string)
 | 
						|
          << SourceRange(Equals.getLocation());
 | 
						|
        Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
 | 
						|
                                                       Ident.getHTMLIdent()));
 | 
						|
        while (Tok.is(tok::html_equals) ||
 | 
						|
               Tok.is(tok::html_quoted_string))
 | 
						|
          consumeToken();
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      Attrs.push_back(HTMLStartTagComment::Attribute(
 | 
						|
                              Ident.getLocation(),
 | 
						|
                              Ident.getHTMLIdent(),
 | 
						|
                              Equals.getLocation(),
 | 
						|
                              SourceRange(Tok.getLocation(),
 | 
						|
                                          Tok.getEndLocation()),
 | 
						|
                              Tok.getHTMLQuotedString()));
 | 
						|
      consumeToken();
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    case tok::html_greater:
 | 
						|
      S.actOnHTMLStartTagFinish(HST,
 | 
						|
                                S.copyArray(llvm::makeArrayRef(Attrs)),
 | 
						|
                                Tok.getLocation(),
 | 
						|
                                /* IsSelfClosing = */ false);
 | 
						|
      consumeToken();
 | 
						|
      return HST;
 | 
						|
 | 
						|
    case tok::html_slash_greater:
 | 
						|
      S.actOnHTMLStartTagFinish(HST,
 | 
						|
                                S.copyArray(llvm::makeArrayRef(Attrs)),
 | 
						|
                                Tok.getLocation(),
 | 
						|
                                /* IsSelfClosing = */ true);
 | 
						|
      consumeToken();
 | 
						|
      return HST;
 | 
						|
 | 
						|
    case tok::html_equals:
 | 
						|
    case tok::html_quoted_string:
 | 
						|
      Diag(Tok.getLocation(),
 | 
						|
           diag::warn_doc_html_start_tag_expected_ident_or_greater);
 | 
						|
      while (Tok.is(tok::html_equals) ||
 | 
						|
             Tok.is(tok::html_quoted_string))
 | 
						|
        consumeToken();
 | 
						|
      if (Tok.is(tok::html_ident) ||
 | 
						|
          Tok.is(tok::html_greater) ||
 | 
						|
          Tok.is(tok::html_slash_greater))
 | 
						|
        continue;
 | 
						|
 | 
						|
      S.actOnHTMLStartTagFinish(HST,
 | 
						|
                                S.copyArray(llvm::makeArrayRef(Attrs)),
 | 
						|
                                SourceLocation(),
 | 
						|
                                /* IsSelfClosing = */ false);
 | 
						|
      return HST;
 | 
						|
 | 
						|
    default:
 | 
						|
      // Not a token from an HTML start tag.  Thus HTML tag prematurely ended.
 | 
						|
      S.actOnHTMLStartTagFinish(HST,
 | 
						|
                                S.copyArray(llvm::makeArrayRef(Attrs)),
 | 
						|
                                SourceLocation(),
 | 
						|
                                /* IsSelfClosing = */ false);
 | 
						|
      bool StartLineInvalid;
 | 
						|
      const unsigned StartLine = SourceMgr.getPresumedLineNumber(
 | 
						|
                                                  HST->getLocation(),
 | 
						|
                                                  &StartLineInvalid);
 | 
						|
      bool EndLineInvalid;
 | 
						|
      const unsigned EndLine = SourceMgr.getPresumedLineNumber(
 | 
						|
                                                  Tok.getLocation(),
 | 
						|
                                                  &EndLineInvalid);
 | 
						|
      if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
 | 
						|
        Diag(Tok.getLocation(),
 | 
						|
             diag::warn_doc_html_start_tag_expected_ident_or_greater)
 | 
						|
          << HST->getSourceRange();
 | 
						|
      else {
 | 
						|
        Diag(Tok.getLocation(),
 | 
						|
             diag::warn_doc_html_start_tag_expected_ident_or_greater);
 | 
						|
        Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
 | 
						|
          << HST->getSourceRange();
 | 
						|
      }
 | 
						|
      return HST;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
HTMLEndTagComment *Parser::parseHTMLEndTag() {
 | 
						|
  assert(Tok.is(tok::html_end_tag));
 | 
						|
  Token TokEndTag = Tok;
 | 
						|
  consumeToken();
 | 
						|
  SourceLocation Loc;
 | 
						|
  if (Tok.is(tok::html_greater)) {
 | 
						|
    Loc = Tok.getLocation();
 | 
						|
    consumeToken();
 | 
						|
  }
 | 
						|
 | 
						|
  return S.actOnHTMLEndTag(TokEndTag.getLocation(),
 | 
						|
                           Loc,
 | 
						|
                           TokEndTag.getHTMLTagEndName());
 | 
						|
}
 | 
						|
 | 
						|
BlockContentComment *Parser::parseParagraphOrBlockCommand() {
 | 
						|
  SmallVector<InlineContentComment *, 8> Content;
 | 
						|
 | 
						|
  while (true) {
 | 
						|
    switch (Tok.getKind()) {
 | 
						|
    case tok::verbatim_block_begin:
 | 
						|
    case tok::verbatim_line_name:
 | 
						|
    case tok::eof:
 | 
						|
      break; // Block content or EOF ahead, finish this parapgaph.
 | 
						|
 | 
						|
    case tok::unknown_command:
 | 
						|
      Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
 | 
						|
                                              Tok.getEndLocation(),
 | 
						|
                                              Tok.getUnknownCommandName()));
 | 
						|
      consumeToken();
 | 
						|
      continue;
 | 
						|
 | 
						|
    case tok::backslash_command:
 | 
						|
    case tok::at_command: {
 | 
						|
      const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
 | 
						|
      if (Info->IsBlockCommand) {
 | 
						|
        if (Content.size() == 0)
 | 
						|
          return parseBlockCommand();
 | 
						|
        break; // Block command ahead, finish this parapgaph.
 | 
						|
      }
 | 
						|
      if (Info->IsVerbatimBlockEndCommand) {
 | 
						|
        Diag(Tok.getLocation(),
 | 
						|
             diag::warn_verbatim_block_end_without_start)
 | 
						|
          << Tok.is(tok::at_command)
 | 
						|
          << Info->Name
 | 
						|
          << SourceRange(Tok.getLocation(), Tok.getEndLocation());
 | 
						|
        consumeToken();
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      if (Info->IsUnknownCommand) {
 | 
						|
        Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
 | 
						|
                                                Tok.getEndLocation(),
 | 
						|
                                                Info->getID()));
 | 
						|
        consumeToken();
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      assert(Info->IsInlineCommand);
 | 
						|
      Content.push_back(parseInlineCommand());
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    case tok::newline: {
 | 
						|
      consumeToken();
 | 
						|
      if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
 | 
						|
        consumeToken();
 | 
						|
        break; // Two newlines -- end of paragraph.
 | 
						|
      }
 | 
						|
      // Also allow [tok::newline, tok::text, tok::newline] if the middle
 | 
						|
      // tok::text is just whitespace.
 | 
						|
      if (Tok.is(tok::text) && isWhitespace(Tok.getText())) {
 | 
						|
        Token WhitespaceTok = Tok;
 | 
						|
        consumeToken();
 | 
						|
        if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
 | 
						|
          consumeToken();
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        // We have [tok::newline, tok::text, non-newline].  Put back tok::text.
 | 
						|
        putBack(WhitespaceTok);
 | 
						|
      }
 | 
						|
      if (Content.size() > 0)
 | 
						|
        Content.back()->addTrailingNewline();
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // Don't deal with HTML tag soup now.
 | 
						|
    case tok::html_start_tag:
 | 
						|
      Content.push_back(parseHTMLStartTag());
 | 
						|
      continue;
 | 
						|
 | 
						|
    case tok::html_end_tag:
 | 
						|
      Content.push_back(parseHTMLEndTag());
 | 
						|
      continue;
 | 
						|
 | 
						|
    case tok::text:
 | 
						|
      Content.push_back(S.actOnText(Tok.getLocation(),
 | 
						|
                                    Tok.getEndLocation(),
 | 
						|
                                    Tok.getText()));
 | 
						|
      consumeToken();
 | 
						|
      continue;
 | 
						|
 | 
						|
    case tok::verbatim_block_line:
 | 
						|
    case tok::verbatim_block_end:
 | 
						|
    case tok::verbatim_line_text:
 | 
						|
    case tok::html_ident:
 | 
						|
    case tok::html_equals:
 | 
						|
    case tok::html_quoted_string:
 | 
						|
    case tok::html_greater:
 | 
						|
    case tok::html_slash_greater:
 | 
						|
      llvm_unreachable("should not see this token");
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
 | 
						|
}
 | 
						|
 | 
						|
VerbatimBlockComment *Parser::parseVerbatimBlock() {
 | 
						|
  assert(Tok.is(tok::verbatim_block_begin));
 | 
						|
 | 
						|
  VerbatimBlockComment *VB =
 | 
						|
      S.actOnVerbatimBlockStart(Tok.getLocation(),
 | 
						|
                                Tok.getVerbatimBlockID());
 | 
						|
  consumeToken();
 | 
						|
 | 
						|
  // Don't create an empty line if verbatim opening command is followed
 | 
						|
  // by a newline.
 | 
						|
  if (Tok.is(tok::newline))
 | 
						|
    consumeToken();
 | 
						|
 | 
						|
  SmallVector<VerbatimBlockLineComment *, 8> Lines;
 | 
						|
  while (Tok.is(tok::verbatim_block_line) ||
 | 
						|
         Tok.is(tok::newline)) {
 | 
						|
    VerbatimBlockLineComment *Line;
 | 
						|
    if (Tok.is(tok::verbatim_block_line)) {
 | 
						|
      Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
 | 
						|
                                      Tok.getVerbatimBlockText());
 | 
						|
      consumeToken();
 | 
						|
      if (Tok.is(tok::newline)) {
 | 
						|
        consumeToken();
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      // Empty line, just a tok::newline.
 | 
						|
      Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
 | 
						|
      consumeToken();
 | 
						|
    }
 | 
						|
    Lines.push_back(Line);
 | 
						|
  }
 | 
						|
 | 
						|
  if (Tok.is(tok::verbatim_block_end)) {
 | 
						|
    const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
 | 
						|
    S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
 | 
						|
                               Info->Name,
 | 
						|
                               S.copyArray(llvm::makeArrayRef(Lines)));
 | 
						|
    consumeToken();
 | 
						|
  } else {
 | 
						|
    // Unterminated \\verbatim block
 | 
						|
    S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
 | 
						|
                               S.copyArray(llvm::makeArrayRef(Lines)));
 | 
						|
  }
 | 
						|
 | 
						|
  return VB;
 | 
						|
}
 | 
						|
 | 
						|
VerbatimLineComment *Parser::parseVerbatimLine() {
 | 
						|
  assert(Tok.is(tok::verbatim_line_name));
 | 
						|
 | 
						|
  Token NameTok = Tok;
 | 
						|
  consumeToken();
 | 
						|
 | 
						|
  SourceLocation TextBegin;
 | 
						|
  StringRef Text;
 | 
						|
  // Next token might not be a tok::verbatim_line_text if verbatim line
 | 
						|
  // starting command comes just before a newline or comment end.
 | 
						|
  if (Tok.is(tok::verbatim_line_text)) {
 | 
						|
    TextBegin = Tok.getLocation();
 | 
						|
    Text = Tok.getVerbatimLineText();
 | 
						|
  } else {
 | 
						|
    TextBegin = NameTok.getEndLocation();
 | 
						|
    Text = "";
 | 
						|
  }
 | 
						|
 | 
						|
  VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
 | 
						|
                                                NameTok.getVerbatimLineID(),
 | 
						|
                                                TextBegin,
 | 
						|
                                                Text);
 | 
						|
  consumeToken();
 | 
						|
  return VL;
 | 
						|
}
 | 
						|
 | 
						|
BlockContentComment *Parser::parseBlockContent() {
 | 
						|
  switch (Tok.getKind()) {
 | 
						|
  case tok::text:
 | 
						|
  case tok::unknown_command:
 | 
						|
  case tok::backslash_command:
 | 
						|
  case tok::at_command:
 | 
						|
  case tok::html_start_tag:
 | 
						|
  case tok::html_end_tag:
 | 
						|
    return parseParagraphOrBlockCommand();
 | 
						|
 | 
						|
  case tok::verbatim_block_begin:
 | 
						|
    return parseVerbatimBlock();
 | 
						|
 | 
						|
  case tok::verbatim_line_name:
 | 
						|
    return parseVerbatimLine();
 | 
						|
 | 
						|
  case tok::eof:
 | 
						|
  case tok::newline:
 | 
						|
  case tok::verbatim_block_line:
 | 
						|
  case tok::verbatim_block_end:
 | 
						|
  case tok::verbatim_line_text:
 | 
						|
  case tok::html_ident:
 | 
						|
  case tok::html_equals:
 | 
						|
  case tok::html_quoted_string:
 | 
						|
  case tok::html_greater:
 | 
						|
  case tok::html_slash_greater:
 | 
						|
    llvm_unreachable("should not see this token");
 | 
						|
  }
 | 
						|
  llvm_unreachable("bogus token kind");
 | 
						|
}
 | 
						|
 | 
						|
FullComment *Parser::parseFullComment() {
 | 
						|
  // Skip newlines at the beginning of the comment.
 | 
						|
  while (Tok.is(tok::newline))
 | 
						|
    consumeToken();
 | 
						|
 | 
						|
  SmallVector<BlockContentComment *, 8> Blocks;
 | 
						|
  while (Tok.isNot(tok::eof)) {
 | 
						|
    Blocks.push_back(parseBlockContent());
 | 
						|
 | 
						|
    // Skip extra newlines after paragraph end.
 | 
						|
    while (Tok.is(tok::newline))
 | 
						|
      consumeToken();
 | 
						|
  }
 | 
						|
  return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
 | 
						|
}
 | 
						|
 | 
						|
} // end namespace comments
 | 
						|
} // end namespace clang
 |