Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e5dc9ad

Browse files
authored
[FileCheck][NFC] Introduce MatchResultDiag and MatchNoteDiag (#195569)
This patch depends on PR #195568 and continues its effort to decouple the `-dump-input` presentation layer (in `llvm/utils/FileCheck/FileCheck.cpp`) and the FileCheck library's diagnostic emission (in `llvm/lib/FileCheck/FileCheck.cpp`). Similar to compiler errors/warnings/remarks vs. notes, the `FileCheckDiag` series emitted by the FileCheck library contains match results, each of which might be followed by a series of associated notes before the next match result. Without this patch series, that association is not formally modeled by `FileCheckDiag` or clearly documented, and `-dump-input` is not able to easily reason about it. This patch improves the situation by introducing two `FileCheckDiag` derived classes: `MatchResultDiag` and `MatchNoteDiag`. It extends `FileCheckDiagList` to directly associate each `MatchNoteDiag` with its `MatchResultDiag`. Thus: - `FileCheckDiagList::adjustPrevDiags` no longer has to determine that association based on directive location info. - Match result instances of `FileCheckDiag` no longer need to store a redundant custom note field, which only makes sense for notes. - Note instances of `FileCheckDiag` no longer need to store the directive type and location, which are already stored by their match result instances. - In both the FileCheck library and the `-dump-input` presentation layer, it is now clearer when a `FileCheckDiag` is a match result vs. a note.
1 parent caee04c commit e5dc9ad

4 files changed

Lines changed: 217 additions & 113 deletions

File tree

llvm/include/llvm/FileCheck/FileCheck.h

Lines changed: 117 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define LLVM_FILECHECK_FILECHECK_H
1515

1616
#include "llvm/ADT/StringRef.h"
17+
#include "llvm/Support/Casting.h"
1718
#include "llvm/Support/Compiler.h"
1819
#include "llvm/Support/Regex.h"
1920
#include "llvm/Support/SMLoc.h"
@@ -111,24 +112,29 @@ class FileCheckType {
111112
};
112113
} // namespace Check
113114

114-
/// Summary of a FileCheck diagnostic.
115-
struct FileCheckDiag {
116-
/// What is the FileCheck directive for this diagnostic?
117-
Check::FileCheckType CheckTy;
118-
/// Where is the FileCheck directive for this diagnostic?
119-
SMLoc CheckLoc;
115+
class MatchResultDiag;
116+
117+
/// Abstract base class for recording a FileCheck diagnostic for a pattern
118+
/// (e.g., \c CHECK-NEXT directive or \c --implicit-check-not).
119+
///
120+
/// \c FileCheckDiag has two direct derived classes:
121+
/// - \c MatchResultDiag records a match result for a pattern. There might be
122+
/// more than one for a single pattern. For example, for \c CHECK-DAG there
123+
/// might be several discarded matches before either a good match or a failure
124+
/// to match.
125+
/// - \c MatchNoteDiag provides an additional note about the most recent
126+
/// \c MatchResultDiag emitted by a FileCheck invocation. For example, there
127+
/// might be a fuzzy match after a failure to match.
128+
class FileCheckDiag {
129+
public:
130+
enum FileCheckDiagKind { FCDK_MatchResultDiag, FCDK_MatchNoteDiag };
131+
120132
/// What type of match result does this diagnostic describe?
121133
///
122134
/// A directive's supplied pattern is said to be either expected or excluded
123135
/// depending on whether the pattern must have or must not have a match in
124136
/// order for the directive to succeed. For example, a CHECK directive's
125137
/// pattern is expected, and a CHECK-NOT directive's pattern is excluded.
126-
///
127-
/// There might be more than one match result for a single pattern. For
128-
/// example, there might be several discarded matches
129-
/// (MatchFoundButDiscarded) before either a good match
130-
/// (MatchFoundAndExpected) or a failure to match (MatchNoneButExpected),
131-
/// and there might be a fuzzy match (MatchFuzzy) after the latter.
132138
enum MatchType {
133139
/// Indicates a good match for an expected pattern.
134140
MatchFoundAndExpected,
@@ -159,41 +165,120 @@ struct FileCheckDiag {
159165
/// Indicates a fuzzy match that serves as a suggestion for the next
160166
/// intended match for an expected pattern with too few or no good matches.
161167
MatchFuzzy,
162-
} MatchTy;
168+
};
169+
170+
private:
171+
const FileCheckDiagKind Kind;
172+
MatchType MatchTy;
173+
SMRange InputRange;
174+
175+
public:
176+
FileCheckDiag(FileCheckDiagKind Kind, MatchType MatchTy, SMRange InputRange)
177+
: Kind(Kind), MatchTy(MatchTy), InputRange(InputRange) {}
178+
/// Destructor is purely virtual to ensure this remains an abstract class.
179+
virtual ~FileCheckDiag() = 0;
180+
/// Of what derived class is this an instance?
181+
FileCheckDiagKind getKind() const { return Kind; }
182+
/// If this is a \c MatchResultDiag, return itself. If this is a
183+
/// \c MatchNoteDiag, return its associated \c MatchResultDiag.
184+
virtual const MatchResultDiag &getMatchResultDiag() const = 0;
185+
/// Adjust the match type.
186+
void adjustMatchType(MatchType MatchTy) { this->MatchTy = MatchTy; }
187+
/// Get the match type.
188+
MatchType getMatchType() const { return MatchTy; }
163189
/// The search range if MatchTy starts with MatchNone, or the match range
164190
/// otherwise.
165-
unsigned InputStartLine;
166-
unsigned InputStartCol;
167-
unsigned InputEndLine;
168-
unsigned InputEndCol;
169-
/// A note to replace the one normally indicated by MatchTy, or the empty
170-
/// string if none.
171-
std::string Note;
172-
LLVM_ABI FileCheckDiag(const SourceMgr &SM,
173-
const Check::FileCheckType &CheckTy, SMLoc CheckLoc,
174-
MatchType MatchTy, SMRange InputRange,
175-
StringRef Note = "");
191+
SMRange getInputRange() const { return InputRange; }
192+
};
193+
194+
/// Class for recording a FileCheck diagnostic that reports a match result for a
195+
/// pattern.
196+
class MatchResultDiag : public FileCheckDiag {
197+
private:
198+
Check::FileCheckType CheckTy;
199+
SMLoc CheckLoc;
200+
201+
public:
202+
MatchResultDiag(const Check::FileCheckType &CheckTy, SMLoc CheckLoc,
203+
MatchType MatchTy, SMRange InputRange)
204+
: FileCheckDiag(FCDK_MatchResultDiag, MatchTy, InputRange),
205+
CheckTy(CheckTy), CheckLoc(CheckLoc) {}
206+
/// Is \p FCD an instance of \c MatchResultDiag?
207+
static bool classof(const FileCheckDiag *FCD) {
208+
return FCD->getKind() == FCDK_MatchResultDiag;
209+
}
210+
/// Get itself.
211+
const MatchResultDiag &getMatchResultDiag() const override { return *this; }
212+
/// What is the type of pattern for this match result?
213+
Check::FileCheckType getCheckTy() const { return CheckTy; }
214+
/// Where is the pattern for this match result?
215+
SMLoc getCheckLoc() const { return CheckLoc; }
216+
};
217+
218+
/// Class for recording a FileCheck diagnostic that provides an additional note
219+
/// (possibly an additional error) about the most recent \c MatchResultDiag.
220+
class MatchNoteDiag : public FileCheckDiag {
221+
private:
222+
MatchResultDiag *MRD;
223+
std::optional<std::string> CustomNote;
224+
225+
public:
226+
MatchNoteDiag(MatchType MatchTy, SMRange InputRange,
227+
std::optional<StringRef> CustomNote = std::nullopt)
228+
: FileCheckDiag(FCDK_MatchNoteDiag, MatchTy, InputRange), MRD(nullptr),
229+
CustomNote(CustomNote) {}
230+
/// Is \p FCD an instance of \c MatchNoteDiag?
231+
static bool classof(const FileCheckDiag *FCD) {
232+
return FCD->getKind() == FCDK_MatchNoteDiag;
233+
}
234+
/// Get the note's associated \c MatchResultDiag.
235+
const MatchResultDiag &getMatchResultDiag() const override { return *MRD; }
236+
/// Set the note's associated \c MatchResultDiag.
237+
void setMatchResultDiag(MatchResultDiag *MRDNew) {
238+
assert(!MRD && "expected setMatchResultDiag to be called only once");
239+
MRD = MRDNew;
240+
}
241+
/// A note to replace the one normally indicated by the match type, or the
242+
/// empty string if none.
243+
const std::optional<std::string> &getCustomNote() const { return CustomNote; }
176244
};
177245

178246
/// A \c FileCheckDiag series emitted by the FileCheck library.
179247
class FileCheckDiagList {
180248
private:
249+
MatchResultDiag *CurMatchResultDiag = nullptr;
181250
using vector_type = std::vector<std::unique_ptr<FileCheckDiag>>;
182251
vector_type DiagList;
183252

184253
public:
185-
/// Emplace a new \c FileCheckDiag.
186-
template <typename... ArgTys> void emplace_back(ArgTys &&...Args) {
254+
/// Emplace a new \c FileCheckDiag of type \c DiagTy. If it's a
255+
/// \c MatchNoteDiag, associate it with its \c MatchResultDiag.
256+
///
257+
/// \c FileCheckTest.cpp calls \c Pattern::printVariableDefs directly, so it
258+
/// can add a \c MatchNoteDiag without a previous \c MatchResultDiag.
259+
/// Otherwise, there should always be a previous \c MatchResultDiag.
260+
template <typename DiagTy, typename... ArgTys>
261+
void emplace(ArgTys &&...Args) {
187262
DiagList.emplace_back(
188-
std::make_unique<FileCheckDiag>(std::forward<ArgTys>(Args)...));
263+
std::make_unique<DiagTy>(std::forward<ArgTys>(Args)...));
264+
FileCheckDiag *Diag = DiagList.back().get();
265+
if (MatchResultDiag *MRD = dyn_cast<MatchResultDiag>(Diag)) {
266+
CurMatchResultDiag = MRD;
267+
return;
268+
}
269+
MatchNoteDiag *Note = cast<MatchNoteDiag>(Diag);
270+
if (!CurMatchResultDiag)
271+
return;
272+
Note->setMatchResultDiag(CurMatchResultDiag);
189273
}
190-
/// Adjust recent consecutive diagnostics of the same \c CheckLoc to have
191-
/// \c MatchTy.
274+
/// Adjust the most recent \c MatchResultDiag, which must exist, and every
275+
/// \c MatchResultNote after it to have the match type \c MatchTy.
192276
void adjustPrevDiags(FileCheckDiag::MatchType MatchTy) {
193-
SMLoc CheckLoc = (*DiagList.rbegin())->CheckLoc;
277+
assert(CurMatchResultDiag && "expected previous MatchResultDiag");
194278
for (auto I = DiagList.rbegin(), E = DiagList.rend();
195-
I != E && (*I)->CheckLoc == CheckLoc; ++I)
196-
(*I)->MatchTy = MatchTy;
279+
I != E && &**I != CurMatchResultDiag; ++I)
280+
(*I)->adjustMatchType(MatchTy);
281+
CurMatchResultDiag->adjustMatchType(MatchTy);
197282
}
198283
class const_iterator {
199284
friend FileCheckDiagList;

llvm/lib/FileCheck/FileCheck.cpp

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,8 +1285,8 @@ void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
12851285
// Indicating a non-zero-length range might instead seem to imply that the
12861286
// substitution matches or was captured from exactly that range.
12871287
if (Diags)
1288-
Diags->emplace_back(SM, CheckTy, getLoc(), MatchTy,
1289-
SMRange(Range.Start, Range.Start), OS.str());
1288+
Diags->emplace<MatchNoteDiag>(
1289+
MatchTy, SMRange(Range.Start, Range.Start), OS.str());
12901290
else
12911291
SM.PrintMessage(Range.Start, SourceMgr::DK_Note, OS.str());
12921292
}
@@ -1340,7 +1340,7 @@ void Pattern::printVariableDefs(const SourceMgr &SM,
13401340
raw_svector_ostream OS(Msg);
13411341
OS << "captured var \"" << VC.Name << "\"";
13421342
if (Diags)
1343-
Diags->emplace_back(SM, CheckTy, getLoc(), MatchTy, VC.Range, OS.str());
1343+
Diags->emplace<MatchNoteDiag>(MatchTy, VC.Range, OS.str());
13441344
else
13451345
SM.PrintMessage(VC.Range.Start, SourceMgr::DK_Note, OS.str(), VC.Range);
13461346
}
@@ -1399,8 +1399,7 @@ void Pattern::printFuzzyMatch(const SourceMgr &SM, StringRef Buffer,
13991399
if (Best && Best != StringRef::npos && BestQuality < 50) {
14001400
SMRange MatchRange = buildMatchRange(Buffer, Best, 0);
14011401
if (Diags)
1402-
Diags->emplace_back(SM, getCheckTy(), getLoc(), FileCheckDiag::MatchFuzzy,
1403-
MatchRange);
1402+
Diags->emplace<MatchNoteDiag>(FileCheckDiag::MatchFuzzy, MatchRange);
14041403
SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note,
14051404
"possible intended match here");
14061405

@@ -1507,18 +1506,7 @@ StringRef FileCheck::CanonicalizeFile(MemoryBuffer &MB,
15071506
return StringRef(OutputBuffer.data(), OutputBuffer.size() - 1);
15081507
}
15091508

1510-
FileCheckDiag::FileCheckDiag(const SourceMgr &SM,
1511-
const Check::FileCheckType &CheckTy,
1512-
SMLoc CheckLoc, MatchType MatchTy,
1513-
SMRange InputRange, StringRef Note)
1514-
: CheckTy(CheckTy), CheckLoc(CheckLoc), MatchTy(MatchTy), Note(Note) {
1515-
auto Start = SM.getLineAndColumn(InputRange.Start);
1516-
auto End = SM.getLineAndColumn(InputRange.End);
1517-
InputStartLine = Start.first;
1518-
InputStartCol = Start.second;
1519-
InputEndLine = End.first;
1520-
InputEndCol = End.second;
1521-
}
1509+
FileCheckDiag::~FileCheckDiag() {}
15221510

15231511
static bool IsPartOfWord(char c) {
15241512
return (isAlnum(c) || c == '-' || c == '_');
@@ -2045,7 +2033,7 @@ static Error printMatch(bool ExpectedMatch, const SourceMgr &SM,
20452033
SMRange MatchRange = buildMatchRange(Buffer, MatchResult.TheMatch->Pos,
20462034
MatchResult.TheMatch->Len);
20472035
if (Diags) {
2048-
Diags->emplace_back(SM, Pat.getCheckTy(), Loc, MatchTy, MatchRange);
2036+
Diags->emplace<MatchResultDiag>(Pat.getCheckTy(), Loc, MatchTy, MatchRange);
20492037
Pat.printSubstitutions(SM, Buffer, MatchRange, MatchTy, Diags);
20502038
Pat.printVariableDefs(SM, MatchTy, Diags);
20512039
}
@@ -2077,9 +2065,9 @@ static Error printMatch(bool ExpectedMatch, const SourceMgr &SM,
20772065
[&](const ErrorDiagnostic &E) {
20782066
E.log(errs());
20792067
if (Diags) {
2080-
Diags->emplace_back(SM, Pat.getCheckTy(), Loc,
2081-
FileCheckDiag::MatchFoundErrorNote,
2082-
E.getRange(), E.getMessage().str());
2068+
Diags->emplace<MatchNoteDiag>(
2069+
FileCheckDiag::MatchFoundErrorNote, E.getRange(),
2070+
E.getMessage().str());
20832071
}
20842072
});
20852073
return ErrorReported::reportedOrSuccess(HasError);
@@ -2131,11 +2119,11 @@ static Error printNoMatch(bool ExpectedMatch, const SourceMgr &SM,
21312119
// diagnostic is all we have to anchor them.
21322120
SMRange SearchRange = buildSearchRange(Buffer);
21332121
if (Diags) {
2134-
Diags->emplace_back(SM, Pat.getCheckTy(), Loc, MatchTy, SearchRange);
2122+
Diags->emplace<MatchResultDiag>(Pat.getCheckTy(), Loc, MatchTy,
2123+
SearchRange);
21352124
SMRange NoteRange = SMRange(SearchRange.Start, SearchRange.Start);
21362125
for (StringRef ErrorMsg : ErrorMsgs)
2137-
Diags->emplace_back(SM, Pat.getCheckTy(), Loc, MatchTy, NoteRange,
2138-
ErrorMsg);
2126+
Diags->emplace<MatchNoteDiag>(MatchTy, NoteRange, ErrorMsg);
21392127
Pat.printSubstitutions(SM, Buffer, SearchRange, MatchTy, Diags);
21402128
}
21412129
if (!PrintDiag) {
@@ -2268,9 +2256,9 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
22682256
if (Req.Verbose) {
22692257
Diags->adjustPrevDiags(FileCheckDiag::MatchFoundButWrongLine);
22702258
} else {
2271-
Diags->emplace_back(SM, Pat.getCheckTy(), Loc,
2272-
FileCheckDiag::MatchFoundButWrongLine,
2273-
buildMatchRange(MatchBuffer, MatchPos, MatchLen));
2259+
Diags->emplace<MatchResultDiag>(
2260+
Pat.getCheckTy(), Loc, FileCheckDiag::MatchFoundButWrongLine,
2261+
buildMatchRange(MatchBuffer, MatchPos, MatchLen));
22742262
}
22752263
}
22762264
return StringRef::npos;
@@ -2283,9 +2271,9 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
22832271
if (Req.Verbose) {
22842272
Diags->adjustPrevDiags(FileCheckDiag::MatchFoundButWrongLine);
22852273
} else {
2286-
Diags->emplace_back(SM, Pat.getCheckTy(), Loc,
2287-
FileCheckDiag::MatchFoundButWrongLine,
2288-
buildMatchRange(MatchBuffer, MatchPos, MatchLen));
2274+
Diags->emplace<MatchResultDiag>(
2275+
Pat.getCheckTy(), Loc, FileCheckDiag::MatchFoundButWrongLine,
2276+
buildMatchRange(MatchBuffer, MatchPos, MatchLen));
22892277
}
22902278
}
22912279
return StringRef::npos;

llvm/unittests/FileCheck/FileCheckTest.cpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -918,8 +918,21 @@ class PatternTester {
918918
FileCheckDiagList &Diags) {
919919
P.printVariableDefs(SM, MatchTy, &Diags);
920920
}
921+
922+
SourceMgr &getSourceMgr() { return SM; }
921923
};
922924

925+
#define EXPECT_SM_RANGE(SM, Range, StartLineExpected, StartColExpected, \
926+
EndLineExpected, EndColExpected) \
927+
do { \
928+
auto StartActual = SM.getLineAndColumn(Range.Start); \
929+
auto EndActual = SM.getLineAndColumn(Range.End); \
930+
EXPECT_EQ(StartActual.first, StartLineExpected); \
931+
EXPECT_EQ(StartActual.second, StartColExpected); \
932+
EXPECT_EQ(EndActual.first, EndLineExpected); \
933+
EXPECT_EQ(EndActual.second, EndColExpected); \
934+
} while (0)
935+
923936
TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
924937
PatternTester Tester;
925938

@@ -1639,22 +1652,21 @@ TEST_F(FileCheckTest, FileCheckContext) {
16391652

16401653
TEST_F(FileCheckTest, CapturedVarDiags) {
16411654
PatternTester Tester;
1655+
SourceMgr &SM = Tester.getSourceMgr();
16421656
ASSERT_FALSE(Tester.parsePattern("[[STRVAR:[a-z]+]] [[#NUMVAR:@LINE]]"));
16431657
EXPECT_THAT_EXPECTED(Tester.match("foobar 2"), Succeeded());
16441658
FileCheckDiagList Diags;
16451659
Tester.printVariableDefs(FileCheckDiag::MatchFoundAndExpected, Diags);
16461660
EXPECT_EQ(Diags.size(), 2ul);
1661+
SmallVector<MatchNoteDiag, 2> Notes;
16471662
for (const FileCheckDiag &Diag : Diags) {
1648-
EXPECT_EQ(Diag.CheckTy, Check::CheckPlain);
1649-
EXPECT_EQ(Diag.MatchTy, FileCheckDiag::MatchFoundAndExpected);
1650-
EXPECT_EQ(Diag.InputStartLine, 1u);
1651-
EXPECT_EQ(Diag.InputEndLine, 1u);
1663+
EXPECT_EQ(Diag.getKind(), FileCheckDiag::FCDK_MatchNoteDiag);
1664+
EXPECT_EQ(Diag.getMatchType(), FileCheckDiag::MatchFoundAndExpected);
1665+
Notes.push_back(cast<MatchNoteDiag>(Diag));
16521666
}
1653-
EXPECT_EQ(Diags[0].InputStartCol, 1u);
1654-
EXPECT_EQ(Diags[0].InputEndCol, 7u);
1655-
EXPECT_EQ(Diags[1].InputStartCol, 8u);
1656-
EXPECT_EQ(Diags[1].InputEndCol, 9u);
1657-
EXPECT_EQ(Diags[0].Note, "captured var \"STRVAR\"");
1658-
EXPECT_EQ(Diags[1].Note, "captured var \"NUMVAR\"");
1667+
EXPECT_SM_RANGE(SM, Notes[0].getInputRange(), 1u, 1u, 1u, 7u);
1668+
EXPECT_SM_RANGE(SM, Notes[1].getInputRange(), 1u, 8u, 1u, 9u);
1669+
EXPECT_EQ(Notes[0].getCustomNote(), "captured var \"STRVAR\"");
1670+
EXPECT_EQ(Notes[1].getCustomNote(), "captured var \"NUMVAR\"");
16591671
}
16601672
} // namespace

0 commit comments

Comments
 (0)