/// \endcode
///
/// The path can be absolute or relative and the same search paths will be used
-/// as for #include directives.
+/// as for #include directives. The line number in an external file may be
+/// substituted with '*' meaning that any line number will match (useful where
+/// the included file is, for example, a system header where the actual line
+/// number may change and is not critical).
///
/// The simple syntax above allows each specification to match exactly one
/// error. You can use the extended syntax to customize this. The extended
class Directive {
public:
static Directive *create(bool RegexKind, SourceLocation DirectiveLoc,
- SourceLocation DiagnosticLoc,
+ SourceLocation DiagnosticLoc, bool MatchAnyLine,
StringRef Text, unsigned Min, unsigned Max);
public:
/// Constant representing n or more matches.
SourceLocation DiagnosticLoc;
const std::string Text;
unsigned Min, Max;
+ bool MatchAnyLine;
virtual ~Directive() { }
protected:
Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
- StringRef Text, unsigned Min, unsigned Max)
+ bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max)
: DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc),
- Text(Text), Min(Min), Max(Max) {
+ Text(Text), Min(Min), Max(Max), MatchAnyLine(MatchAnyLine) {
assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!");
assert(!DiagnosticLoc.isInvalid() && "DiagnosticLoc is invalid!");
}
class StandardDirective : public Directive {
public:
StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
- StringRef Text, unsigned Min, unsigned Max)
- : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max) { }
+ bool MatchAnyLine, StringRef Text, unsigned Min,
+ unsigned Max)
+ : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) { }
bool isValid(std::string &Error) override {
// all strings are considered valid; even empty ones
class RegexDirective : public Directive {
public:
RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
- StringRef Text, unsigned Min, unsigned Max, StringRef RegexStr)
- : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(RegexStr) { }
+ bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max,
+ StringRef RegexStr)
+ : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max),
+ Regex(RegexStr) { }
bool isValid(std::string &Error) override {
if (Regex.isValid(Error))
// Next optional token: @
SourceLocation ExpectedLoc;
+ bool MatchAnyLine = false;
if (!PH.Next("@")) {
ExpectedLoc = Pos;
} else {
if (PH.Next(Line) && Line > 0)
ExpectedLoc = SM.translateFileLineCol(FE, Line, 1);
+ else if (PH.Next("*")) {
+ MatchAnyLine = true;
+ ExpectedLoc = SM.translateFileLineCol(FE, 1, 1);
+ }
}
if (ExpectedLoc.isInvalid()) {
// Construct new directive.
std::unique_ptr<Directive> D(
- Directive::create(RegexKind, Pos, ExpectedLoc, Text, Min, Max));
+ Directive::create(RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text,
+ Min, Max));
std::string Error;
if (D->isValid(Error)) {
llvm::raw_svector_ostream OS(Fmt);
for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
Directive &D = **I;
- OS << "\n File " << SourceMgr.getFilename(D.DiagnosticLoc)
- << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
+ OS << "\n File " << SourceMgr.getFilename(D.DiagnosticLoc);
+ if (D.MatchAnyLine)
+ OS << " Line *";
+ else
+ OS << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
if (D.DirectiveLoc != D.DiagnosticLoc)
OS << " (directive at "
<< SourceMgr.getFilename(D.DirectiveLoc) << ':'
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);
- if (LineNo1 != LineNo2)
- continue;
+ if (!D.MatchAnyLine) {
+ unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
+ if (LineNo1 != LineNo2)
+ continue;
+ }
if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first))
continue;
}
Directive *Directive::create(bool RegexKind, SourceLocation DirectiveLoc,
- SourceLocation DiagnosticLoc, StringRef Text,
- unsigned Min, unsigned Max) {
+ SourceLocation DiagnosticLoc, bool MatchAnyLine,
+ StringRef Text, unsigned Min, unsigned Max) {
if (!RegexKind)
- return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max);
+ return new StandardDirective(DirectiveLoc, DiagnosticLoc, MatchAnyLine,
+ Text, Min, Max);
// Parse the directive into a regular expression.
std::string RegexStr;
}
}
- return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max,
- RegexStr);
+ return new RegexDirective(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text,
+ Min, Max, RegexStr);
}
// CHECK: error: no expected directives found: consider use of 'expected-no-diagnostics'
// CHECK-NEXT: error: 'error' diagnostics seen but not expected:
-// CHECK-NEXT: Line 1: header
+// CHECK-NEXT: Line 5: header
// CHECK-NEXT: Line 10: source
// CHECK-NEXT: 3 errors generated.
#endif
+
+#ifdef CHECK2
+// RUN: not %clang_cc1 -DCHECK2 -verify %s 2>&1 | FileCheck -check-prefix=CHECK2 %s
+
+// The following checks that -verify can match "any line" in an included file.
+// The location of the diagnostic need therefore only match in the file, not to
+// a specific line number. This is useful where -verify is used as a testing
+// tool for 3rd-party libraries where headers may change and the specific line
+// number of a diagnostic in a header is not important.
+
+// expected-error@verify2.h:* {{header}}
+// expected-error@verify2.h:* {{unknown}}
+
+// CHECK2: error: 'error' diagnostics expected but not seen:
+// CHECK2-NEXT: File {{.*}}verify2.h Line * (directive at {{.*}}verify2.c:32): unknown
+// CHECK2-NEXT: error: 'error' diagnostics seen but not expected:
+// CHECK2-NEXT: File {{.*}}verify2.c Line 10: source
+// CHECK2-NEXT: 2 errors generated.
+#endif