From: Jordan Rose Date: Tue, 10 Jul 2012 02:57:03 +0000 (+0000) Subject: Allow line numbers on -verify directives. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=aa48fe80a1b2000809900a437f0819d929793002;p=clang Allow line numbers on -verify directives. // expected-warning@10 {{some text}} The line number may be absolute (as above), or relative to the current line by prefixing the number with either '+' or '-'. Patch by Andy Gibbs! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159978 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 6fd7d2264b..f8d3b10a19 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -63,6 +63,8 @@ def warn_fe_serialized_diag_failure : Warning< "unable to open file %0 for serializing diagnostics (%1)">, InGroup>; +def err_verify_missing_line : Error< + "missing or invalid line number following '@' in expected %0">; def err_verify_missing_start : Error< "cannot find start ('{{') of expected %0">; def err_verify_missing_end : Error< diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h index 0cb3b834d7..415b017b73 100644 --- a/include/clang/Basic/SourceManager.h +++ b/include/clang/Basic/SourceManager.h @@ -36,6 +36,7 @@ #define LLVM_CLANG_SOURCEMANAGER_H #include "clang/Basic/LLVM.h" +#include "clang/Basic/FileManager.h" #include "clang/Basic/SourceLocation.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/DataTypes.h" @@ -897,6 +898,13 @@ public: return getFileIDSlow(SLocOffset); } + /// \brief Return the filename of the file containing a SourceLocation. + StringRef getFilename(SourceLocation SpellingLoc) const { + if (const FileEntry *F = getFileEntryForID(getFileID(SpellingLoc))) + return F->getName(); + return StringRef(); + } + /// \brief Return the source location corresponding to the first byte of /// the specified file. SourceLocation getLocForStartOfFile(FileID FID) const { diff --git a/include/clang/Frontend/VerifyDiagnosticConsumer.h b/include/clang/Frontend/VerifyDiagnosticConsumer.h index 6462371787..0df24e2a15 100644 --- a/include/clang/Frontend/VerifyDiagnosticConsumer.h +++ b/include/clang/Frontend/VerifyDiagnosticConsumer.h @@ -44,6 +44,15 @@ class TextDiagnosticBuffer; /// You can place as many diagnostics on one line as you wish. To make the code /// more readable, you can use slash-newline to separate out the diagnostics. /// +/// Alternatively, it is possible to specify the line on which the diagnostic +/// should appear by appending "@" to "expected-", for example: +/// +/// #warning some text +/// // expected-warning@10 {{some text}} +/// +/// The line number may be absolute (as above), or relative to the current +/// line by prefixing the number with either '+' or '-'. +/// /// The simple syntax above allows each specification to match exactly one /// error. You can use the extended syntax to customize this. The extended /// syntax is "expected- {{diag text}}", where \ is one of @@ -74,13 +83,15 @@ public: /// class Directive { public: - static Directive *create(bool RegexKind, const SourceLocation &Location, + static Directive *create(bool RegexKind, SourceLocation DirectiveLoc, + SourceLocation DiagnosticLoc, StringRef Text, unsigned Count); public: /// Constant representing one or more matches aka regex "+". static const unsigned OneOrMoreCount = UINT_MAX; - SourceLocation Location; + SourceLocation DirectiveLoc; + SourceLocation DiagnosticLoc; const std::string Text; unsigned Count; @@ -94,9 +105,10 @@ public: virtual bool match(StringRef S) = 0; protected: - Directive(const SourceLocation &Location, StringRef Text, - unsigned Count) - : Location(Location), Text(Text), Count(Count) { } + Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, + StringRef Text, unsigned Count) + : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc), + Text(Text), Count(Count) { } private: Directive(const Directive&); // DO NOT IMPLEMENT diff --git a/lib/Frontend/VerifyDiagnosticConsumer.cpp b/lib/Frontend/VerifyDiagnosticConsumer.cpp index 4dc1bf33d6..11756c240d 100644 --- a/lib/Frontend/VerifyDiagnosticConsumer.cpp +++ b/lib/Frontend/VerifyDiagnosticConsumer.cpp @@ -83,9 +83,9 @@ namespace { /// class StandardDirective : public Directive { public: - StandardDirective(const SourceLocation &Location, StringRef Text, - unsigned Count) - : Directive(Location, Text, Count) { } + StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, + StringRef Text, unsigned Count) + : Directive(DirectiveLoc, DiagnosticLoc, Text, Count) { } virtual bool isValid(std::string &Error) { // all strings are considered valid; even empty ones @@ -101,9 +101,9 @@ public: /// class RegexDirective : public Directive { public: - RegexDirective(const SourceLocation &Location, StringRef Text, - unsigned Count) - : Directive(Location, Text, Count), Regex(Text) { } + RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, + StringRef Text, unsigned Count) + : Directive(DirectiveLoc, DiagnosticLoc, Text, Count), Regex(Text) { } virtual bool isValid(std::string &Error) { if (Regex.isValid(Error)) @@ -195,17 +195,17 @@ static void ParseDirective(const char *CommentStart, unsigned CommentLen, SourceLocation Pos, DiagnosticsEngine &Diags) { // A single comment may contain multiple directives. for (ParseHelper PH(CommentStart, CommentStart+CommentLen); !PH.Done();) { - // search for token: expected + // Search for token: expected if (!PH.Search("expected")) break; PH.Advance(); - // next token: - + // Next token: - if (!PH.Next("-")) continue; PH.Advance(); - // next token: { error | warning | note } + // Next token: { error | warning | note } DirectiveList* DL = NULL; if (PH.Next("error")) DL = &ED.Errors; @@ -217,21 +217,53 @@ static void ParseDirective(const char *CommentStart, unsigned CommentLen, continue; PH.Advance(); - // default directive kind + // Default directive kind. bool RegexKind = false; const char* KindStr = "string"; - // next optional token: - + // Next optional token: - if (PH.Next("-re")) { PH.Advance(); RegexKind = true; KindStr = "regex"; } - // skip optional whitespace + // Next optional token: @ + SourceLocation ExpectedLoc; + if (!PH.Next("@")) { + ExpectedLoc = Pos; + } else { + PH.Advance(); + unsigned Line = 0; + bool FoundPlus = PH.Next("+"); + if (FoundPlus || PH.Next("-")) { + // Relative to current line. + PH.Advance(); + bool Invalid = false; + unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid); + if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) { + if (FoundPlus) ExpectedLine += Line; + else ExpectedLine -= Line; + ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); + } + } else { + // Absolute line number. + if (PH.Next(Line) && Line > 0) + ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); + } + + if (ExpectedLoc.isInvalid()) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_line) << KindStr; + continue; + } + PH.Advance(); + } + + // Skip optional whitespace. PH.SkipWhitespace(); - // next optional token: positive integer or a '+'. + // Next optional token: positive integer or a '+'. unsigned Count = 1; if (PH.Next(Count)) PH.Advance(); @@ -240,10 +272,10 @@ static void ParseDirective(const char *CommentStart, unsigned CommentLen, PH.Advance(); } - // skip optional whitespace + // Skip optional whitespace. PH.SkipWhitespace(); - // next token: {{ + // Next token: {{ if (!PH.Next("{{")) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_missing_start) << KindStr; @@ -252,7 +284,7 @@ static void ParseDirective(const char *CommentStart, unsigned CommentLen, PH.Advance(); const char* const ContentBegin = PH.C; // mark content begin - // search for token: }} + // Search for token: }} if (!PH.Search("}}")) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_missing_end) << KindStr; @@ -261,7 +293,7 @@ static void ParseDirective(const char *CommentStart, unsigned CommentLen, const char* const ContentEnd = PH.P; // mark content end PH.Advance(); - // build directive text; convert \n to newlines + // Build directive text; convert \n to newlines. std::string Text; StringRef NewlineStr = "\\n"; StringRef Content(ContentBegin, ContentEnd-ContentBegin); @@ -275,8 +307,8 @@ static void ParseDirective(const char *CommentStart, unsigned CommentLen, if (Text.empty()) Text.assign(ContentBegin, ContentEnd); - // construct new directive - Directive *D = Directive::create(RegexKind, Pos, Text, Count); + // Construct new directive. + Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text, Count); std::string Error; if (D->isValid(Error)) DL->push_back(D); @@ -318,15 +350,12 @@ static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) { }; } -/// PrintProblem - This takes a diagnostic map of the delta between expected and -/// seen diagnostics. If there's anything in it, then something unexpected -/// happened. Print the map out in a nice format and return "true". If the map -/// is empty and we're not going to print things, then return "false". -/// -static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr, - const_diag_iterator diag_begin, - const_diag_iterator diag_end, - const char *Kind, bool Expected) { +/// \brief Takes a list of diagnostics that have been generated but not matched +/// by an expected-* directive and produces a diagnostic to the user from this. +static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr, + const_diag_iterator diag_begin, + const_diag_iterator diag_end, + const char *Kind) { if (diag_begin == diag_end) return 0; SmallString<256> Fmt; @@ -340,29 +369,31 @@ static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr, } Diags.Report(diag::err_verify_inconsistent_diags) - << Kind << !Expected << OS.str(); + << Kind << /*Unexpected=*/true << OS.str(); return std::distance(diag_begin, diag_end); } -static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr, - DirectiveList &DL, const char *Kind, - bool Expected) { +/// \brief Takes a list of diagnostics that were expected to have been generated +/// but were not and produces a diagnostic to the user from this. +static unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr, + DirectiveList &DL, const char *Kind) { if (DL.empty()) return 0; 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->getPresumedLineNumber(D.Location); + Directive &D = **I; + OS << "\n Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); + if (D.DirectiveLoc != D.DiagnosticLoc) + OS << " (directive at " + << SourceMgr.getFilename(D.DirectiveLoc) << ":" + << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ")"; OS << ": " << D.Text; } Diags.Report(diag::err_verify_inconsistent_diags) - << Kind << !Expected << OS.str(); + << Kind << /*Unexpected=*/false << OS.str(); return DL.size(); } @@ -379,7 +410,7 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) { Directive& D = **I; - unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.Location); + unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); bool FoundOnce = false; for (unsigned i = 0; i < D.Count; ++i) { @@ -410,9 +441,8 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, } } // Now all that's left in Right are those that were not matched. - unsigned num = PrintProblem(Diags, &SourceMgr, LeftOnly, Label, true); - num += PrintProblem(Diags, &SourceMgr, Right.begin(), Right.end(), - Label, false); + unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label); + num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label); return num; } @@ -472,15 +502,12 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() { // Check that the expected diagnostics occurred. NumErrors += CheckResults(Diags, SM, *Buffer, ED); } else { - NumErrors += (PrintProblem(Diags, 0, - Buffer->err_begin(), Buffer->err_end(), - "error", false) + - PrintProblem(Diags, 0, - Buffer->warn_begin(), Buffer->warn_end(), - "warn", false) + - PrintProblem(Diags, 0, - Buffer->note_begin(), Buffer->note_end(), - "note", false)); + NumErrors += (PrintUnexpected(Diags, 0, Buffer->err_begin(), + Buffer->err_end(), "error") + + PrintUnexpected(Diags, 0, Buffer->warn_begin(), + Buffer->warn_end(), "warn") + + PrintUnexpected(Diags, 0, Buffer->note_begin(), + Buffer->note_end(), "note")); } Diags.takeClient(); @@ -498,9 +525,10 @@ VerifyDiagnosticConsumer::clone(DiagnosticsEngine &Diags) const { return new VerifyDiagnosticConsumer(Diags); } -Directive *Directive::create(bool RegexKind, const SourceLocation &Location, - StringRef Text, unsigned Count) { +Directive *Directive::create(bool RegexKind, SourceLocation DirectiveLoc, + SourceLocation DiagnosticLoc, StringRef Text, + unsigned Count) { if (RegexKind) - return new RegexDirective(Location, Text, Count); - return new StandardDirective(Location, Text, Count); + return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Count); + return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Count); }