diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index f8d3b10a19e9b36680c3b05b9d18a3bfbfa67421..5a67617ab1cec957ae1156f2e79fa12a60cf596c 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -65,6 +65,8 @@ def warn_fe_serialized_diag_failure : Warning< def err_verify_missing_line : Error< "missing or invalid line number following '@' in expected %0">; +def err_verify_invalid_range : Error< + "invalid range 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/Frontend/VerifyDiagnosticConsumer.h b/include/clang/Frontend/VerifyDiagnosticConsumer.h index 0df24e2a15ad97e2a799a699eba486f66c624803..0e3bb062e184b1179fea50f16ad4cbd3ca76be08 100644 --- a/include/clang/Frontend/VerifyDiagnosticConsumer.h +++ b/include/clang/Frontend/VerifyDiagnosticConsumer.h @@ -47,8 +47,10 @@ class TextDiagnosticBuffer; /// Alternatively, it is possible to specify the line on which the diagnostic /// should appear by appending "@<line>" to "expected-<type>", for example: /// +/// \code /// #warning some text /// // expected-warning@10 {{some text}} +/// \endcode /// /// The line number may be absolute (as above), or relative to the current /// line by prefixing the number with either '+' or '-'. @@ -63,6 +65,31 @@ class TextDiagnosticBuffer; /// void f(); // expected-note 2 {{previous declaration is here}} /// \endcode /// +/// Where the diagnostic is expected to occur a minimum number of times, this +/// can be specified by appending a '+' to the number. Example: +/// +/// \code +/// void f(); // expected-note 0+ {{previous declaration is here}} +/// void g(); // expected-note 1+ {{previous declaration is here}} +/// \endcode +/// +/// In the first example, the diagnostic becomes optional, i.e. it will be +/// swallowed if it occurs, but will not generate an error if it does not +/// occur. In the second example, the diagnostic must occur at least once. +/// As a short-hand, "one or more" can be specified simply by '+'. Example: +/// +/// \code +/// void g(); // expected-note + {{previous declaration is here}} +/// \endcode +/// +/// A range can also be specified by "<n>-<m>". Example: +/// +/// \code +/// void f(); // expected-note 0-1 {{previous declaration is here}} +/// \endcode +/// +/// In this example, the diagnostic may appear only once, if at all. +/// /// Regex matching mode may be selected by appending '-re' to type. Example: /// /// expected-error-re @@ -85,15 +112,15 @@ public: public: static Directive *create(bool RegexKind, SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, - StringRef Text, unsigned Count); + StringRef Text, unsigned Min, unsigned Max); public: - /// Constant representing one or more matches aka regex "+". - static const unsigned OneOrMoreCount = UINT_MAX; + /// Constant representing n or more matches. + static const unsigned MaxCount = UINT_MAX; SourceLocation DirectiveLoc; SourceLocation DiagnosticLoc; const std::string Text; - unsigned Count; + unsigned Min, Max; virtual ~Directive() { } @@ -106,9 +133,12 @@ public: protected: Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, - StringRef Text, unsigned Count) + StringRef Text, unsigned Min, unsigned Max) : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc), - Text(Text), Count(Count) { } + Text(Text), Min(Min), Max(Max) { + assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!"); + assert(!DiagnosticLoc.isInvalid() && "DiagnosticLoc is invalid!"); + } private: Directive(const Directive&); // DO NOT IMPLEMENT diff --git a/lib/Frontend/VerifyDiagnosticConsumer.cpp b/lib/Frontend/VerifyDiagnosticConsumer.cpp index 11756c240d2de3a348a3178c476f673ed8bf6fdd..e55e75556108394b85a94bc8a9add63e0ca8aa8c 100644 --- a/lib/Frontend/VerifyDiagnosticConsumer.cpp +++ b/lib/Frontend/VerifyDiagnosticConsumer.cpp @@ -84,8 +84,8 @@ namespace { class StandardDirective : public Directive { public: StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, - StringRef Text, unsigned Count) - : Directive(DirectiveLoc, DiagnosticLoc, Text, Count) { } + StringRef Text, unsigned Min, unsigned Max) + : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max) { } virtual bool isValid(std::string &Error) { // all strings are considered valid; even empty ones @@ -102,8 +102,8 @@ public: class RegexDirective : public Directive { public: RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, - StringRef Text, unsigned Count) - : Directive(DirectiveLoc, DiagnosticLoc, Text, Count), Regex(Text) { } + StringRef Text, unsigned Min, unsigned Max) + : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(Text) { } virtual bool isValid(std::string &Error) { if (Regex.isValid(Error)) @@ -264,11 +264,29 @@ static void ParseDirective(const char *CommentStart, unsigned CommentLen, PH.SkipWhitespace(); // Next optional token: positive integer or a '+'. - unsigned Count = 1; - if (PH.Next(Count)) + unsigned Min = 1; + unsigned Max = 1; + if (PH.Next(Min)) { PH.Advance(); - else if (PH.Next("+")) { - Count = Directive::OneOrMoreCount; + // A positive integer can be followed by a '+' meaning min + // or more, or by a '-' meaning a range from min to max. + if (PH.Next("+")) { + Max = Directive::MaxCount; + PH.Advance(); + } else if (PH.Next("-")) { + PH.Advance(); + if (!PH.Next(Max) || Max < Min) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_invalid_range) << KindStr; + continue; + } + PH.Advance(); + } else { + Max = Min; + } + } else if (PH.Next("+")) { + // '+' on its own means "1 or more". + Max = Directive::MaxCount; PH.Advance(); } @@ -308,7 +326,8 @@ static void ParseDirective(const char *CommentStart, unsigned CommentLen, Text.assign(ContentBegin, ContentEnd); // Construct new directive. - Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text, Count); + Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text, + Min, Max); std::string Error; if (D->isValid(Error)) DL->push_back(D); @@ -411,9 +430,8 @@ 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.DiagnosticLoc); - bool FoundOnce = false; - for (unsigned i = 0; i < D.Count; ++i) { + for (unsigned i = 0; i < D.Max; ++i) { DiagList::iterator II, IE; for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); @@ -425,18 +443,12 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, break; } if (II == IE) { - if (D.Count == D.OneOrMoreCount) { - if (!FoundOnce) - LeftOnly.push_back(*I); - // We are only interested in at least one match, so exit the loop. - break; - } // Not found. + if (i >= D.Min) break; LeftOnly.push_back(*I); } else { // Found. The same cannot be found twice. Right.erase(II); - FoundOnce = true; } } } @@ -527,8 +539,8 @@ VerifyDiagnosticConsumer::clone(DiagnosticsEngine &Diags) const { Directive *Directive::create(bool RegexKind, SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, StringRef Text, - unsigned Count) { + unsigned Min, unsigned Max) { if (RegexKind) - return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Count); - return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Count); + return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max); + return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max); }