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