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