Skip to content

Commit 497a860

Browse files
authored
[FileCheck]: Fix diagnostics for NOT prefixes (#78412)
Fixes #70221 Fix a bug in FileCheck that corrects the error message when multiple prefixes are provided through --check-prefixes and one of them is a PREFIX-NOT. Earlier, only the first of the provided prefixes was displayed as the erroneous prefix, while the actual error might be on the prefix that occurred at the end of the prefix list in the input file. Now, the right NOT prefix is shown in the error message.
1 parent 9350860 commit 497a860

File tree

6 files changed

+126
-42
lines changed

6 files changed

+126
-42
lines changed

llvm/lib/FileCheck/FileCheck.cpp

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,7 +1784,7 @@ bool FileCheck::readCheckFile(
17841784

17851785
PatternContext->createLineVariable();
17861786

1787-
std::vector<Pattern> ImplicitNegativeChecks;
1787+
std::vector<FileCheckString::DagNotPrefixInfo> ImplicitNegativeChecks;
17881788
for (StringRef PatternString : Req.ImplicitCheckNot) {
17891789
// Create a buffer with fake command line content in order to display the
17901790
// command line option responsible for the specific implicit CHECK-NOT.
@@ -1807,14 +1807,15 @@ bool FileCheck::readCheckFile(
18071807
}
18081808
}
18091809

1810-
ImplicitNegativeChecks.push_back(
1811-
Pattern(Check::CheckNot, PatternContext.get()));
1812-
ImplicitNegativeChecks.back().parsePattern(PatternInBuffer,
1813-
"IMPLICIT-CHECK", SM, Req);
1810+
ImplicitNegativeChecks.emplace_back(
1811+
Pattern(Check::CheckNot, PatternContext.get()),
1812+
StringRef("IMPLICIT-CHECK"));
1813+
ImplicitNegativeChecks.back().DagNotPat.parsePattern(
1814+
PatternInBuffer, "IMPLICIT-CHECK", SM, Req);
18141815
}
18151816

1816-
std::vector<Pattern> DagNotMatches = ImplicitNegativeChecks;
1817-
1817+
std::vector<FileCheckString::DagNotPrefixInfo> DagNotMatches =
1818+
ImplicitNegativeChecks;
18181819
// LineNumber keeps track of the line on which CheckPrefix instances are
18191820
// found.
18201821
unsigned LineNumber = 1;
@@ -1926,7 +1927,7 @@ bool FileCheck::readCheckFile(
19261927

19271928
// Handle CHECK-DAG/-NOT.
19281929
if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot) {
1929-
DagNotMatches.push_back(P);
1930+
DagNotMatches.emplace_back(P, UsedPrefix);
19301931
continue;
19311932
}
19321933

@@ -2165,7 +2166,7 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
21652166
FileCheckRequest &Req,
21662167
std::vector<FileCheckDiag> *Diags) const {
21672168
size_t LastPos = 0;
2168-
std::vector<const Pattern *> NotStrings;
2169+
std::vector<const DagNotPrefixInfo *> NotStrings;
21692170

21702171
// IsLabelScanMode is true when we are scanning forward to find CHECK-LABEL
21712172
// bounds; we have not processed variable definitions within the bounded block
@@ -2302,17 +2303,19 @@ bool FileCheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const {
23022303
return false;
23032304
}
23042305

2305-
bool FileCheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
2306-
const std::vector<const Pattern *> &NotStrings,
2307-
const FileCheckRequest &Req,
2308-
std::vector<FileCheckDiag> *Diags) const {
2306+
bool FileCheckString::CheckNot(
2307+
const SourceMgr &SM, StringRef Buffer,
2308+
const std::vector<const DagNotPrefixInfo *> &NotStrings,
2309+
const FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) const {
23092310
bool DirectiveFail = false;
2310-
for (const Pattern *Pat : NotStrings) {
2311-
assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
2312-
Pattern::MatchResult MatchResult = Pat->match(Buffer, SM);
2313-
if (Error Err = reportMatchResult(/*ExpectedMatch=*/false, SM, Prefix,
2314-
Pat->getLoc(), *Pat, 1, Buffer,
2315-
std::move(MatchResult), Req, Diags)) {
2311+
for (auto NotInfo : NotStrings) {
2312+
assert((NotInfo->DagNotPat.getCheckTy() == Check::CheckNot) &&
2313+
"Expect CHECK-NOT!");
2314+
Pattern::MatchResult MatchResult = NotInfo->DagNotPat.match(Buffer, SM);
2315+
if (Error Err = reportMatchResult(
2316+
/*ExpectedMatch=*/false, SM, NotInfo->DagNotPrefix,
2317+
NotInfo->DagNotPat.getLoc(), NotInfo->DagNotPat, 1, Buffer,
2318+
std::move(MatchResult), Req, Diags)) {
23162319
cantFail(handleErrors(std::move(Err), [&](const ErrorReported &E) {}));
23172320
DirectiveFail = true;
23182321
continue;
@@ -2321,10 +2324,11 @@ bool FileCheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
23212324
return DirectiveFail;
23222325
}
23232326

2324-
size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
2325-
std::vector<const Pattern *> &NotStrings,
2326-
const FileCheckRequest &Req,
2327-
std::vector<FileCheckDiag> *Diags) const {
2327+
size_t
2328+
FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
2329+
std::vector<const DagNotPrefixInfo *> &NotStrings,
2330+
const FileCheckRequest &Req,
2331+
std::vector<FileCheckDiag> *Diags) const {
23282332
if (DagNotStrings.empty())
23292333
return 0;
23302334

@@ -2344,13 +2348,14 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
23442348
// group, so we don't use a range-based for loop here.
23452349
for (auto PatItr = DagNotStrings.begin(), PatEnd = DagNotStrings.end();
23462350
PatItr != PatEnd; ++PatItr) {
2347-
const Pattern &Pat = *PatItr;
2351+
const Pattern &Pat = PatItr->DagNotPat;
2352+
const StringRef DNPrefix = PatItr->DagNotPrefix;
23482353
assert((Pat.getCheckTy() == Check::CheckDAG ||
23492354
Pat.getCheckTy() == Check::CheckNot) &&
23502355
"Invalid CHECK-DAG or CHECK-NOT!");
23512356

23522357
if (Pat.getCheckTy() == Check::CheckNot) {
2353-
NotStrings.push_back(&Pat);
2358+
NotStrings.push_back(&*PatItr);
23542359
continue;
23552360
}
23562361

@@ -2367,7 +2372,7 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
23672372
// With a group of CHECK-DAGs, a single mismatching means the match on
23682373
// that group of CHECK-DAGs fails immediately.
23692374
if (MatchResult.TheError || Req.VerboseVerbose) {
2370-
if (Error Err = reportMatchResult(/*ExpectedMatch=*/true, SM, Prefix,
2375+
if (Error Err = reportMatchResult(/*ExpectedMatch=*/true, SM, DNPrefix,
23712376
Pat.getLoc(), Pat, 1, MatchBuffer,
23722377
std::move(MatchResult), Req, Diags)) {
23732378
cantFail(
@@ -2430,13 +2435,13 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
24302435
}
24312436
if (!Req.VerboseVerbose)
24322437
cantFail(printMatch(
2433-
/*ExpectedMatch=*/true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer,
2438+
/*ExpectedMatch=*/true, SM, DNPrefix, Pat.getLoc(), Pat, 1, Buffer,
24342439
Pattern::MatchResult(MatchPos, MatchLen, Error::success()), Req,
24352440
Diags));
24362441

24372442
// Handle the end of a CHECK-DAG group.
24382443
if (std::next(PatItr) == PatEnd ||
2439-
std::next(PatItr)->getCheckTy() == Check::CheckNot) {
2444+
std::next(PatItr)->DagNotPat.getCheckTy() == Check::CheckNot) {
24402445
if (!NotStrings.empty()) {
24412446
// If there are CHECK-NOTs between two CHECK-DAGs or from CHECK to
24422447
// CHECK-DAG, verify that there are no 'not' strings occurred in that

llvm/lib/FileCheck/FileCheckImpl.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -823,9 +823,19 @@ struct FileCheckString {
823823
/// The location in the match file that the check string was specified.
824824
SMLoc Loc;
825825

826-
/// All of the strings that are disallowed from occurring between this match
827-
/// string and the previous one (or start of file).
828-
std::vector<Pattern> DagNotStrings;
826+
/// Hold the information about the DAG/NOT strings in the program, which are
827+
/// not explicitly stored otherwise. This allows for better and more accurate
828+
/// diagnostic messages.
829+
struct DagNotPrefixInfo {
830+
Pattern DagNotPat;
831+
StringRef DagNotPrefix;
832+
833+
DagNotPrefixInfo(const Pattern &P, StringRef S)
834+
: DagNotPat(P), DagNotPrefix(S) {}
835+
};
836+
837+
/// Hold the DAG/NOT strings occurring in the input file.
838+
std::vector<DagNotPrefixInfo> DagNotStrings;
829839

830840
FileCheckString(const Pattern &P, StringRef S, SMLoc L)
831841
: Pat(P), Prefix(S), Loc(L) {}
@@ -845,12 +855,12 @@ struct FileCheckString {
845855
/// \p Buffer. Errors are reported against \p SM and diagnostics recorded in
846856
/// \p Diags according to the verbosity level set in \p Req.
847857
bool CheckNot(const SourceMgr &SM, StringRef Buffer,
848-
const std::vector<const Pattern *> &NotStrings,
858+
const std::vector<const DagNotPrefixInfo *> &NotStrings,
849859
const FileCheckRequest &Req,
850860
std::vector<FileCheckDiag> *Diags) const;
851861
/// Matches "dag strings" and their mixed "not strings".
852862
size_t CheckDag(const SourceMgr &SM, StringRef Buffer,
853-
std::vector<const Pattern *> &NotStrings,
863+
std::vector<const DagNotPrefixInfo *> &NotStrings,
854864
const FileCheckRequest &Req,
855865
std::vector<FileCheckDiag> *Diags) const;
856866
};

llvm/test/FileCheck/check-ignore-case.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,6 @@ One Line To Match
4343
# LINE: {{o}}ne line
4444
# LINE-SAME: {{t}}o match
4545

46-
# ERROR: command line:1:{{[0-9]+}}: error: CHECK-NOT: excluded string found in input
46+
# ERROR: command line:1:{{[0-9]+}}: error: IMPLICIT-CHECK-NOT: excluded string found in input
4747
# ERROR-NEXT: -implicit-check-not='sTrInG'
4848
# ERROR: note: found here
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
; Test two trailing NOT strings
2+
; RUN: rm -f %t && \
3+
; RUN: echo "LEADING: placeholder1" >>%t && echo "MIDDLE-NOT: placeholder2" >>%t && echo "TRAILING-NOT: placeholder3" >>%t && \
4+
; RUN: %ProtectFileCheckOutput not FileCheck --strict-whitespace --check-prefixes LEADING,MIDDLE,TRAILING --dump-input=never --input-file %t %t 2>&1 | \
5+
; RUN: FileCheck --check-prefix TEST1 %s
6+
7+
; Test NOT string occurring in between two allowable strings
8+
; RUN: rm -f %t && \
9+
; RUN: echo "LEADING: placeholder1" >>%t && echo "MIDDLE-NOT: placeholder2" >>%t && echo "TRAILING: placeholder3" >>%t && \
10+
; RUN: %ProtectFileCheckOutput not FileCheck --strict-whitespace --check-prefixes LEADING,MIDDLE,TRAILING --dump-input=never --input-file %t %t 2>&1 | \
11+
; RUN: FileCheck --check-prefix TEST2 %s
12+
13+
; Test first prefix found being the NOT string
14+
; RUN: rm -f %t && \
15+
; RUN: echo "LEADING-NOT: placeholder1" >>%t && echo "MIDDLE: placeholder2" >>%t && echo "TRAILING: placeholder3" >>%t && \
16+
; RUN: %ProtectFileCheckOutput not FileCheck --strict-whitespace --check-prefixes LEADING,MIDDLE,TRAILING --dump-input=never --input-file %t %t 2>&1 | \
17+
; RUN: FileCheck --check-prefix TEST3 %s
18+
19+
; Test all given prefixes being NOT strings
20+
; RUN: rm -f %t && \
21+
; RUN: echo "LEADING-NOT: placeholder1" >>%t && echo "MIDDLE-NOT: placeholder2" >>%t && echo "TRAILING-NOT: placeholder3" >>%t && \
22+
; RUN: %ProtectFileCheckOutput not FileCheck --strict-whitespace --check-prefixes LEADING,MIDDLE,TRAILING --dump-input=never --input-file %t %t 2>&1 | \
23+
; RUN: FileCheck --check-prefix TEST4 %s
24+
25+
; TEST1: error: MIDDLE-NOT: excluded string found in input
26+
; TEST1-NEXT: MIDDLE-NOT: placeholder2
27+
; TEST1-NEXT: {{^}} ^{{$}}
28+
; TEST1-NEXT: note: found here
29+
; TEST1-NEXT: MIDDLE-NOT: placeholder2
30+
; TEST1-NEXT: {{^}} ^~~~~~~~~~~~{{$}}
31+
; TEST1-NEXT: error: TRAILING-NOT: excluded string found in input
32+
; TEST1-NEXT: TRAILING-NOT: placeholder3
33+
; TEST1-NEXT: {{^}} ^{{$}}
34+
; TEST1-NEXT: note: found here
35+
; TEST1-NEXT: TRAILING-NOT: placeholder3
36+
; TEST1-NEXT: {{^}} ^~~~~~~~~~~~{{$}}
37+
38+
; TEST2: error: MIDDLE-NOT: excluded string found in input
39+
; TEST2-NEXT: MIDDLE-NOT: placeholder2
40+
; TEST2-NEXT: {{^}} ^{{$}}
41+
; TEST2-NEXT: note: found here
42+
; TEST2-NEXT: MIDDLE-NOT: placeholder2
43+
; TEST2-NEXT: {{^}} ^~~~~~~~~~~~{{$}}
44+
45+
; TEST3: error: LEADING-NOT: excluded string found in input
46+
; TEST3-NEXT: LEADING-NOT: placeholder1
47+
; TEST3-NEXT: {{^}} ^{{$}}
48+
; TEST3-NEXT: note: found here
49+
; TEST3-NEXT: LEADING-NOT: placeholder1
50+
; TEST3-NEXT: {{^}} ^~~~~~~~~~~~{{$}}
51+
52+
; TEST4: error: LEADING-NOT: excluded string found in input
53+
; TEST4-NEXT: LEADING-NOT: placeholder1
54+
; TEST4-NEXT: {{^}} ^{{$}}
55+
; TEST4-NEXT: note: found here
56+
; TEST4-NEXT: LEADING-NOT: placeholder1
57+
; TEST4-NEXT: {{^}} ^~~~~~~~~~~~{{$}}
58+
; TEST4-NEXT: error: MIDDLE-NOT: excluded string found in input
59+
; TEST4-NEXT: MIDDLE-NOT: placeholder2
60+
; TEST4-NEXT: {{^}} ^{{$}}
61+
; TEST4-NEXT: note: found here
62+
; TEST4-NEXT: MIDDLE-NOT: placeholder2
63+
; TEST4-NEXT: {{^}} ^~~~~~~~~~~~{{$}}
64+
; TEST4-NEXT: error: TRAILING-NOT: excluded string found in input
65+
; TEST4-NEXT: TRAILING-NOT: placeholder3
66+
; TEST4-NEXT: {{^}} ^{{$}}
67+
; TEST4-NEXT: note: found here
68+
; TEST4-NEXT: TRAILING-NOT: placeholder3
69+
; TEST4-NEXT: {{^}} ^~~~~~~~~~~~{{$}}

llvm/test/FileCheck/dump-input/annotations.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@
650650
; RUN: -implicit-check-not='{{remark:|error:}}'
651651

652652
; Verbose diagnostics are suppressed but not errors.
653-
; IMPNOT:{{.*}}command line:1:22: error: CHECK-NOT: excluded string found in input
653+
; IMPNOT:{{.*}}command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
654654

655655
; IMPNOT:<<<<<<
656656
; IMPNOT-NEXT: 1: hello world again!

llvm/test/FileCheck/implicit-check-not.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,36 +21,36 @@
2121

2222
warning: aaa
2323
; CHECK-PASS: warning: aaa
24-
; CHECK-ERROR1: command line:1:22: error: CHECK-FAIL1-NOT: excluded string found in input
24+
; CHECK-ERROR1: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
2525
; CHECK-ERROR1-NEXT: -implicit-check-not='warning:'
2626
; CHECK-ERROR1: note: found here
2727
; CHECK-FAIL2: warning: aaa
2828
; CHECK-FAIL3: warning: aaa
29-
; CHECK-ERROR4: command line:1:22: error: CHECK-FAIL1-NOT: excluded string found in input
29+
; CHECK-ERROR4: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
3030
; CHECK-ERROR4-NEXT: {{-implicit-check-not='\{\{aaa\|bbb\|ccc\}\}'}}
3131
; CHECK-ERROR4: note: found here
32-
; CHECK-ERROR5: command line:1:22: error: CHECK-FAIL1-NOT: excluded string found in input
32+
; CHECK-ERROR5: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
3333
; CHECK-ERROR5-NEXT: -implicit-check-not='aaa'
3434
; CHECK-ERROR5: note: found here
3535

3636
warning: bbb
3737
; CHECK-PASS: warning: bbb
3838
; CHECK-FAIL1: warning: bbb
39-
; CHECK-ERROR2: command line:1:22: error: CHECK-FAIL2-NOT: excluded string found in input
39+
; CHECK-ERROR2: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
4040
; CHECK-ERROR2-NEXT: -implicit-check-not='warning:'
4141
; CHECK-ERROR2: note: found here
4242
; CHECK-FAIL3: warning: bbb
43-
; CHECK-ERROR6: command line:1:22: error: CHECK-FAIL2-NOT: excluded string found in input
43+
; CHECK-ERROR6: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
4444
; CHECK-ERROR6-NEXT: -implicit-check-not='bbb'
4545
; CHECK-ERROR6: note: found here
4646

4747
warning: ccc
4848
; CHECK-PASS: warning: ccc
4949
; CHECK-FAIL1: warning: ccc
5050
; CHECK-FAIL2: warning: ccc
51-
; CHECK-ERROR3: command line:1:22: error: CHECK-FAIL3-NOT: excluded string found in input
51+
; CHECK-ERROR3: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
5252
; CHECK-ERROR3-NEXT: -implicit-check-not='warning:'
5353
; CHECK-ERROR3: note: found here
54-
; CHECK-ERROR7: command line:1:22: error: CHECK-FAIL3-NOT: excluded string found in input
54+
; CHECK-ERROR7: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
5555
; CHECK-ERROR7-NEXT: -implicit-check-not='ccc'
5656
; CHECK-ERROR7: note: found here

0 commit comments

Comments
 (0)