Allow -verify directives to specify a min and max count, not just "+".

void f(); // expected-note 0+ {{previous declaration is here}}
  void g(); // expected-note 0-1 {{previous declaration is here}}

The old "+" syntax is still an alias for "1+", and single numbers still work.

Patch by Andy Gibbs!

llvm-svn: 159979
This commit is contained in:
Jordan Rose 2012-07-10 02:57:26 +00:00
parent e1572eb3e2
commit b8b2ca6ffb
3 changed files with 71 additions and 27 deletions

View File

@ -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<

View File

@ -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

View File

@ -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);
}