diff options
Diffstat (limited to 'lib/Frontend/VerifyDiagnosticsClient.cpp')
-rw-r--r-- | lib/Frontend/VerifyDiagnosticsClient.cpp | 463 |
1 files changed, 319 insertions, 144 deletions
diff --git a/lib/Frontend/VerifyDiagnosticsClient.cpp b/lib/Frontend/VerifyDiagnosticsClient.cpp index 99ec910..ae36481 100644 --- a/lib/Frontend/VerifyDiagnosticsClient.cpp +++ b/lib/Frontend/VerifyDiagnosticsClient.cpp @@ -16,6 +16,7 @@ #include "clang/Frontend/TextDiagnosticBuffer.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/Regex.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -71,97 +72,267 @@ bool VerifyDiagnosticsClient::HadErrors() { typedef TextDiagnosticBuffer::DiagList DiagList; typedef TextDiagnosticBuffer::const_iterator const_diag_iterator; -/// FindDiagnostics - Go through the comment and see if it indicates expected -/// diagnostics. If so, then put them in a diagnostic list. +namespace { + +/// Directive - Abstract class representing a parsed verify directive. /// -static void FindDiagnostics(const char *CommentStart, unsigned CommentLen, - DiagList &ExpectedDiags, - Preprocessor &PP, SourceLocation Pos, - const char *ExpectedStr) { - const char *CommentEnd = CommentStart+CommentLen; - unsigned ExpectedStrLen = strlen(ExpectedStr); - - // Find all expected-foo diagnostics in the string and add them to - // ExpectedDiags. - while (CommentStart != CommentEnd) { - CommentStart = std::find(CommentStart, CommentEnd, 'e'); - if (unsigned(CommentEnd-CommentStart) < ExpectedStrLen) return; - - // If this isn't expected-foo, ignore it. - if (memcmp(CommentStart, ExpectedStr, ExpectedStrLen)) { - ++CommentStart; - continue; - } +class Directive { +public: + static Directive* Create(bool RegexKind, const SourceLocation &Location, + const std::string &Text, unsigned Count); +public: + SourceLocation Location; + const std::string Text; + unsigned Count; + + virtual ~Directive() { } + + // Returns true if directive text is valid. + // Otherwise returns false and populates E. + virtual bool isValid(std::string &Error) = 0; + + // Returns true on match. + virtual bool Match(const std::string &S) = 0; + +protected: + Directive(const SourceLocation &Location, const std::string &Text, + unsigned Count) + : Location(Location), Text(Text), Count(Count) { } + +private: + Directive(const Directive&); // DO NOT IMPLEMENT + void operator=(const Directive&); // DO NOT IMPLEMENT +}; + +/// StandardDirective - Directive with string matching. +/// +class StandardDirective : public Directive { +public: + StandardDirective(const SourceLocation &Location, const std::string &Text, + unsigned Count) + : Directive(Location, Text, Count) { } + + virtual bool isValid(std::string &Error) { + // all strings are considered valid; even empty ones + return true; + } + + virtual bool Match(const std::string &S) { + return S.find(Text) != std::string::npos || + Text.find(S) != std::string::npos; + } +}; + +/// RegexDirective - Directive with regular-expression matching. +/// +class RegexDirective : public Directive { +public: + RegexDirective(const SourceLocation &Location, const std::string &Text, + unsigned Count) + : Directive(Location, Text, Count), Regex(Text) { } + + virtual bool isValid(std::string &Error) { + if (Regex.isValid(Error)) + return true; + return false; + } + + virtual bool Match(const std::string &S) { + return Regex.match(S); + } - CommentStart += ExpectedStrLen; - - // Skip whitespace. - while (CommentStart != CommentEnd && - isspace(CommentStart[0])) - ++CommentStart; - - // Default, if we find the '{' now, is 1 time. - int Times = 1; - int Temp = 0; - // In extended syntax, there could be a digit now. - while (CommentStart != CommentEnd && - CommentStart[0] >= '0' && CommentStart[0] <= '9') { - Temp *= 10; - Temp += CommentStart[0] - '0'; - ++CommentStart; +private: + llvm::Regex Regex; +}; + +typedef std::vector<Directive*> DirectiveList; + +/// ExpectedData - owns directive objects and deletes on destructor. +/// +struct ExpectedData { + DirectiveList Errors; + DirectiveList Warnings; + DirectiveList Notes; + + ~ExpectedData() { + DirectiveList* Lists[] = { &Errors, &Warnings, &Notes, 0 }; + for (DirectiveList **PL = Lists; *PL; ++PL) { + DirectiveList * const L = *PL; + for (DirectiveList::iterator I = L->begin(), E = L->end(); I != E; ++I) + delete *I; } - if (Temp > 0) - Times = Temp; - - // Skip whitespace again. - while (CommentStart != CommentEnd && - isspace(CommentStart[0])) - ++CommentStart; - - // We should have a {{ now. - if (CommentEnd-CommentStart < 2 || - CommentStart[0] != '{' || CommentStart[1] != '{') { - if (std::find(CommentStart, CommentEnd, '{') != CommentEnd) - PP.Diag(Pos, diag::err_verify_bogus_characters); - else - PP.Diag(Pos, diag::err_verify_missing_start); - return; + } +}; + +class ParseHelper +{ +public: + ParseHelper(const char *Begin, const char *End) + : Begin(Begin), End(End), C(Begin), P(Begin), PEnd(NULL) { } + + // Return true if string literal is next. + bool Next(const std::string &S) { + std::string::size_type LEN = S.length(); + P = C; + PEnd = C + LEN; + if (PEnd > End) + return false; + return !memcmp(P, S.c_str(), LEN); + } + + // Return true if number is next. + // Output N only if number is next. + bool Next(unsigned &N) { + unsigned TMP = 0; + P = C; + for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) { + TMP *= 10; + TMP += P[0] - '0'; } - CommentStart += 2; - - // Find the }}. - const char *ExpectedEnd = CommentStart; - while (1) { - ExpectedEnd = std::find(ExpectedEnd, CommentEnd, '}'); - if (CommentEnd-ExpectedEnd < 2) { - PP.Diag(Pos, diag::err_verify_missing_end); - return; - } + if (P == C) + return false; + PEnd = P; + N = TMP; + return true; + } + + // Return true if string literal is found. + // When true, P marks begin-position of S in content. + bool Search(const std::string &S) { + P = std::search(C, End, S.begin(), S.end()); + PEnd = P + S.length(); + return P != End; + } + + // Advance 1-past previous next/search. + // Behavior is undefined if previous next/search failed. + bool Advance() { + C = PEnd; + return C < End; + } + + // Skip zero or more whitespace. + void SkipWhitespace() { + for (; C < End && isspace(*C); ++C) + ; + } + + // Return true if EOF reached. + bool Done() { + return !(C < End); + } + + const char * const Begin; // beginning of expected content + const char * const End; // end of expected content (1-past) + const char *C; // position of next char in content + const char *P; + +private: + const char *PEnd; // previous next/search subject end (1-past) +}; + +} // namespace anonymous + +/// ParseDirective - Go through the comment and see if it indicates expected +/// diagnostics. If so, then put them in the appropriate directive list. +/// +static void ParseDirective(const char *CommentStart, unsigned CommentLen, + ExpectedData &ED, Preprocessor &PP, + SourceLocation Pos) { + // A single comment may contain multiple directives. + for (ParseHelper PH(CommentStart, CommentStart+CommentLen); !PH.Done();) { + // search for token: expected + if (!PH.Search("expected")) + break; + PH.Advance(); + + // next token: - + if (!PH.Next("-")) + continue; + PH.Advance(); + + // next token: { error | warning | note } + DirectiveList* DL = NULL; + if (PH.Next("error")) + DL = &ED.Errors; + else if (PH.Next("warning")) + DL = &ED.Warnings; + else if (PH.Next("note")) + DL = &ED.Notes; + else + continue; + PH.Advance(); - if (ExpectedEnd[1] == '}') - break; + // default directive kind + bool RegexKind = false; + const char* KindStr = "string"; - ++ExpectedEnd; // Skip over singular }'s + // next optional token: - + if (PH.Next("-re")) { + PH.Advance(); + RegexKind = true; + KindStr = "regex"; } - std::string Msg(CommentStart, ExpectedEnd); - std::string::size_type FindPos; - while ((FindPos = Msg.find("\\n")) != std::string::npos) - Msg.replace(FindPos, 2, "\n"); - // Add is possibly multiple times. - for (int i = 0; i < Times; ++i) - ExpectedDiags.push_back(std::make_pair(Pos, Msg)); + // skip optional whitespace + PH.SkipWhitespace(); + + // next optional token: positive integer + unsigned Count = 1; + if (PH.Next(Count)) + PH.Advance(); + + // skip optional whitespace + PH.SkipWhitespace(); + + // next token: {{ + if (!PH.Next("{{")) { + PP.Diag(Pos.getFileLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_start) << KindStr; + continue; + } + PH.Advance(); + const char* const ContentBegin = PH.C; // mark content begin - CommentStart = ExpectedEnd; + // search for token: }} + if (!PH.Search("}}")) { + PP.Diag(Pos.getFileLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_end) << KindStr; + continue; + } + const char* const ContentEnd = PH.P; // mark content end + PH.Advance(); + + // build directive text; convert \n to newlines + std::string Text; + llvm::StringRef NewlineStr = "\\n"; + llvm::StringRef Content(ContentBegin, ContentEnd-ContentBegin); + size_t CPos = 0; + size_t FPos; + while ((FPos = Content.find(NewlineStr, CPos)) != llvm::StringRef::npos) { + Text += Content.substr(CPos, FPos-CPos); + Text += '\n'; + CPos = FPos + NewlineStr.size(); + } + if (Text.empty()) + Text.assign(ContentBegin, ContentEnd); + + // construct new directive + Directive *D = Directive::Create(RegexKind, Pos, Text, Count); + std::string Error; + if (D->isValid(Error)) + DL->push_back(D); + else { + PP.Diag(Pos.getFileLocWithOffset(ContentBegin-PH.Begin), + diag::err_verify_invalid_content) + << KindStr << Error; + } } } /// FindExpectedDiags - Lex the main source file to find all of the // expected errors and warnings. -static void FindExpectedDiags(Preprocessor &PP, - DiagList &ExpectedErrors, - DiagList &ExpectedWarnings, - DiagList &ExpectedNotes) { +static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED) { // Create a raw lexer to pull all the comments out of the main file. We don't // want to look in #include'd headers for expected-error strings. SourceManager &SM = PP.getSourceManager(); @@ -185,17 +356,8 @@ static void FindExpectedDiags(Preprocessor &PP, std::string Comment = PP.getSpelling(Tok); if (Comment.empty()) continue; - // Find all expected errors. - FindDiagnostics(&Comment[0], Comment.size(), ExpectedErrors, PP, - Tok.getLocation(), "expected-error"); - - // Find all expected warnings. - FindDiagnostics(&Comment[0], Comment.size(), ExpectedWarnings, PP, - Tok.getLocation(), "expected-warning"); - - // Find all expected notes. - FindDiagnostics(&Comment[0], Comment.size(), ExpectedNotes, PP, - Tok.getLocation(), "expected-note"); + // Find all expected errors/warnings/notes. + ParseDirective(&Comment[0], Comment.size(), ED, PP, Tok.getLocation()); }; } @@ -225,49 +387,68 @@ static unsigned PrintProblem(Diagnostic &Diags, SourceManager *SourceMgr, return std::distance(diag_begin, diag_end); } -/// CompareDiagLists - Compare two diagnostic lists and return the difference -/// between them. +static unsigned PrintProblem(Diagnostic &Diags, SourceManager *SourceMgr, + DirectiveList &DL, const char *Kind, + bool Expected) { + if (DL.empty()) + return 0; + + llvm::SmallString<256> Fmt; + llvm::raw_svector_ostream OS(Fmt); + for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) { + Directive& D = **I; + if (D.Location.isInvalid() || !SourceMgr) + OS << "\n (frontend)"; + else + OS << "\n Line " << SourceMgr->getInstantiationLineNumber(D.Location); + OS << ": " << D.Text; + } + + Diags.Report(diag::err_verify_inconsistent_diags) + << Kind << !Expected << OS.str(); + return DL.size(); +} + +/// CheckLists - Compare expected to seen diagnostic lists and return the +/// the difference between them. /// -static unsigned CompareDiagLists(Diagnostic &Diags, - SourceManager &SourceMgr, - const_diag_iterator d1_begin, - const_diag_iterator d1_end, - const_diag_iterator d2_begin, - const_diag_iterator d2_end, - const char *Label) { - DiagList LeftOnly; - DiagList Left(d1_begin, d1_end); +static unsigned CheckLists(Diagnostic &Diags, SourceManager &SourceMgr, + const char *Label, + DirectiveList &Left, + const_diag_iterator d2_begin, + const_diag_iterator d2_end) { + DirectiveList LeftOnly; DiagList Right(d2_begin, d2_end); - for (const_diag_iterator I = Left.begin(), E = Left.end(); I != E; ++I) { - unsigned LineNo1 = SourceMgr.getInstantiationLineNumber(I->first); - const std::string &Diag1 = I->second; + for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) { + Directive& D = **I; + unsigned LineNo1 = SourceMgr.getInstantiationLineNumber(D.Location); - DiagList::iterator II, IE; - for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { - unsigned LineNo2 = SourceMgr.getInstantiationLineNumber(II->first); - if (LineNo1 != LineNo2) continue; + for (unsigned i = 0; i < D.Count; ++i) { + DiagList::iterator II, IE; + for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { + unsigned LineNo2 = SourceMgr.getInstantiationLineNumber(II->first); + if (LineNo1 != LineNo2) + continue; - const std::string &Diag2 = II->second; - if (Diag2.find(Diag1) != std::string::npos || - Diag1.find(Diag2) != std::string::npos) { - break; + const std::string &RightText = II->second; + if (D.Match(RightText)) + break; + } + if (II == IE) { + // Not found. + LeftOnly.push_back(*I); + } else { + // Found. The same cannot be found twice. + Right.erase(II); } - } - if (II == IE) { - // Not found. - LeftOnly.push_back(*I); - } else { - // Found. The same cannot be found twice. - Right.erase(II); } } // Now all that's left in Right are those that were not matched. - return (PrintProblem(Diags, &SourceMgr, - LeftOnly.begin(), LeftOnly.end(), Label, true) + - PrintProblem(Diags, &SourceMgr, - Right.begin(), Right.end(), Label, false)); + return (PrintProblem(Diags, &SourceMgr, LeftOnly, Label, true) + + PrintProblem(Diags, &SourceMgr, Right.begin(), Right.end(), + Label, false)); } /// CheckResults - This compares the expected results to those that @@ -276,9 +457,7 @@ static unsigned CompareDiagLists(Diagnostic &Diags, /// static unsigned CheckResults(Diagnostic &Diags, SourceManager &SourceMgr, const TextDiagnosticBuffer &Buffer, - const DiagList &ExpectedErrors, - const DiagList &ExpectedWarnings, - const DiagList &ExpectedNotes) { + ExpectedData &ED) { // We want to capture the delta between what was expected and what was // seen. // @@ -287,31 +466,22 @@ static unsigned CheckResults(Diagnostic &Diags, SourceManager &SourceMgr, unsigned NumProblems = 0; // See if there are error mismatches. - NumProblems += CompareDiagLists(Diags, SourceMgr, - ExpectedErrors.begin(), ExpectedErrors.end(), - Buffer.err_begin(), Buffer.err_end(), - "error"); + NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors, + Buffer.err_begin(), Buffer.err_end()); // See if there are warning mismatches. - NumProblems += CompareDiagLists(Diags, SourceMgr, - ExpectedWarnings.begin(), - ExpectedWarnings.end(), - Buffer.warn_begin(), Buffer.warn_end(), - "warning"); + NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings, + Buffer.warn_begin(), Buffer.warn_end()); // See if there are note mismatches. - NumProblems += CompareDiagLists(Diags, SourceMgr, - ExpectedNotes.begin(), - ExpectedNotes.end(), - Buffer.note_begin(), Buffer.note_end(), - "note"); + NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes, + Buffer.note_begin(), Buffer.note_end()); return NumProblems; } - void VerifyDiagnosticsClient::CheckDiagnostics() { - DiagList ExpectedErrors, ExpectedWarnings, ExpectedNotes; + ExpectedData ED; // Ensure any diagnostics go to the primary client. DiagnosticClient *CurClient = Diags.getClient(); @@ -320,13 +490,11 @@ void VerifyDiagnosticsClient::CheckDiagnostics() { // If we have a preprocessor, scan the source for expected diagnostic // markers. If not then any diagnostics are unexpected. if (CurrentPreprocessor) { - FindExpectedDiags(*CurrentPreprocessor, ExpectedErrors, ExpectedWarnings, - ExpectedNotes); + FindExpectedDiags(*CurrentPreprocessor, ED); // Check that the expected diagnostics occurred. NumErrors += CheckResults(Diags, CurrentPreprocessor->getSourceManager(), - *Buffer, - ExpectedErrors, ExpectedWarnings, ExpectedNotes); + *Buffer, ED); } else { NumErrors += (PrintProblem(Diags, 0, Buffer->err_begin(), Buffer->err_end(), @@ -344,3 +512,10 @@ void VerifyDiagnosticsClient::CheckDiagnostics() { // Reset the buffer, we have processed all the diagnostics in it. Buffer.reset(new TextDiagnosticBuffer()); } + +Directive* Directive::Create(bool RegexKind, const SourceLocation &Location, + const std::string &Text, unsigned Count) { + if (RegexKind) + return new RegexDirective(Location, Text, Count); + return new StandardDirective(Location, Text, Count); +} |