1//===--- CommentBriefParser.cpp - Dumb comment parser ---------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/AST/CommentBriefParser.h"
10#include "clang/AST/CommentCommandTraits.h"
11#include "clang/Basic/CharInfo.h"
12
13namespace clang {
14namespace comments {
15
16namespace {
17
18/// Convert all whitespace into spaces, remove leading and trailing spaces,
19/// compress multiple spaces into one.
20void cleanupBrief(std::string &S) {
21  bool PrevWasSpace = true;
22  std::string::iterator O = S.begin();
23  for (std::string::iterator I = S.begin(), E = S.end();
24       I != E; ++I) {
25    const char C = *I;
26    if (clang::isWhitespace(C)) {
27      if (!PrevWasSpace) {
28        *O++ = ' ';
29        PrevWasSpace = true;
30      }
31    } else {
32      *O++ = C;
33      PrevWasSpace = false;
34    }
35  }
36  if (O != S.begin() && *(O - 1) == ' ')
37    --O;
38
39  S.resize(O - S.begin());
40}
41
42bool isWhitespace(StringRef Text) {
43  return llvm::all_of(Text, clang::isWhitespace);
44}
45} // unnamed namespace
46
47BriefParser::BriefParser(Lexer &L, const CommandTraits &Traits) :
48    L(L), Traits(Traits) {
49  // Get lookahead token.
50  ConsumeToken();
51}
52
53std::string BriefParser::Parse() {
54  std::string FirstParagraphOrBrief;
55  std::string ReturnsParagraph;
56  bool InFirstParagraph = true;
57  bool InBrief = false;
58  bool InReturns = false;
59
60  while (Tok.isNot(tok::eof)) {
61    if (Tok.is(tok::text)) {
62      if (InFirstParagraph || InBrief)
63        FirstParagraphOrBrief += Tok.getText();
64      else if (InReturns)
65        ReturnsParagraph += Tok.getText();
66      ConsumeToken();
67      continue;
68    }
69
70    if (Tok.is(tok::backslash_command) || Tok.is(tok::at_command)) {
71      const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
72      if (Info->IsBriefCommand) {
73        FirstParagraphOrBrief.clear();
74        InBrief = true;
75        ConsumeToken();
76        continue;
77      }
78      if (Info->IsReturnsCommand) {
79        InReturns = true;
80        InBrief = false;
81        InFirstParagraph = false;
82        ReturnsParagraph += "Returns ";
83        ConsumeToken();
84        continue;
85      }
86      // Block commands implicitly start a new paragraph.
87      if (Info->IsBlockCommand) {
88        // We found an implicit paragraph end.
89        InFirstParagraph = false;
90        if (InBrief)
91          break;
92      }
93    }
94
95    if (Tok.is(tok::newline)) {
96      if (InFirstParagraph || InBrief)
97        FirstParagraphOrBrief += ' ';
98      else if (InReturns)
99        ReturnsParagraph += ' ';
100      ConsumeToken();
101
102      // If the next token is a whitespace only text, ignore it.  Thus we allow
103      // two paragraphs to be separated by line that has only whitespace in it.
104      //
105      // We don't need to add a space to the parsed text because we just added
106      // a space for the newline.
107      if (Tok.is(tok::text)) {
108        if (isWhitespace(Tok.getText()))
109          ConsumeToken();
110      }
111
112      if (Tok.is(tok::newline)) {
113        ConsumeToken();
114        // We found a paragraph end.  This ends the brief description if
115        // \command or its equivalent was explicitly used.
116        // Stop scanning text because an explicit \paragraph is the
117        // preferred one.
118        if (InBrief)
119          break;
120        // End first paragraph if we found some non-whitespace text.
121        if (InFirstParagraph && !isWhitespace(FirstParagraphOrBrief))
122          InFirstParagraph = false;
123        // End the \\returns paragraph because we found the paragraph end.
124        InReturns = false;
125      }
126      continue;
127    }
128
129    // We didn't handle this token, so just drop it.
130    ConsumeToken();
131  }
132
133  cleanupBrief(FirstParagraphOrBrief);
134  if (!FirstParagraphOrBrief.empty())
135    return FirstParagraphOrBrief;
136
137  cleanupBrief(ReturnsParagraph);
138  return ReturnsParagraph;
139}
140
141} // end namespace comments
142} // end namespace clang
143
144
145