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