Skip to content

[FileCheck]: Fix diagnostics for NOT prefixes #78412

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 33 additions & 28 deletions llvm/lib/FileCheck/FileCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1784,7 +1784,7 @@ bool FileCheck::readCheckFile(

PatternContext->createLineVariable();

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

ImplicitNegativeChecks.push_back(
Pattern(Check::CheckNot, PatternContext.get()));
ImplicitNegativeChecks.back().parsePattern(PatternInBuffer,
"IMPLICIT-CHECK", SM, Req);
ImplicitNegativeChecks.emplace_back(
Pattern(Check::CheckNot, PatternContext.get()),
StringRef("IMPLICIT-CHECK"));
ImplicitNegativeChecks.back().DagNotPat.parsePattern(
PatternInBuffer, "IMPLICIT-CHECK", SM, Req);
}

std::vector<Pattern> DagNotMatches = ImplicitNegativeChecks;

std::vector<FileCheckString::DagNotPrefixInfo> DagNotMatches =
ImplicitNegativeChecks;
// LineNumber keeps track of the line on which CheckPrefix instances are
// found.
unsigned LineNumber = 1;
Expand Down Expand Up @@ -1926,7 +1927,7 @@ bool FileCheck::readCheckFile(

// Handle CHECK-DAG/-NOT.
if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot) {
DagNotMatches.push_back(P);
DagNotMatches.emplace_back(P, UsedPrefix);
continue;
}

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

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

bool FileCheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
const std::vector<const Pattern *> &NotStrings,
const FileCheckRequest &Req,
std::vector<FileCheckDiag> *Diags) const {
bool FileCheckString::CheckNot(
const SourceMgr &SM, StringRef Buffer,
const std::vector<const DagNotPrefixInfo *> &NotStrings,
const FileCheckRequest &Req, std::vector<FileCheckDiag> *Diags) const {
bool DirectiveFail = false;
for (const Pattern *Pat : NotStrings) {
assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
Pattern::MatchResult MatchResult = Pat->match(Buffer, SM);
if (Error Err = reportMatchResult(/*ExpectedMatch=*/false, SM, Prefix,
Pat->getLoc(), *Pat, 1, Buffer,
std::move(MatchResult), Req, Diags)) {
for (auto NotInfo : NotStrings) {
assert((NotInfo->DagNotPat.getCheckTy() == Check::CheckNot) &&
"Expect CHECK-NOT!");
Pattern::MatchResult MatchResult = NotInfo->DagNotPat.match(Buffer, SM);
if (Error Err = reportMatchResult(
/*ExpectedMatch=*/false, SM, NotInfo->DagNotPrefix,
NotInfo->DagNotPat.getLoc(), NotInfo->DagNotPat, 1, Buffer,
std::move(MatchResult), Req, Diags)) {
cantFail(handleErrors(std::move(Err), [&](const ErrorReported &E) {}));
DirectiveFail = true;
continue;
Expand All @@ -2321,10 +2324,11 @@ bool FileCheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
return DirectiveFail;
}

size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
std::vector<const Pattern *> &NotStrings,
const FileCheckRequest &Req,
std::vector<FileCheckDiag> *Diags) const {
size_t
FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
std::vector<const DagNotPrefixInfo *> &NotStrings,
const FileCheckRequest &Req,
std::vector<FileCheckDiag> *Diags) const {
if (DagNotStrings.empty())
return 0;

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

if (Pat.getCheckTy() == Check::CheckNot) {
NotStrings.push_back(&Pat);
NotStrings.push_back(&*PatItr);
continue;
}

Expand All @@ -2367,7 +2372,7 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
// With a group of CHECK-DAGs, a single mismatching means the match on
// that group of CHECK-DAGs fails immediately.
if (MatchResult.TheError || Req.VerboseVerbose) {
if (Error Err = reportMatchResult(/*ExpectedMatch=*/true, SM, Prefix,
if (Error Err = reportMatchResult(/*ExpectedMatch=*/true, SM, DNPrefix,
Pat.getLoc(), Pat, 1, MatchBuffer,
std::move(MatchResult), Req, Diags)) {
cantFail(
Expand Down Expand Up @@ -2430,13 +2435,13 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
}
if (!Req.VerboseVerbose)
cantFail(printMatch(
/*ExpectedMatch=*/true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer,
/*ExpectedMatch=*/true, SM, DNPrefix, Pat.getLoc(), Pat, 1, Buffer,
Pattern::MatchResult(MatchPos, MatchLen, Error::success()), Req,
Diags));

// Handle the end of a CHECK-DAG group.
if (std::next(PatItr) == PatEnd ||
std::next(PatItr)->getCheckTy() == Check::CheckNot) {
std::next(PatItr)->DagNotPat.getCheckTy() == Check::CheckNot) {
if (!NotStrings.empty()) {
// If there are CHECK-NOTs between two CHECK-DAGs or from CHECK to
// CHECK-DAG, verify that there are no 'not' strings occurred in that
Expand Down
20 changes: 15 additions & 5 deletions llvm/lib/FileCheck/FileCheckImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -823,9 +823,19 @@ struct FileCheckString {
/// The location in the match file that the check string was specified.
SMLoc Loc;

/// All of the strings that are disallowed from occurring between this match
/// string and the previous one (or start of file).
std::vector<Pattern> DagNotStrings;
/// Hold the information about the DAG/NOT strings in the program, which are
/// not explicitly stored otherwise. This allows for better and more accurate
/// diagnostic messages.
struct DagNotPrefixInfo {
Pattern DagNotPat;
StringRef DagNotPrefix;

DagNotPrefixInfo(const Pattern &P, StringRef S)
: DagNotPat(P), DagNotPrefix(S) {}
};

/// Hold the DAG/NOT strings occurring in the input file.
std::vector<DagNotPrefixInfo> DagNotStrings;

FileCheckString(const Pattern &P, StringRef S, SMLoc L)
: Pat(P), Prefix(S), Loc(L) {}
Expand All @@ -845,12 +855,12 @@ struct FileCheckString {
/// \p Buffer. Errors are reported against \p SM and diagnostics recorded in
/// \p Diags according to the verbosity level set in \p Req.
bool CheckNot(const SourceMgr &SM, StringRef Buffer,
const std::vector<const Pattern *> &NotStrings,
const std::vector<const DagNotPrefixInfo *> &NotStrings,
const FileCheckRequest &Req,
std::vector<FileCheckDiag> *Diags) const;
/// Matches "dag strings" and their mixed "not strings".
size_t CheckDag(const SourceMgr &SM, StringRef Buffer,
std::vector<const Pattern *> &NotStrings,
std::vector<const DagNotPrefixInfo *> &NotStrings,
const FileCheckRequest &Req,
std::vector<FileCheckDiag> *Diags) const;
};
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/FileCheck/check-ignore-case.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ One Line To Match
# LINE: {{o}}ne line
# LINE-SAME: {{t}}o match

# ERROR: command line:1:{{[0-9]+}}: error: CHECK-NOT: excluded string found in input
# ERROR: command line:1:{{[0-9]+}}: error: IMPLICIT-CHECK-NOT: excluded string found in input
# ERROR-NEXT: -implicit-check-not='sTrInG'
# ERROR: note: found here
69 changes: 69 additions & 0 deletions llvm/test/FileCheck/check-not-custom-prefix.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
; Test two trailing NOT strings
; RUN: rm -f %t && \
; RUN: echo "LEADING: placeholder1" >>%t && echo "MIDDLE-NOT: placeholder2" >>%t && echo "TRAILING-NOT: placeholder3" >>%t && \
; RUN: %ProtectFileCheckOutput not FileCheck --strict-whitespace --check-prefixes LEADING,MIDDLE,TRAILING --dump-input=never --input-file %t %t 2>&1 | \
; RUN: FileCheck --check-prefix TEST1 %s

; Test NOT string occurring in between two allowable strings
; RUN: rm -f %t && \
; RUN: echo "LEADING: placeholder1" >>%t && echo "MIDDLE-NOT: placeholder2" >>%t && echo "TRAILING: placeholder3" >>%t && \
; RUN: %ProtectFileCheckOutput not FileCheck --strict-whitespace --check-prefixes LEADING,MIDDLE,TRAILING --dump-input=never --input-file %t %t 2>&1 | \
; RUN: FileCheck --check-prefix TEST2 %s

; Test first prefix found being the NOT string
; RUN: rm -f %t && \
; RUN: echo "LEADING-NOT: placeholder1" >>%t && echo "MIDDLE: placeholder2" >>%t && echo "TRAILING: placeholder3" >>%t && \
; RUN: %ProtectFileCheckOutput not FileCheck --strict-whitespace --check-prefixes LEADING,MIDDLE,TRAILING --dump-input=never --input-file %t %t 2>&1 | \
; RUN: FileCheck --check-prefix TEST3 %s

; Test all given prefixes being NOT strings
; RUN: rm -f %t && \
; RUN: echo "LEADING-NOT: placeholder1" >>%t && echo "MIDDLE-NOT: placeholder2" >>%t && echo "TRAILING-NOT: placeholder3" >>%t && \
; RUN: %ProtectFileCheckOutput not FileCheck --strict-whitespace --check-prefixes LEADING,MIDDLE,TRAILING --dump-input=never --input-file %t %t 2>&1 | \
; RUN: FileCheck --check-prefix TEST4 %s

; TEST1: error: MIDDLE-NOT: excluded string found in input
; TEST1-NEXT: MIDDLE-NOT: placeholder2
; TEST1-NEXT: {{^}} ^{{$}}
; TEST1-NEXT: note: found here
; TEST1-NEXT: MIDDLE-NOT: placeholder2
; TEST1-NEXT: {{^}} ^~~~~~~~~~~~{{$}}
; TEST1-NEXT: error: TRAILING-NOT: excluded string found in input
; TEST1-NEXT: TRAILING-NOT: placeholder3
; TEST1-NEXT: {{^}} ^{{$}}
; TEST1-NEXT: note: found here
; TEST1-NEXT: TRAILING-NOT: placeholder3
; TEST1-NEXT: {{^}} ^~~~~~~~~~~~{{$}}

; TEST2: error: MIDDLE-NOT: excluded string found in input
; TEST2-NEXT: MIDDLE-NOT: placeholder2
; TEST2-NEXT: {{^}} ^{{$}}
; TEST2-NEXT: note: found here
; TEST2-NEXT: MIDDLE-NOT: placeholder2
; TEST2-NEXT: {{^}} ^~~~~~~~~~~~{{$}}

; TEST3: error: LEADING-NOT: excluded string found in input
; TEST3-NEXT: LEADING-NOT: placeholder1
; TEST3-NEXT: {{^}} ^{{$}}
; TEST3-NEXT: note: found here
; TEST3-NEXT: LEADING-NOT: placeholder1
; TEST3-NEXT: {{^}} ^~~~~~~~~~~~{{$}}

; TEST4: error: LEADING-NOT: excluded string found in input
; TEST4-NEXT: LEADING-NOT: placeholder1
; TEST4-NEXT: {{^}} ^{{$}}
; TEST4-NEXT: note: found here
; TEST4-NEXT: LEADING-NOT: placeholder1
; TEST4-NEXT: {{^}} ^~~~~~~~~~~~{{$}}
; TEST4-NEXT: error: MIDDLE-NOT: excluded string found in input
; TEST4-NEXT: MIDDLE-NOT: placeholder2
; TEST4-NEXT: {{^}} ^{{$}}
; TEST4-NEXT: note: found here
; TEST4-NEXT: MIDDLE-NOT: placeholder2
; TEST4-NEXT: {{^}} ^~~~~~~~~~~~{{$}}
; TEST4-NEXT: error: TRAILING-NOT: excluded string found in input
; TEST4-NEXT: TRAILING-NOT: placeholder3
; TEST4-NEXT: {{^}} ^{{$}}
; TEST4-NEXT: note: found here
; TEST4-NEXT: TRAILING-NOT: placeholder3
; TEST4-NEXT: {{^}} ^~~~~~~~~~~~{{$}}
2 changes: 1 addition & 1 deletion llvm/test/FileCheck/dump-input/annotations.txt
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@
; RUN: -implicit-check-not='{{remark:|error:}}'

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

; IMPNOT:<<<<<<
; IMPNOT-NEXT: 1: hello world again!
Expand Down
14 changes: 7 additions & 7 deletions llvm/test/FileCheck/implicit-check-not.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,36 @@

warning: aaa
; CHECK-PASS: warning: aaa
; CHECK-ERROR1: command line:1:22: error: CHECK-FAIL1-NOT: excluded string found in input
; CHECK-ERROR1: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
; CHECK-ERROR1-NEXT: -implicit-check-not='warning:'
; CHECK-ERROR1: note: found here
; CHECK-FAIL2: warning: aaa
; CHECK-FAIL3: warning: aaa
; CHECK-ERROR4: command line:1:22: error: CHECK-FAIL1-NOT: excluded string found in input
; CHECK-ERROR4: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
; CHECK-ERROR4-NEXT: {{-implicit-check-not='\{\{aaa\|bbb\|ccc\}\}'}}
; CHECK-ERROR4: note: found here
; CHECK-ERROR5: command line:1:22: error: CHECK-FAIL1-NOT: excluded string found in input
; CHECK-ERROR5: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
; CHECK-ERROR5-NEXT: -implicit-check-not='aaa'
; CHECK-ERROR5: note: found here

warning: bbb
; CHECK-PASS: warning: bbb
; CHECK-FAIL1: warning: bbb
; CHECK-ERROR2: command line:1:22: error: CHECK-FAIL2-NOT: excluded string found in input
; CHECK-ERROR2: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
; CHECK-ERROR2-NEXT: -implicit-check-not='warning:'
; CHECK-ERROR2: note: found here
; CHECK-FAIL3: warning: bbb
; CHECK-ERROR6: command line:1:22: error: CHECK-FAIL2-NOT: excluded string found in input
; CHECK-ERROR6: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
; CHECK-ERROR6-NEXT: -implicit-check-not='bbb'
; CHECK-ERROR6: note: found here

warning: ccc
; CHECK-PASS: warning: ccc
; CHECK-FAIL1: warning: ccc
; CHECK-FAIL2: warning: ccc
; CHECK-ERROR3: command line:1:22: error: CHECK-FAIL3-NOT: excluded string found in input
; CHECK-ERROR3: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
; CHECK-ERROR3-NEXT: -implicit-check-not='warning:'
; CHECK-ERROR3: note: found here
; CHECK-ERROR7: command line:1:22: error: CHECK-FAIL3-NOT: excluded string found in input
; CHECK-ERROR7: command line:1:22: error: IMPLICIT-CHECK-NOT: excluded string found in input
; CHECK-ERROR7-NEXT: -implicit-check-not='ccc'
; CHECK-ERROR7: note: found here