summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/include/llvm/Support/FileCheck.h11
-rw-r--r--llvm/lib/Support/FileCheck.cpp12
-rw-r--r--llvm/test/FileCheck/dump-input-annotations.txt46
-rw-r--r--llvm/utils/FileCheck/FileCheck.cpp51
4 files changed, 86 insertions, 34 deletions
diff --git a/llvm/include/llvm/Support/FileCheck.h b/llvm/include/llvm/Support/FileCheck.h
index a230b279f6e..ace90cec8fb 100644
--- a/llvm/include/llvm/Support/FileCheck.h
+++ b/llvm/include/llvm/Support/FileCheck.h
@@ -127,7 +127,8 @@ public:
const StringMap<StringRef> &VariableTable,
SMRange MatchRange = None) const;
void PrintFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
- const StringMap<StringRef> &VariableTable) const;
+ const StringMap<StringRef> &VariableTable,
+ std::vector<FileCheckDiag> *Diags) const;
bool hasVariable() const {
return !(VariableUses.empty() && VariableDefs.empty());
@@ -157,13 +158,19 @@ struct FileCheckDiag {
/// Where is the FileCheck directive for this diagnostic?
unsigned CheckLine, CheckCol;
/// What kind of match result does this diagnostic describe?
+ ///
+ /// There might be more than one of these for the same directive. For
+ /// example, there might be a fuzzy match after a fail.
enum MatchType {
// TODO: More members will appear with later patches in this series.
/// Indicates no match for an expected pattern.
MatchNoneButExpected,
+ /// Indicates a possible intended match because there's no perfect match.
+ MatchFuzzy,
MatchTypeCount,
} MatchTy;
- /// The search range.
+ /// The match range if MatchTy is not MatchNoneButExpected, or the search
+ /// range otherwise.
unsigned InputStartLine, InputStartCol, InputEndLine, InputEndCol;
FileCheckDiag(const SourceMgr &SM, const Check::FileCheckType &CheckTy,
SMLoc CheckLoc, MatchType MatchTy, SMRange InputRange);
diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp
index ce60b0bf9c3..49e2500a63a 100644
--- a/llvm/lib/Support/FileCheck.cpp
+++ b/llvm/lib/Support/FileCheck.cpp
@@ -429,7 +429,8 @@ static SMRange ProcessMatchResult(FileCheckDiag::MatchType MatchTy,
void FileCheckPattern::PrintFuzzyMatch(
const SourceMgr &SM, StringRef Buffer,
- const StringMap<StringRef> &VariableTable) const {
+ const StringMap<StringRef> &VariableTable,
+ std::vector<FileCheckDiag> *Diags) const {
// Attempt to find the closest/best fuzzy match. Usually an error happens
// because some string in the output didn't exactly match. In these cases, we
// would like to show the user a best guess at what "should have" matched, to
@@ -463,8 +464,11 @@ void FileCheckPattern::PrintFuzzyMatch(
// reasonable and not equal to what we showed in the "scanning from here"
// line.
if (Best && Best != StringRef::npos && BestQuality < 50) {
- SM.PrintMessage(SMLoc::getFromPointer(Buffer.data() + Best),
- SourceMgr::DK_Note, "possible intended match here");
+ SMRange MatchRange =
+ ProcessMatchResult(FileCheckDiag::MatchFuzzy, SM, getLoc(),
+ getCheckTy(), Buffer, Best, 0, Diags);
+ SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note,
+ "possible intended match here");
// FIXME: If we wanted to be really friendly we would show why the match
// failed, as it can be hard to spot simple one character differences.
@@ -956,7 +960,7 @@ static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
// Allow the pattern to print additional information if desired.
Pat.PrintVariableUses(SM, Buffer, VariableTable);
if (ExpectedMatch)
- Pat.PrintFuzzyMatch(SM, Buffer, VariableTable);
+ Pat.PrintFuzzyMatch(SM, Buffer, VariableTable, Diags);
}
static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
diff --git a/llvm/test/FileCheck/dump-input-annotations.txt b/llvm/test/FileCheck/dump-input-annotations.txt
index 1d6740622eb..94c35e2c9f6 100644
--- a/llvm/test/FileCheck/dump-input-annotations.txt
+++ b/llvm/test/FileCheck/dump-input-annotations.txt
@@ -1,5 +1,5 @@
;--------------------------------------------------
-; Use -strict-whitespace to check marker alignment here.
+; Use -strict-whitespace to check marker and note alignment here.
; (Also check multiline marker where start/end columns vary across lines.)
;
; In the remaining checks, don't use -strict-whitespace and thus check just the
@@ -10,6 +10,7 @@
; RUN: echo 'hello world' > %t.in
; RUN: echo 'goodbye' >> %t.in
; RUN: echo 'world' >> %t.in
+; RUN: echo 'unicorn' >> %t.in
; RUN: echo 'CHECK: hello' > %t.chk
; RUN: echo 'CHECK: universe' >> %t.chk
@@ -19,17 +20,20 @@
; ALIGN:Full input was:
; ALIGN-NEXT:<<<<<<
-; ALIGN-NEXT: 1: hello world
-; ALIGN-NEXT:check:2 X~~~~
-; ALIGN-NEXT: 2: goodbye
-; ALIGN-NEXT:check:2 ~~~~~~~
-; ALIGN-NEXT: 3: world
-; ALIGN-NEXT:check:2 ~~~~~ error: no match found
+; ALIGN-NEXT: 1: hello world
+; ALIGN-NEXT:check:2'0 X~~~~
+; ALIGN-NEXT: 2: goodbye
+; ALIGN-NEXT:check:2'0 ~~~~~~~
+; ALIGN-NEXT: 3: world
+; ALIGN-NEXT:check:2'0 ~~~~~
+; ALIGN-NEXT: 4: unicorn
+; ALIGN-NEXT:check:2'0 ~~~~~~~ error: no match found
+; ALIGN-NEXT:check:2'1 ? possible intended match
; ALIGN-NEXT:>>>>>>
; ALIGN-NOT:{{.}}
;--------------------------------------------------
-; CHECK (also: multi-line search range)
+; CHECK (also: multi-line search range, fuzzy match)
;--------------------------------------------------
; Good match and no match.
@@ -49,11 +53,12 @@
; RUN: | FileCheck -match-full-lines %s -check-prefixes=CHK,CHK-V
; CHK: <<<<<<
-; CHK-NEXT: 1: hello
-; CHK-NEXT: 2: again
-; CHK-NEXT: check:2 X~~~~
-; CHK-NEXT: 3: whirled
-; CHK-NEXT: check:2 ~~~~~~~ error: no match found
+; CHK-NEXT: 1: hello
+; CHK-NEXT: 2: again
+; CHK-NEXT: check:2'0 X~~~~
+; CHK-NEXT: 3: whirled
+; CHK-NEXT: check:2'0 ~~~~~~~ error: no match found
+; CHK-NEXT: check:2'1 ? possible intended match
; CHK-NEXT: >>>>>>
; CHK-NOT: {{.}}
@@ -228,13 +233,14 @@
; RUN: | FileCheck -match-full-lines %s -check-prefixes=LAB,LAB-V,LAB-VV
; LAB: <<<<<<
-; LAB-NEXT: 1: lab0
-; LAB-NEXT: 2: foo
-; LAB-NEXT: label:3 X~~
-; LAB-NEXT: 3: lab1
-; LAB-NEXT: label:3 ~~~~
-; LAB-NEXT: 4: bar
-; LAB-NEXT: label:3 ~~~ error: no match found
+; LAB-NEXT: 1: lab0
+; LAB-NEXT: 2: foo
+; LAB-NEXT: label:3'0 X~~
+; LAB-NEXT: 3: lab1
+; LAB-NEXT: label:3'0 ~~~~
+; LAB-NEXT: label:3'1 ? possible intended match
+; LAB-NEXT: 4: bar
+; LAB-NEXT: label:3'0 ~~~ error: no match found
; LAB-NEXT: >>>>>>
; LAB-NOT: {{.}}
diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp
index 33449147fcc..c6846f7da71 100644
--- a/llvm/utils/FileCheck/FileCheck.cpp
+++ b/llvm/utils/FileCheck/FileCheck.cpp
@@ -145,6 +145,8 @@ static MarkerStyle GetMarker(FileCheckDiag::MatchType MatchTy) {
switch (MatchTy) {
case FileCheckDiag::MatchNoneButExpected:
return MarkerStyle('X', raw_ostream::RED, "error: no match found");
+ case FileCheckDiag::MatchFuzzy:
+ return MarkerStyle('?', raw_ostream::MAGENTA, "possible intended match");
case FileCheckDiag::MatchTypeCount:
llvm_unreachable_internal("unexpected match type");
}
@@ -164,18 +166,28 @@ static void DumpInputAnnotationHelp(raw_ostream &OS) {
// Labels for annotation lines.
OS << " - ";
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L";
- OS << " labels the match result for a pattern of type T from "
+ OS << " labels the only match result for a pattern of type T from "
<< "line L of\n"
<< " the check file\n";
+ OS << " - ";
+ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "T:L'N";
+ OS << " labels the Nth match result for a pattern of type T from line "
+ << "L of\n"
+ << " the check file\n";
// Markers on annotation lines.
OS << " - ";
WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "X~~";
- OS << " marks search range when no match is found\n";
+ OS << " marks search range when no match is found\n"
+ << " - ";
+ WithColor(OS, raw_ostream::SAVEDCOLOR, true) << "?";
+ OS << " marks fuzzy match when no match is found\n";
// Colors.
OS << " - colors ";
WithColor(OS, raw_ostream::RED, true) << "error";
+ OS << ", ";
+ WithColor(OS, raw_ostream::MAGENTA, true) << "fuzzy match";
OS << "\n\n"
<< "If you are not seeing color above or in input dumps, try: -color\n";
}
@@ -185,6 +197,8 @@ struct InputAnnotation {
/// The check file line (one-origin indexing) where the directive that
/// produced this annotation is located.
unsigned CheckLine;
+ /// The index of the match result for this check.
+ unsigned CheckDiagIndex;
/// The label for this annotation.
std::string Label;
/// What input line (one-origin indexing) this annotation marks. This might
@@ -234,6 +248,8 @@ std::string GetCheckTypeAbbreviation(Check::FileCheckType Ty) {
static void BuildInputAnnotations(const std::vector<FileCheckDiag> &Diags,
std::vector<InputAnnotation> &Annotations,
unsigned &LabelWidth) {
+ // How many diagnostics has the current check seen so far?
+ unsigned CheckDiagCount = 0;
// What's the widest label?
LabelWidth = 0;
for (auto DiagItr = Diags.begin(), DiagEnd = Diags.end(); DiagItr != DiagEnd;
@@ -245,6 +261,19 @@ static void BuildInputAnnotations(const std::vector<FileCheckDiag> &Diags,
llvm::raw_string_ostream Label(A.Label);
Label << GetCheckTypeAbbreviation(DiagItr->CheckTy) << ":"
<< DiagItr->CheckLine;
+ A.CheckDiagIndex = UINT_MAX;
+ auto DiagNext = std::next(DiagItr);
+ if (DiagNext != DiagEnd && DiagItr->CheckTy == DiagNext->CheckTy &&
+ DiagItr->CheckLine == DiagNext->CheckLine)
+ A.CheckDiagIndex = CheckDiagCount++;
+ else if (CheckDiagCount) {
+ A.CheckDiagIndex = CheckDiagCount;
+ CheckDiagCount = 0;
+ }
+ if (A.CheckDiagIndex != UINT_MAX)
+ Label << "'" << A.CheckDiagIndex;
+ else
+ A.CheckDiagIndex = 0;
Label.flush();
LabelWidth = std::max((std::string::size_type)LabelWidth, A.Label.size());
@@ -278,6 +307,7 @@ static void BuildInputAnnotations(const std::vector<FileCheckDiag> &Diags,
}
InputAnnotation B;
B.CheckLine = A.CheckLine;
+ B.CheckDiagIndex = A.CheckDiagIndex;
B.Label = A.Label;
B.InputLine = L;
B.Marker = Marker;
@@ -308,16 +338,21 @@ static void DumpAnnotatedInput(
//
// Second, for annotations for the same input line, sort in the order of the
// FileCheck directive's line in the check file (where there's at most one
- // directive per line). The rationale of this choice is that, for any input
- // line, this sort establishes a total order of annotations that, with
- // respect to match results, is consistent across multiple lines, thus
- // making match results easier to track from one line to the next when they
- // span multiple lines.
+ // directive per line) and then by the index of the match result for that
+ // directive. The rationale of this choice is that, for any input line, this
+ // sort establishes a total order of annotations that, with respect to match
+ // results, is consistent across multiple lines, thus making match results
+ // easier to track from one line to the next when they span multiple lines.
std::sort(Annotations.begin(), Annotations.end(),
[](const InputAnnotation &A, const InputAnnotation &B) {
if (A.InputLine != B.InputLine)
return A.InputLine < B.InputLine;
- return A.CheckLine < B.CheckLine;
+ if (A.CheckLine != B.CheckLine)
+ return A.CheckLine < B.CheckLine;
+ assert(A.CheckDiagIndex != B.CheckDiagIndex &&
+ "expected diagnostic indices to be unique within a "
+ " check line");
+ return A.CheckDiagIndex < B.CheckDiagIndex;
});
// Compute the width of the label column.
OpenPOWER on IntegriCloud