Home | History | Annotate | Line # | Download | only in AST
      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 
     12 namespace clang {
     13 namespace comments {
     14 
     15 namespace {
     16 inline 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.
     23 void 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 
     46 bool 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 
     56 BriefParser::BriefParser(Lexer &L, const CommandTraits &Traits) :
     57     L(L), Traits(Traits) {
     58   // Get lookahead token.
     59   ConsumeToken();
     60 }
     61 
     62 std::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