[llvm] [FileCheck]: Fix diagnostic for trailing CHECK-NOT (PR #78412)
Vinayak Dev via llvm-commits
llvm-commits at lists.llvm.org
Thu Jan 18 04:36:57 PST 2024
https://github.com/vinayakdsci updated https://github.com/llvm/llvm-project/pull/78412
>From 1e14adc55880a35b582019215de2b5dd965236dc Mon Sep 17 00:00:00 2001
From: Vinayak Dev <vinayakdev.sci at gmail.com>
Date: Mon, 15 Jan 2024 22:40:44 +0530
Subject: [PATCH] [FileCheck]: Fix diagnostics for NOT prefixes
---
llvm/lib/FileCheck/CMakeLists.txt | 6 +-
llvm/lib/FileCheck/FileCheck.cpp | 72 ++++++++-------
llvm/lib/FileCheck/FileCheckImpl.h | 20 +++--
llvm/test/FileCheck/check-ignore-case.txt | 2 +-
.../FileCheck/check-not-custom-prefix.txt | 89 +++++++++++++++++++
.../test/FileCheck/dump-input/annotations.txt | 2 +-
llvm/test/FileCheck/implicit-check-not.txt | 14 +--
7 files changed, 154 insertions(+), 51 deletions(-)
create mode 100644 llvm/test/FileCheck/check-not-custom-prefix.txt
diff --git a/llvm/lib/FileCheck/CMakeLists.txt b/llvm/lib/FileCheck/CMakeLists.txt
index 91c80e1482f1985..f51fb5b51d451c5 100644
--- a/llvm/lib/FileCheck/CMakeLists.txt
+++ b/llvm/lib/FileCheck/CMakeLists.txt
@@ -1,8 +1,6 @@
-add_llvm_component_library(LLVMFileCheck
+add_llvm_component_library(LLVMFileCheck
FileCheck.cpp
-
ADDITIONAL_HEADER_DIRS
- "${LLVM_MAIN_INCLUDE_DIR}/llvm/FileCheck"
-)
+ "${LLVM_MAIN_INCLUDE_DIR}/llvm/FileCheck")
target_link_libraries(LLVMFileCheck LLVMSupport)
diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp
index b728c14d288aa5b..e936277e26d16d0 100644
--- a/llvm/lib/FileCheck/FileCheck.cpp
+++ b/llvm/lib/FileCheck/FileCheck.cpp
@@ -9,8 +9,8 @@
// FileCheck does a line-by line check of a file that validates whether it
// contains the expected content. This is useful for regression tests etc.
//
-// This file implements most of the API that will be used by the FileCheck utility
-// as well as various unittests.
+// This file implements most of the API that will be used by the FileCheck
+// utility as well as various unittests.
//===----------------------------------------------------------------------===//
#include "llvm/FileCheck/FileCheck.h"
@@ -307,7 +307,7 @@ Pattern::parseVariable(StringRef &Str, const SourceMgr &SM) {
StringRef Name = Str.take_front(I);
Str = Str.substr(I);
- return VariableProperties {Name, IsPseudo};
+ return VariableProperties{Name, IsPseudo};
}
// StringRef holding all characters considered as horizontal whitespaces by
@@ -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.
@@ -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;
@@ -1903,20 +1904,22 @@ bool FileCheck::readCheckFile(
// Verify that CHECK-LABEL lines do not define or use variables
if ((CheckTy == Check::CheckLabel) && P.hasVariable()) {
- SM.PrintMessage(
- SMLoc::getFromPointer(UsedPrefixStart), SourceMgr::DK_Error,
- "found '" + UsedPrefix + "-LABEL:'"
- " with variable definition or use");
+ SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),
+ SourceMgr::DK_Error,
+ "found '" + UsedPrefix +
+ "-LABEL:'"
+ " with variable definition or use");
return true;
}
- // Verify that CHECK-NEXT/SAME/EMPTY lines have at least one CHECK line before them.
+ // Verify that CHECK-NEXT/SAME/EMPTY lines have at least one CHECK line
+ // before them.
if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame ||
CheckTy == Check::CheckEmpty) &&
CheckStrings->empty()) {
- StringRef Type = CheckTy == Check::CheckNext
- ? "NEXT"
- : CheckTy == Check::CheckEmpty ? "EMPTY" : "SAME";
+ StringRef Type = CheckTy == Check::CheckNext ? "NEXT"
+ : CheckTy == Check::CheckEmpty ? "EMPTY"
+ : "SAME";
SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart),
SourceMgr::DK_Error,
"found '" + UsedPrefix + "-" + Type +
@@ -1926,7 +1929,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;
}
@@ -1957,11 +1960,12 @@ bool FileCheck::readCheckFile(
}
// Add an EOF pattern for any trailing --implicit-check-not/CHECK-DAG/-NOTs,
- // and use the first prefix as a filler for the error message.
+ // and use the prefix from the last/trailing CHECK-NOT for the error message
if (!DagNotMatches.empty()) {
CheckStrings->emplace_back(
Pattern(Check::CheckEOF, PatternContext.get(), LineNumber + 1),
- *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data()));
+ DagNotMatches.back().DagNotPrefix,
+ SMLoc::getFromPointer(Buffer.data()));
std::swap(DagNotMatches, CheckStrings->back().DagNotStrings);
}
@@ -2165,7 +2169,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<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
@@ -2303,16 +2307,18 @@ bool FileCheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const {
}
bool FileCheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
- const std::vector<const Pattern *> &NotStrings,
+ const std::vector<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;
@@ -2322,7 +2328,7 @@ bool FileCheckString::CheckNot(const SourceMgr &SM, StringRef Buffer,
}
size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
- std::vector<const Pattern *> &NotStrings,
+ std::vector<DagNotPrefixInfo> &NotStrings,
const FileCheckRequest &Req,
std::vector<FileCheckDiag> *Diags) const {
if (DagNotStrings.empty())
@@ -2344,13 +2350,13 @@ 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;
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.emplace_back(Pat, PatItr->DagNotPrefix);
continue;
}
@@ -2436,7 +2442,7 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
// 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
diff --git a/llvm/lib/FileCheck/FileCheckImpl.h b/llvm/lib/FileCheck/FileCheckImpl.h
index c15461684ea3924..c880382e0b456e4 100644
--- a/llvm/lib/FileCheck/FileCheckImpl.h
+++ b/llvm/lib/FileCheck/FileCheckImpl.h
@@ -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) {}
@@ -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<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<DagNotPrefixInfo> &NotStrings,
const FileCheckRequest &Req,
std::vector<FileCheckDiag> *Diags) const;
};
diff --git a/llvm/test/FileCheck/check-ignore-case.txt b/llvm/test/FileCheck/check-ignore-case.txt
index c3b4d97ab5e4177..47999ff29351579 100644
--- a/llvm/test/FileCheck/check-ignore-case.txt
+++ b/llvm/test/FileCheck/check-ignore-case.txt
@@ -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
diff --git a/llvm/test/FileCheck/check-not-custom-prefix.txt b/llvm/test/FileCheck/check-not-custom-prefix.txt
new file mode 100644
index 000000000000000..33e556e65b929a2
--- /dev/null
+++ b/llvm/test/FileCheck/check-not-custom-prefix.txt
@@ -0,0 +1,89 @@
+; 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
+
+; Test explicit and implicit NOT strings mixed together
+; 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 --implicit-check-not placeholder2 --dump-input=never \
+; RUN: --input-file %t %t 2>&1 | \
+; RUN: FileCheck --check-prefix TEST5 %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: {{^}} ^~~~~~~~~~~~{{$}}
+
+; TEST5: error: IMPLICIT-CHECK-NOT: excluded string found in input
+; TEST5-NEXT: -implicit-check-not='placeholder2'
+; TEST5-NEXT: {{^}} ^{{$}}
+; TEST5-NEXT: note: found here
+; TEST5-NEXT: MIDDLE-NOT: placeholder2
+; TEST5-NEXT: {{^}} ^~~~~~~~~~~~{{$}}
+; TEST5-NEXT: error: MIDDLE-NOT: excluded string found in input
+; TEST5-NEXT: MIDDLE-NOT: placeholder2
+; TEST5-NEXT: {{^}} ^{{$}}
+; TEST5-NEXT: note: found here
+; TEST5-NEXT: MIDDLE-NOT: placeholder2
+; TEST5-NEXT: {{^}} ^~~~~~~~~~~~{{$}}
diff --git a/llvm/test/FileCheck/dump-input/annotations.txt b/llvm/test/FileCheck/dump-input/annotations.txt
index 45bf54698bca008..367ea3377e6aeb5 100644
--- a/llvm/test/FileCheck/dump-input/annotations.txt
+++ b/llvm/test/FileCheck/dump-input/annotations.txt
@@ -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!
diff --git a/llvm/test/FileCheck/implicit-check-not.txt b/llvm/test/FileCheck/implicit-check-not.txt
index 95dd9fa782df8f1..eaca0d37fcb2911 100644
--- a/llvm/test/FileCheck/implicit-check-not.txt
+++ b/llvm/test/FileCheck/implicit-check-not.txt
@@ -21,26 +21,26 @@
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
@@ -48,9 +48,9 @@ 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
More information about the llvm-commits
mailing list