clang API Documentation

VerifyDiagnosticConsumer.cpp
Go to the documentation of this file.
00001 //===---- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ------===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // This is a concrete diagnostic client, which buffers the diagnostic messages.
00011 //
00012 //===----------------------------------------------------------------------===//
00013 
00014 #include "clang/Frontend/VerifyDiagnosticConsumer.h"
00015 #include "clang/Frontend/FrontendDiagnostic.h"
00016 #include "clang/Frontend/TextDiagnosticBuffer.h"
00017 #include "clang/Lex/Preprocessor.h"
00018 #include "llvm/ADT/SmallString.h"
00019 #include "llvm/Support/Regex.h"
00020 #include "llvm/Support/raw_ostream.h"
00021 #include <climits>
00022 
00023 using namespace clang;
00024 
00025 VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &_Diags)
00026   : Diags(_Diags), PrimaryClient(Diags.getClient()),
00027     OwnsPrimaryClient(Diags.ownsClient()),
00028     Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0) 
00029 {
00030   Diags.takeClient();
00031 }
00032 
00033 VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
00034   CheckDiagnostics();  
00035   Diags.takeClient();
00036   if (OwnsPrimaryClient)
00037     delete PrimaryClient;
00038 }
00039 
00040 // DiagnosticConsumer interface.
00041 
00042 void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
00043                                                const Preprocessor *PP) {
00044   // FIXME: Const hack, we screw up the preprocessor but in practice its ok
00045   // because it doesn't get reused. It would be better if we could make a copy
00046   // though.
00047   CurrentPreprocessor = const_cast<Preprocessor*>(PP);
00048 
00049   PrimaryClient->BeginSourceFile(LangOpts, PP);
00050 }
00051 
00052 void VerifyDiagnosticConsumer::EndSourceFile() {
00053   CheckDiagnostics();
00054 
00055   PrimaryClient->EndSourceFile();
00056 
00057   CurrentPreprocessor = 0;
00058 }
00059 
00060 void VerifyDiagnosticConsumer::HandleDiagnostic(
00061       DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
00062   if (FirstErrorFID.isInvalid() && Info.hasSourceManager()) {
00063     const SourceManager &SM = Info.getSourceManager();
00064     FirstErrorFID = SM.getFileID(Info.getLocation());
00065   }
00066   // Send the diagnostic to the buffer, we will check it once we reach the end
00067   // of the source file (or are destructed).
00068   Buffer->HandleDiagnostic(DiagLevel, Info);
00069 }
00070 
00071 //===----------------------------------------------------------------------===//
00072 // Checking diagnostics implementation.
00073 //===----------------------------------------------------------------------===//
00074 
00075 typedef TextDiagnosticBuffer::DiagList DiagList;
00076 typedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
00077 
00078 namespace {
00079 
00080 /// Directive - Abstract class representing a parsed verify directive.
00081 ///
00082 class Directive {
00083 public:
00084   static Directive* Create(bool RegexKind, const SourceLocation &Location,
00085                            const std::string &Text, unsigned Count);
00086 public:
00087   /// Constant representing one or more matches aka regex "+".
00088   static const unsigned OneOrMoreCount =  UINT_MAX;
00089 
00090   SourceLocation Location;
00091   const std::string Text;
00092   unsigned Count;
00093 
00094   virtual ~Directive() { }
00095 
00096   // Returns true if directive text is valid.
00097   // Otherwise returns false and populates E.
00098   virtual bool isValid(std::string &Error) = 0;
00099 
00100   // Returns true on match.
00101   virtual bool Match(const std::string &S) = 0;
00102 
00103 protected:
00104   Directive(const SourceLocation &Location, const std::string &Text,
00105             unsigned Count)
00106     : Location(Location), Text(Text), Count(Count) { }
00107 
00108 private:
00109   Directive(const Directive&); // DO NOT IMPLEMENT
00110   void operator=(const Directive&); // DO NOT IMPLEMENT
00111 };
00112 
00113 /// StandardDirective - Directive with string matching.
00114 ///
00115 class StandardDirective : public Directive {
00116 public:
00117   StandardDirective(const SourceLocation &Location, const std::string &Text,
00118                     unsigned Count)
00119     : Directive(Location, Text, Count) { }
00120 
00121   virtual bool isValid(std::string &Error) {
00122     // all strings are considered valid; even empty ones
00123     return true;
00124   }
00125 
00126   virtual bool Match(const std::string &S) {
00127     return S.find(Text) != std::string::npos;
00128   }
00129 };
00130 
00131 /// RegexDirective - Directive with regular-expression matching.
00132 ///
00133 class RegexDirective : public Directive {
00134 public:
00135   RegexDirective(const SourceLocation &Location, const std::string &Text,
00136                  unsigned Count)
00137     : Directive(Location, Text, Count), Regex(Text) { }
00138 
00139   virtual bool isValid(std::string &Error) {
00140     if (Regex.isValid(Error))
00141       return true;
00142     return false;
00143   }
00144 
00145   virtual bool Match(const std::string &S) {
00146     return Regex.match(S);
00147   }
00148 
00149 private:
00150   llvm::Regex Regex;
00151 };
00152 
00153 typedef std::vector<Directive*> DirectiveList;
00154 
00155 /// ExpectedData - owns directive objects and deletes on destructor.
00156 ///
00157 struct ExpectedData {
00158   DirectiveList Errors;
00159   DirectiveList Warnings;
00160   DirectiveList Notes;
00161 
00162   ~ExpectedData() {
00163     DirectiveList* Lists[] = { &Errors, &Warnings, &Notes, 0 };
00164     for (DirectiveList **PL = Lists; *PL; ++PL) {
00165       DirectiveList * const L = *PL;
00166       for (DirectiveList::iterator I = L->begin(), E = L->end(); I != E; ++I)
00167         delete *I;
00168     }
00169   }
00170 };  
00171 
00172 class ParseHelper
00173 {
00174 public:
00175   ParseHelper(const char *Begin, const char *End)
00176     : Begin(Begin), End(End), C(Begin), P(Begin), PEnd(NULL) { }
00177 
00178   // Return true if string literal is next.
00179   bool Next(StringRef S) {
00180     P = C;
00181     PEnd = C + S.size();
00182     if (PEnd > End)
00183       return false;
00184     return !memcmp(P, S.data(), S.size());
00185   }
00186 
00187   // Return true if number is next.
00188   // Output N only if number is next.
00189   bool Next(unsigned &N) {
00190     unsigned TMP = 0;
00191     P = C;
00192     for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) {
00193       TMP *= 10;
00194       TMP += P[0] - '0';
00195     }
00196     if (P == C)
00197       return false;
00198     PEnd = P;
00199     N = TMP;
00200     return true;
00201   }
00202 
00203   // Return true if string literal is found.
00204   // When true, P marks begin-position of S in content.
00205   bool Search(StringRef S) {
00206     P = std::search(C, End, S.begin(), S.end());
00207     PEnd = P + S.size();
00208     return P != End;
00209   }
00210 
00211   // Advance 1-past previous next/search.
00212   // Behavior is undefined if previous next/search failed.
00213   bool Advance() {
00214     C = PEnd;
00215     return C < End;
00216   }
00217 
00218   // Skip zero or more whitespace.
00219   void SkipWhitespace() {
00220     for (; C < End && isspace(*C); ++C)
00221       ;
00222   }
00223 
00224   // Return true if EOF reached.
00225   bool Done() {
00226     return !(C < End);
00227   }
00228 
00229   const char * const Begin; // beginning of expected content
00230   const char * const End;   // end of expected content (1-past)
00231   const char *C;            // position of next char in content
00232   const char *P;
00233 
00234 private:
00235   const char *PEnd; // previous next/search subject end (1-past)
00236 };
00237 
00238 } // namespace anonymous
00239 
00240 /// ParseDirective - Go through the comment and see if it indicates expected
00241 /// diagnostics. If so, then put them in the appropriate directive list.
00242 ///
00243 static void ParseDirective(const char *CommentStart, unsigned CommentLen,
00244                            ExpectedData &ED, Preprocessor &PP,
00245                            SourceLocation Pos) {
00246   // A single comment may contain multiple directives.
00247   for (ParseHelper PH(CommentStart, CommentStart+CommentLen); !PH.Done();) {
00248     // search for token: expected
00249     if (!PH.Search("expected"))
00250       break;
00251     PH.Advance();
00252 
00253     // next token: -
00254     if (!PH.Next("-"))
00255       continue;
00256     PH.Advance();
00257 
00258     // next token: { error | warning | note }
00259     DirectiveList* DL = NULL;
00260     if (PH.Next("error"))
00261       DL = &ED.Errors;
00262     else if (PH.Next("warning"))
00263       DL = &ED.Warnings;
00264     else if (PH.Next("note"))
00265       DL = &ED.Notes;
00266     else
00267       continue;
00268     PH.Advance();
00269 
00270     // default directive kind
00271     bool RegexKind = false;
00272     const char* KindStr = "string";
00273 
00274     // next optional token: -
00275     if (PH.Next("-re")) {
00276       PH.Advance();
00277       RegexKind = true;
00278       KindStr = "regex";
00279     }
00280 
00281     // skip optional whitespace
00282     PH.SkipWhitespace();
00283 
00284     // next optional token: positive integer or a '+'.
00285     unsigned Count = 1;
00286     if (PH.Next(Count))
00287       PH.Advance();
00288     else if (PH.Next("+")) {
00289       Count = Directive::OneOrMoreCount;
00290       PH.Advance();
00291     }
00292 
00293     // skip optional whitespace
00294     PH.SkipWhitespace();
00295 
00296     // next token: {{
00297     if (!PH.Next("{{")) {
00298       PP.Diag(Pos.getLocWithOffset(PH.C-PH.Begin),
00299               diag::err_verify_missing_start) << KindStr;
00300       continue;
00301     }
00302     PH.Advance();
00303     const char* const ContentBegin = PH.C; // mark content begin
00304 
00305     // search for token: }}
00306     if (!PH.Search("}}")) {
00307       PP.Diag(Pos.getLocWithOffset(PH.C-PH.Begin),
00308               diag::err_verify_missing_end) << KindStr;
00309       continue;
00310     }
00311     const char* const ContentEnd = PH.P; // mark content end
00312     PH.Advance();
00313 
00314     // build directive text; convert \n to newlines
00315     std::string Text;
00316     StringRef NewlineStr = "\\n";
00317     StringRef Content(ContentBegin, ContentEnd-ContentBegin);
00318     size_t CPos = 0;
00319     size_t FPos;
00320     while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
00321       Text += Content.substr(CPos, FPos-CPos);
00322       Text += '\n';
00323       CPos = FPos + NewlineStr.size();
00324     }
00325     if (Text.empty())
00326       Text.assign(ContentBegin, ContentEnd);
00327 
00328     // construct new directive
00329     Directive *D = Directive::Create(RegexKind, Pos, Text, Count);
00330     std::string Error;
00331     if (D->isValid(Error))
00332       DL->push_back(D);
00333     else {
00334       PP.Diag(Pos.getLocWithOffset(ContentBegin-PH.Begin),
00335               diag::err_verify_invalid_content)
00336         << KindStr << Error;
00337     }
00338   }
00339 }
00340 
00341 /// FindExpectedDiags - Lex the main source file to find all of the
00342 //   expected errors and warnings.
00343 static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) {
00344   // Create a raw lexer to pull all the comments out of FID.
00345   if (FID.isInvalid())
00346     return;
00347 
00348   SourceManager& SM = PP.getSourceManager();
00349   // Create a lexer to lex all the tokens of the main file in raw mode.
00350   const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
00351   Lexer RawLex(FID, FromFile, SM, PP.getLangOpts());
00352 
00353   // Return comments as tokens, this is how we find expected diagnostics.
00354   RawLex.SetCommentRetentionState(true);
00355 
00356   Token Tok;
00357   Tok.setKind(tok::comment);
00358   while (Tok.isNot(tok::eof)) {
00359     RawLex.Lex(Tok);
00360     if (!Tok.is(tok::comment)) continue;
00361 
00362     std::string Comment = PP.getSpelling(Tok);
00363     if (Comment.empty()) continue;
00364 
00365     // Find all expected errors/warnings/notes.
00366     ParseDirective(&Comment[0], Comment.size(), ED, PP, Tok.getLocation());
00367   };
00368 }
00369 
00370 /// PrintProblem - This takes a diagnostic map of the delta between expected and
00371 /// seen diagnostics. If there's anything in it, then something unexpected
00372 /// happened. Print the map out in a nice format and return "true". If the map
00373 /// is empty and we're not going to print things, then return "false".
00374 ///
00375 static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
00376                              const_diag_iterator diag_begin,
00377                              const_diag_iterator diag_end,
00378                              const char *Kind, bool Expected) {
00379   if (diag_begin == diag_end) return 0;
00380 
00381   SmallString<256> Fmt;
00382   llvm::raw_svector_ostream OS(Fmt);
00383   for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
00384     if (I->first.isInvalid() || !SourceMgr)
00385       OS << "\n  (frontend)";
00386     else
00387       OS << "\n  Line " << SourceMgr->getPresumedLineNumber(I->first);
00388     OS << ": " << I->second;
00389   }
00390 
00391   Diags.Report(diag::err_verify_inconsistent_diags)
00392     << Kind << !Expected << OS.str();
00393   return std::distance(diag_begin, diag_end);
00394 }
00395 
00396 static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
00397                              DirectiveList &DL, const char *Kind,
00398                              bool Expected) {
00399   if (DL.empty())
00400     return 0;
00401 
00402   SmallString<256> Fmt;
00403   llvm::raw_svector_ostream OS(Fmt);
00404   for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
00405     Directive& D = **I;
00406     if (D.Location.isInvalid() || !SourceMgr)
00407       OS << "\n  (frontend)";
00408     else
00409       OS << "\n  Line " << SourceMgr->getPresumedLineNumber(D.Location);
00410     OS << ": " << D.Text;
00411   }
00412 
00413   Diags.Report(diag::err_verify_inconsistent_diags)
00414     << Kind << !Expected << OS.str();
00415   return DL.size();
00416 }
00417 
00418 /// CheckLists - Compare expected to seen diagnostic lists and return the
00419 /// the difference between them.
00420 ///
00421 static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
00422                            const char *Label,
00423                            DirectiveList &Left,
00424                            const_diag_iterator d2_begin,
00425                            const_diag_iterator d2_end) {
00426   DirectiveList LeftOnly;
00427   DiagList Right(d2_begin, d2_end);
00428 
00429   for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) {
00430     Directive& D = **I;
00431     unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.Location);
00432     bool FoundOnce = false;
00433 
00434     for (unsigned i = 0; i < D.Count; ++i) {
00435       DiagList::iterator II, IE;
00436       for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
00437         unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
00438         if (LineNo1 != LineNo2)
00439           continue;
00440 
00441         const std::string &RightText = II->second;
00442         if (D.Match(RightText))
00443           break;
00444       }
00445       if (II == IE) {
00446         if (D.Count == D.OneOrMoreCount) {
00447           if (!FoundOnce)
00448             LeftOnly.push_back(*I);
00449           // We are only interested in at least one match, so exit the loop.
00450           break;
00451         }
00452         // Not found.
00453         LeftOnly.push_back(*I);
00454       } else {
00455         // Found. The same cannot be found twice.
00456         Right.erase(II);
00457         FoundOnce = true;
00458       }
00459     }
00460   }
00461   // Now all that's left in Right are those that were not matched.
00462   unsigned num = PrintProblem(Diags, &SourceMgr, LeftOnly, Label, true);
00463   num += PrintProblem(Diags, &SourceMgr, Right.begin(), Right.end(),
00464                       Label, false);
00465   return num;
00466 }
00467 
00468 /// CheckResults - This compares the expected results to those that
00469 /// were actually reported. It emits any discrepencies. Return "true" if there
00470 /// were problems. Return "false" otherwise.
00471 ///
00472 static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
00473                              const TextDiagnosticBuffer &Buffer,
00474                              ExpectedData &ED) {
00475   // We want to capture the delta between what was expected and what was
00476   // seen.
00477   //
00478   //   Expected \ Seen - set expected but not seen
00479   //   Seen \ Expected - set seen but not expected
00480   unsigned NumProblems = 0;
00481 
00482   // See if there are error mismatches.
00483   NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
00484                             Buffer.err_begin(), Buffer.err_end());
00485 
00486   // See if there are warning mismatches.
00487   NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
00488                             Buffer.warn_begin(), Buffer.warn_end());
00489 
00490   // See if there are note mismatches.
00491   NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
00492                             Buffer.note_begin(), Buffer.note_end());
00493 
00494   return NumProblems;
00495 }
00496 
00497 void VerifyDiagnosticConsumer::CheckDiagnostics() {
00498   ExpectedData ED;
00499 
00500   // Ensure any diagnostics go to the primary client.
00501   bool OwnsCurClient = Diags.ownsClient();
00502   DiagnosticConsumer *CurClient = Diags.takeClient();
00503   Diags.setClient(PrimaryClient, false);
00504 
00505   // If we have a preprocessor, scan the source for expected diagnostic
00506   // markers. If not then any diagnostics are unexpected.
00507   if (CurrentPreprocessor) {
00508     SourceManager &SM = CurrentPreprocessor->getSourceManager();
00509     // Extract expected-error strings from main file.
00510     FindExpectedDiags(*CurrentPreprocessor, ED, SM.getMainFileID());
00511     // Only check for expectations in other diagnostic locations
00512     // if they are not the main file (via ID or FileEntry) - the main
00513     // file has already been looked at, and its expectations must not
00514     // be added twice.
00515     if (!FirstErrorFID.isInvalid() && FirstErrorFID != SM.getMainFileID()
00516         && (!SM.getFileEntryForID(FirstErrorFID)
00517             || (SM.getFileEntryForID(FirstErrorFID) !=
00518                 SM.getFileEntryForID(SM.getMainFileID())))) {
00519       FindExpectedDiags(*CurrentPreprocessor, ED, FirstErrorFID);
00520       FirstErrorFID = FileID();
00521     }
00522 
00523     // Check that the expected diagnostics occurred.
00524     NumErrors += CheckResults(Diags, SM, *Buffer, ED);
00525   } else {
00526     NumErrors += (PrintProblem(Diags, 0,
00527                                Buffer->err_begin(), Buffer->err_end(),
00528                                "error", false) +
00529                   PrintProblem(Diags, 0,
00530                                Buffer->warn_begin(), Buffer->warn_end(),
00531                                "warn", false) +
00532                   PrintProblem(Diags, 0,
00533                                Buffer->note_begin(), Buffer->note_end(),
00534                                "note", false));
00535   }
00536 
00537   Diags.takeClient();
00538   Diags.setClient(CurClient, OwnsCurClient);
00539 
00540   // Reset the buffer, we have processed all the diagnostics in it.
00541   Buffer.reset(new TextDiagnosticBuffer());
00542 }
00543 
00544 DiagnosticConsumer *
00545 VerifyDiagnosticConsumer::clone(DiagnosticsEngine &Diags) const {
00546   if (!Diags.getClient())
00547     Diags.setClient(PrimaryClient->clone(Diags));
00548   
00549   return new VerifyDiagnosticConsumer(Diags);
00550 }
00551 
00552 Directive* Directive::Create(bool RegexKind, const SourceLocation &Location,
00553                              const std::string &Text, unsigned Count) {
00554   if (RegexKind)
00555     return new RegexDirective(Location, Text, Count);
00556   return new StandardDirective(Location, Text, Count);
00557 }