Home | History | Annotate | Download | only in Frontend
      1 //===---- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This is a concrete diagnostic client, which buffers the diagnostic messages.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "clang/Frontend/VerifyDiagnosticConsumer.h"
     15 #include "clang/Frontend/FrontendDiagnostic.h"
     16 #include "clang/Frontend/TextDiagnosticBuffer.h"
     17 #include "clang/Lex/Preprocessor.h"
     18 #include "llvm/ADT/SmallString.h"
     19 #include "llvm/Support/Regex.h"
     20 #include "llvm/Support/raw_ostream.h"
     21 using namespace clang;
     22 
     23 VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &_Diags)
     24   : Diags(_Diags), PrimaryClient(Diags.getClient()),
     25     OwnsPrimaryClient(Diags.ownsClient()),
     26     Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0)
     27 {
     28   Diags.takeClient();
     29 }
     30 
     31 VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
     32   CheckDiagnostics();
     33   Diags.takeClient();
     34   if (OwnsPrimaryClient)
     35     delete PrimaryClient;
     36 }
     37 
     38 // DiagnosticConsumer interface.
     39 
     40 void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
     41                                              const Preprocessor *PP) {
     42   // FIXME: Const hack, we screw up the preprocessor but in practice its ok
     43   // because it doesn't get reused. It would be better if we could make a copy
     44   // though.
     45   CurrentPreprocessor = const_cast<Preprocessor*>(PP);
     46 
     47   PrimaryClient->BeginSourceFile(LangOpts, PP);
     48 }
     49 
     50 void VerifyDiagnosticConsumer::EndSourceFile() {
     51   CheckDiagnostics();
     52 
     53   PrimaryClient->EndSourceFile();
     54 
     55   CurrentPreprocessor = 0;
     56 }
     57 
     58 void VerifyDiagnosticConsumer::HandleDiagnostic(
     59       DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
     60   if (FirstErrorFID.isInvalid() && Info.hasSourceManager()) {
     61     const SourceManager &SM = Info.getSourceManager();
     62     FirstErrorFID = SM.getFileID(Info.getLocation());
     63   }
     64   // Send the diagnostic to the buffer, we will check it once we reach the end
     65   // of the source file (or are destructed).
     66   Buffer->HandleDiagnostic(DiagLevel, Info);
     67 }
     68 
     69 //===----------------------------------------------------------------------===//
     70 // Checking diagnostics implementation.
     71 //===----------------------------------------------------------------------===//
     72 
     73 typedef TextDiagnosticBuffer::DiagList DiagList;
     74 typedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
     75 
     76 namespace {
     77 
     78 /// Directive - Abstract class representing a parsed verify directive.
     79 ///
     80 class Directive {
     81 public:
     82   static Directive* Create(bool RegexKind, const SourceLocation &Location,
     83                            const std::string &Text, unsigned Count);
     84 public:
     85   SourceLocation Location;
     86   const std::string Text;
     87   unsigned Count;
     88 
     89   virtual ~Directive() { }
     90 
     91   // Returns true if directive text is valid.
     92   // Otherwise returns false and populates E.
     93   virtual bool isValid(std::string &Error) = 0;
     94 
     95   // Returns true on match.
     96   virtual bool Match(const std::string &S) = 0;
     97 
     98 protected:
     99   Directive(const SourceLocation &Location, const std::string &Text,
    100             unsigned Count)
    101     : Location(Location), Text(Text), Count(Count) { }
    102 
    103 private:
    104   Directive(const Directive&); // DO NOT IMPLEMENT
    105   void operator=(const Directive&); // DO NOT IMPLEMENT
    106 };
    107 
    108 /// StandardDirective - Directive with string matching.
    109 ///
    110 class StandardDirective : public Directive {
    111 public:
    112   StandardDirective(const SourceLocation &Location, const std::string &Text,
    113                     unsigned Count)
    114     : Directive(Location, Text, Count) { }
    115 
    116   virtual bool isValid(std::string &Error) {
    117     // all strings are considered valid; even empty ones
    118     return true;
    119   }
    120 
    121   virtual bool Match(const std::string &S) {
    122     return S.find(Text) != std::string::npos ||
    123            Text.find(S) != std::string::npos;
    124   }
    125 };
    126 
    127 /// RegexDirective - Directive with regular-expression matching.
    128 ///
    129 class RegexDirective : public Directive {
    130 public:
    131   RegexDirective(const SourceLocation &Location, const std::string &Text,
    132                  unsigned Count)
    133     : Directive(Location, Text, Count), Regex(Text) { }
    134 
    135   virtual bool isValid(std::string &Error) {
    136     if (Regex.isValid(Error))
    137       return true;
    138     return false;
    139   }
    140 
    141   virtual bool Match(const std::string &S) {
    142     return Regex.match(S);
    143   }
    144 
    145 private:
    146   llvm::Regex Regex;
    147 };
    148 
    149 typedef std::vector<Directive*> DirectiveList;
    150 
    151 /// ExpectedData - owns directive objects and deletes on destructor.
    152 ///
    153 struct ExpectedData {
    154   DirectiveList Errors;
    155   DirectiveList Warnings;
    156   DirectiveList Notes;
    157 
    158   ~ExpectedData() {
    159     DirectiveList* Lists[] = { &Errors, &Warnings, &Notes, 0 };
    160     for (DirectiveList **PL = Lists; *PL; ++PL) {
    161       DirectiveList * const L = *PL;
    162       for (DirectiveList::iterator I = L->begin(), E = L->end(); I != E; ++I)
    163         delete *I;
    164     }
    165   }
    166 };
    167 
    168 class ParseHelper
    169 {
    170 public:
    171   ParseHelper(const char *Begin, const char *End)
    172     : Begin(Begin), End(End), C(Begin), P(Begin), PEnd(NULL) { }
    173 
    174   // Return true if string literal is next.
    175   bool Next(StringRef S) {
    176     P = C;
    177     PEnd = C + S.size();
    178     if (PEnd > End)
    179       return false;
    180     return !memcmp(P, S.data(), S.size());
    181   }
    182 
    183   // Return true if number is next.
    184   // Output N only if number is next.
    185   bool Next(unsigned &N) {
    186     unsigned TMP = 0;
    187     P = C;
    188     for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) {
    189       TMP *= 10;
    190       TMP += P[0] - '0';
    191     }
    192     if (P == C)
    193       return false;
    194     PEnd = P;
    195     N = TMP;
    196     return true;
    197   }
    198 
    199   // Return true if string literal is found.
    200   // When true, P marks begin-position of S in content.
    201   bool Search(StringRef S) {
    202     P = std::search(C, End, S.begin(), S.end());
    203     PEnd = P + S.size();
    204     return P != End;
    205   }
    206 
    207   // Advance 1-past previous next/search.
    208   // Behavior is undefined if previous next/search failed.
    209   bool Advance() {
    210     C = PEnd;
    211     return C < End;
    212   }
    213 
    214   // Skip zero or more whitespace.
    215   void SkipWhitespace() {
    216     for (; C < End && isspace(*C); ++C)
    217       ;
    218   }
    219 
    220   // Return true if EOF reached.
    221   bool Done() {
    222     return !(C < End);
    223   }
    224 
    225   const char * const Begin; // beginning of expected content
    226   const char * const End;   // end of expected content (1-past)
    227   const char *C;            // position of next char in content
    228   const char *P;
    229 
    230 private:
    231   const char *PEnd; // previous next/search subject end (1-past)
    232 };
    233 
    234 } // namespace anonymous
    235 
    236 /// ParseDirective - Go through the comment and see if it indicates expected
    237 /// diagnostics. If so, then put them in the appropriate directive list.
    238 ///
    239 static void ParseDirective(const char *CommentStart, unsigned CommentLen,
    240                            ExpectedData &ED, Preprocessor &PP,
    241                            SourceLocation Pos) {
    242   // A single comment may contain multiple directives.
    243   for (ParseHelper PH(CommentStart, CommentStart+CommentLen); !PH.Done();) {
    244     // search for token: expected
    245     if (!PH.Search("expected"))
    246       break;
    247     PH.Advance();
    248 
    249     // next token: -
    250     if (!PH.Next("-"))
    251       continue;
    252     PH.Advance();
    253 
    254     // next token: { error | warning | note }
    255     DirectiveList* DL = NULL;
    256     if (PH.Next("error"))
    257       DL = &ED.Errors;
    258     else if (PH.Next("warning"))
    259       DL = &ED.Warnings;
    260     else if (PH.Next("note"))
    261       DL = &ED.Notes;
    262     else
    263       continue;
    264     PH.Advance();
    265 
    266     // default directive kind
    267     bool RegexKind = false;
    268     const char* KindStr = "string";
    269 
    270     // next optional token: -
    271     if (PH.Next("-re")) {
    272       PH.Advance();
    273       RegexKind = true;
    274       KindStr = "regex";
    275     }
    276 
    277     // skip optional whitespace
    278     PH.SkipWhitespace();
    279 
    280     // next optional token: positive integer
    281     unsigned Count = 1;
    282     if (PH.Next(Count))
    283       PH.Advance();
    284 
    285     // skip optional whitespace
    286     PH.SkipWhitespace();
    287 
    288     // next token: {{
    289     if (!PH.Next("{{")) {
    290       PP.Diag(Pos.getLocWithOffset(PH.C-PH.Begin),
    291               diag::err_verify_missing_start) << KindStr;
    292       continue;
    293     }
    294     PH.Advance();
    295     const char* const ContentBegin = PH.C; // mark content begin
    296 
    297     // search for token: }}
    298     if (!PH.Search("}}")) {
    299       PP.Diag(Pos.getLocWithOffset(PH.C-PH.Begin),
    300               diag::err_verify_missing_end) << KindStr;
    301       continue;
    302     }
    303     const char* const ContentEnd = PH.P; // mark content end
    304     PH.Advance();
    305 
    306     // build directive text; convert \n to newlines
    307     std::string Text;
    308     StringRef NewlineStr = "\\n";
    309     StringRef Content(ContentBegin, ContentEnd-ContentBegin);
    310     size_t CPos = 0;
    311     size_t FPos;
    312     while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
    313       Text += Content.substr(CPos, FPos-CPos);
    314       Text += '\n';
    315       CPos = FPos + NewlineStr.size();
    316     }
    317     if (Text.empty())
    318       Text.assign(ContentBegin, ContentEnd);
    319 
    320     // construct new directive
    321     Directive *D = Directive::Create(RegexKind, Pos, Text, Count);
    322     std::string Error;
    323     if (D->isValid(Error))
    324       DL->push_back(D);
    325     else {
    326       PP.Diag(Pos.getLocWithOffset(ContentBegin-PH.Begin),
    327               diag::err_verify_invalid_content)
    328         << KindStr << Error;
    329     }
    330   }
    331 }
    332 
    333 /// FindExpectedDiags - Lex the main source file to find all of the
    334 //   expected errors and warnings.
    335 static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) {
    336   // Create a raw lexer to pull all the comments out of FID.
    337   if (FID.isInvalid())
    338     return;
    339 
    340   SourceManager& SM = PP.getSourceManager();
    341   // Create a lexer to lex all the tokens of the main file in raw mode.
    342   const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
    343   Lexer RawLex(FID, FromFile, SM, PP.getLangOptions());
    344 
    345   // Return comments as tokens, this is how we find expected diagnostics.
    346   RawLex.SetCommentRetentionState(true);
    347 
    348   Token Tok;
    349   Tok.setKind(tok::comment);
    350   while (Tok.isNot(tok::eof)) {
    351     RawLex.Lex(Tok);
    352     if (!Tok.is(tok::comment)) continue;
    353 
    354     std::string Comment = PP.getSpelling(Tok);
    355     if (Comment.empty()) continue;
    356 
    357     // Find all expected errors/warnings/notes.
    358     ParseDirective(&Comment[0], Comment.size(), ED, PP, Tok.getLocation());
    359   };
    360 }
    361 
    362 /// PrintProblem - This takes a diagnostic map of the delta between expected and
    363 /// seen diagnostics. If there's anything in it, then something unexpected
    364 /// happened. Print the map out in a nice format and return "true". If the map
    365 /// is empty and we're not going to print things, then return "false".
    366 ///
    367 static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
    368                              const_diag_iterator diag_begin,
    369                              const_diag_iterator diag_end,
    370                              const char *Kind, bool Expected) {
    371   if (diag_begin == diag_end) return 0;
    372 
    373   llvm::SmallString<256> Fmt;
    374   llvm::raw_svector_ostream OS(Fmt);
    375   for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
    376     if (I->first.isInvalid() || !SourceMgr)
    377       OS << "\n  (frontend)";
    378     else
    379       OS << "\n  Line " << SourceMgr->getPresumedLineNumber(I->first);
    380     OS << ": " << I->second;
    381   }
    382 
    383   Diags.Report(diag::err_verify_inconsistent_diags)
    384     << Kind << !Expected << OS.str();
    385   return std::distance(diag_begin, diag_end);
    386 }
    387 
    388 static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
    389                              DirectiveList &DL, const char *Kind,
    390                              bool Expected) {
    391   if (DL.empty())
    392     return 0;
    393 
    394   llvm::SmallString<256> Fmt;
    395   llvm::raw_svector_ostream OS(Fmt);
    396   for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
    397     Directive& D = **I;
    398     if (D.Location.isInvalid() || !SourceMgr)
    399       OS << "\n  (frontend)";
    400     else
    401       OS << "\n  Line " << SourceMgr->getPresumedLineNumber(D.Location);
    402     OS << ": " << D.Text;
    403   }
    404 
    405   Diags.Report(diag::err_verify_inconsistent_diags)
    406     << Kind << !Expected << OS.str();
    407   return DL.size();
    408 }
    409 
    410 /// CheckLists - Compare expected to seen diagnostic lists and return the
    411 /// the difference between them.
    412 ///
    413 static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
    414                            const char *Label,
    415                            DirectiveList &Left,
    416                            const_diag_iterator d2_begin,
    417                            const_diag_iterator d2_end) {
    418   DirectiveList LeftOnly;
    419   DiagList Right(d2_begin, d2_end);
    420 
    421   for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) {
    422     Directive& D = **I;
    423     unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.Location);
    424 
    425     for (unsigned i = 0; i < D.Count; ++i) {
    426       DiagList::iterator II, IE;
    427       for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
    428         unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
    429         if (LineNo1 != LineNo2)
    430           continue;
    431 
    432         const std::string &RightText = II->second;
    433         if (D.Match(RightText))
    434           break;
    435       }
    436       if (II == IE) {
    437         // Not found.
    438         LeftOnly.push_back(*I);
    439       } else {
    440         // Found. The same cannot be found twice.
    441         Right.erase(II);
    442       }
    443     }
    444   }
    445   // Now all that's left in Right are those that were not matched.
    446 
    447   return (PrintProblem(Diags, &SourceMgr, LeftOnly, Label, true) +
    448           PrintProblem(Diags, &SourceMgr, Right.begin(), Right.end(),
    449                        Label, false));
    450 }
    451 
    452 /// CheckResults - This compares the expected results to those that
    453 /// were actually reported. It emits any discrepencies. Return "true" if there
    454 /// were problems. Return "false" otherwise.
    455 ///
    456 static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
    457                              const TextDiagnosticBuffer &Buffer,
    458                              ExpectedData &ED) {
    459   // We want to capture the delta between what was expected and what was
    460   // seen.
    461   //
    462   //   Expected \ Seen - set expected but not seen
    463   //   Seen \ Expected - set seen but not expected
    464   unsigned NumProblems = 0;
    465 
    466   // See if there are error mismatches.
    467   NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
    468                             Buffer.err_begin(), Buffer.err_end());
    469 
    470   // See if there are warning mismatches.
    471   NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
    472                             Buffer.warn_begin(), Buffer.warn_end());
    473 
    474   // See if there are note mismatches.
    475   NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
    476                             Buffer.note_begin(), Buffer.note_end());
    477 
    478   return NumProblems;
    479 }
    480 
    481 void VerifyDiagnosticConsumer::CheckDiagnostics() {
    482   ExpectedData ED;
    483 
    484   // Ensure any diagnostics go to the primary client.
    485   bool OwnsCurClient = Diags.ownsClient();
    486   DiagnosticConsumer *CurClient = Diags.takeClient();
    487   Diags.setClient(PrimaryClient, false);
    488 
    489   // If we have a preprocessor, scan the source for expected diagnostic
    490   // markers. If not then any diagnostics are unexpected.
    491   if (CurrentPreprocessor) {
    492     SourceManager &SM = CurrentPreprocessor->getSourceManager();
    493     // Extract expected-error strings from main file.
    494     FindExpectedDiags(*CurrentPreprocessor, ED, SM.getMainFileID());
    495     // Only check for expectations in other diagnostic locations
    496     // if they are not the main file (via ID or FileEntry) - the main
    497     // file has already been looked at, and its expectations must not
    498     // be added twice.
    499     if (!FirstErrorFID.isInvalid() && FirstErrorFID != SM.getMainFileID()
    500         && (!SM.getFileEntryForID(FirstErrorFID)
    501             || (SM.getFileEntryForID(FirstErrorFID) !=
    502                 SM.getFileEntryForID(SM.getMainFileID())))) {
    503       FindExpectedDiags(*CurrentPreprocessor, ED, FirstErrorFID);
    504       FirstErrorFID = FileID();
    505     }
    506 
    507     // Check that the expected diagnostics occurred.
    508     NumErrors += CheckResults(Diags, SM, *Buffer, ED);
    509   } else {
    510     NumErrors += (PrintProblem(Diags, 0,
    511                                Buffer->err_begin(), Buffer->err_end(),
    512                                "error", false) +
    513                   PrintProblem(Diags, 0,
    514                                Buffer->warn_begin(), Buffer->warn_end(),
    515                                "warn", false) +
    516                   PrintProblem(Diags, 0,
    517                                Buffer->note_begin(), Buffer->note_end(),
    518                                "note", false));
    519   }
    520 
    521   Diags.takeClient();
    522   Diags.setClient(CurClient, OwnsCurClient);
    523 
    524   // Reset the buffer, we have processed all the diagnostics in it.
    525   Buffer.reset(new TextDiagnosticBuffer());
    526 }
    527 
    528 DiagnosticConsumer *
    529 VerifyDiagnosticConsumer::clone(DiagnosticsEngine &Diags) const {
    530   if (!Diags.getClient())
    531     Diags.setClient(PrimaryClient->clone(Diags));
    532 
    533   return new VerifyDiagnosticConsumer(Diags);
    534 }
    535 
    536 Directive* Directive::Create(bool RegexKind, const SourceLocation &Location,
    537                              const std::string &Text, unsigned Count) {
    538   if (RegexKind)
    539     return new RegexDirective(Location, Text, Count);
    540   return new StandardDirective(Location, Text, Count);
    541 }
    542