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