LLVM 24.0.0git
SourceMgrDiagnosticVerifier.cpp
Go to the documentation of this file.
1//===- SourceMgrDiagnosticVerifier.cpp -----------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
12
13using namespace llvm;
14
15/// Given a diagnostic kind, return a human readable string for it.
17 switch (kind) {
19 return "note";
21 return "warning";
23 return "error";
25 return "remark";
26 }
27 llvm_unreachable("Unknown SourceMgr::DiagKind");
28}
29
31 raw_ostream &os, SourceMgr &mgr, const Twine &msg) const {
32 if (fileLoc.isValid()) {
33 SMRange range(fileLoc, SMLoc::getFromPointer(fileLoc.getPointer() +
34 substring.size()));
35 mgr.PrintMessage(os, fileLoc, SourceMgr::DK_Error, msg, range);
36 } else {
38 }
39 return false;
40}
41
43 // If this isn't a regex diagnostic, we simply check if the string was
44 // contained.
46 return substringRegex->match(str);
47 return str.contains(substring);
48}
49
51 SourceMgr &mgr) {
52 std::string regexStr;
53 raw_string_ostream regexOS(regexStr);
54 StringRef strToProcess = substring;
55 while (!strToProcess.empty()) {
56 // Find the next regex block.
57 size_t regexIt = strToProcess.find("{{");
58 if (regexIt == StringRef::npos) {
59 regexOS << Regex::escape(strToProcess);
60 break;
61 }
62 regexOS << Regex::escape(strToProcess.take_front(regexIt));
63 strToProcess = strToProcess.drop_front(regexIt + 2);
64
65 // Find the end of the regex block.
66 size_t regexEndIt = strToProcess.find("}}");
67 if (regexEndIt == StringRef::npos)
68 return emitError(os, mgr, "found start of regex with no end '}}'");
69 StringRef regexBlock = strToProcess.take_front(regexEndIt);
70
71 // Validate that the regex is actually valid.
72 std::string regexError;
73 if (!Regex(regexBlock).isValid(regexError))
74 return emitError(os, mgr, "invalid regex: " + regexError);
75
76 regexOS << '(' << regexBlock << ')';
77 strToProcess = strToProcess.drop_front(regexEndIt + 2);
78 }
79 substringRegex = Regex(regexStr);
80 return true;
81}
82
85 SourceMgr &mgr,
86 const MemoryBuffer *buf) {
87 // If the buffer is invalid, return an empty list.
88 if (!buf)
89 return {};
90 auto &expectedDiags = expectedDiagsPerFile[buf->getBufferIdentifier()];
91
92 // The number of the last line that did not correlate to a designator.
93 unsigned lastNonDesignatorLine = 0;
94
95 // The indices of designators that apply to the next non designator line.
96 SmallVector<unsigned, 1> designatorsForNextLine;
97
98 // Scan the file for expected-* designators.
100 buf->getBuffer().split(lines, '\n');
101 for (unsigned lineNo = 0, e = lines.size(); lineNo < e; ++lineNo) {
103 if (!expected.match(lines[lineNo].rtrim(), &matches)) {
104 // Check for designators that apply to this line.
105 if (!designatorsForNextLine.empty()) {
106 for (unsigned diagIndex : designatorsForNextLine)
107 expectedDiags[diagIndex].lineNo = lineNo + 1;
108 designatorsForNextLine.clear();
109 }
110 lastNonDesignatorLine = lineNo;
111 continue;
112 }
113
114 // Point to the start of expected-*.
115 SMLoc expectedStart = SMLoc::getFromPointer(matches[0].data());
116
118 if (matches[1] == "error")
119 kind = SourceMgr::DK_Error;
120 else if (matches[1] == "warning")
122 else if (matches[1] == "remark")
124 else {
125 assert(matches[1] == "note");
126 kind = SourceMgr::DK_Note;
127 }
128 ExpectedDiag record(kind, lineNo + 1, expectedStart, matches[5]);
129
130 // Check to see if this is a regex match, i.e. it includes the `-re`.
131 if (!matches[2].empty() && !record.computeRegex(os, mgr)) {
132 ok = false;
133 continue;
134 }
135
136 StringRef offsetMatch = matches[3];
137 if (!offsetMatch.empty()) {
138 offsetMatch = offsetMatch.drop_front(1);
139
140 // Get the integer value without the @ and +/- prefix.
141 if (offsetMatch[0] == '+' || offsetMatch[0] == '-') {
142 int offset;
143 offsetMatch.drop_front().getAsInteger(0, offset);
144
145 if (offsetMatch.front() == '+')
146 record.lineNo += offset;
147 else
148 record.lineNo -= offset;
149 } else if (offsetMatch.consume_front("unknown")) {
150 // This is matching unknown locations.
151 record.fileLoc = SMLoc();
152 expectedUnknownLocDiags.emplace_back(std::move(record));
153 continue;
154 } else if (offsetMatch.consume_front("above")) {
155 // If the designator applies 'above' we add it to the last non
156 // designator line.
157 record.lineNo = lastNonDesignatorLine + 1;
158 } else {
159 // Otherwise, this is a 'below' designator and applies to the next
160 // non-designator line.
161 assert(offsetMatch.consume_front("below"));
162 designatorsForNextLine.push_back(expectedDiags.size());
163
164 // Set the line number to the last in the case that this designator
165 // ends up dangling.
166 record.lineNo = e;
167 }
168 }
169 expectedDiags.emplace_back(std::move(record));
170 }
171 return expectedDiags;
172}
173
174std::optional<MutableArrayRef<SourceMgrDiagnosticVerifier::ExpectedDiag>>
176 auto expectedDiags = expectedDiagsPerFile.find(bufName);
177 if (expectedDiags != expectedDiagsPerFile.end())
178 return MutableArrayRef<ExpectedDiag>(expectedDiags->second);
179 return std::nullopt;
180}
181
184 SourceMgr::DiagKind kind, bool hasLoc,
185 const MemoryBuffer *buf, unsigned lineNo,
186 StringRef message, bool reportUnexpected) {
188 if (hasLoc) {
189 // If the buffer couldn't be resolved, `diags` stays empty: a diagnostic
190 // with a location in an unknown file can never match anything.
191 if (buf) {
192 if (auto maybeDiags = getExpectedDiags(buf->getBufferIdentifier()))
193 diags = *maybeDiags;
194 else
195 diags = computeExpectedDiags(os, mgr, buf);
196 }
197 } else {
198 diags = expectedUnknownLocDiags;
199 }
200
201 // Search for a matching expected diagnostic.
202 // If we find something that is close then emit a more specific error.
203 ExpectedDiag *nearMiss = nullptr;
204
205 // If this was an expected error, remember that we saw it and return.
206 for (auto &e : diags) {
207 // File line must match (unless it's an unknown location).
208 if (hasLoc && e.lineNo != lineNo)
209 continue;
210 if (e.match(message)) {
211 if (e.kind == kind) {
212 e.matched = true;
214 }
215
216 // If this only differs based on the diagnostic kind, then consider it
217 // to be a near miss.
218 nearMiss = &e;
219 }
220 }
221
222 if (!reportUnexpected)
224
225 ok = false;
226
227 // Otherwise, emit an error for the near miss.
228 if (nearMiss) {
229 mgr.PrintMessage(os, nearMiss->fileLoc, SourceMgr::DK_Error,
230 "'" + getDiagKindStr(kind) +
231 "' diagnostic emitted when expecting a '" +
232 getDiagKindStr(nearMiss->kind) + "'");
234 }
236}
237
239 // Verify that all expected errors were seen.
240 auto checkExpectedDiags = [&](ExpectedDiag &diag) {
241 if (!diag.matched) {
242 diag.emitError(os, mgr,
243 "expected " + getDiagKindStr(diag.kind) + " \"" +
244 diag.substring + "\" was not produced");
245 ok = false;
246 }
247 };
248 for (auto &expectedDiagsPair : expectedDiagsPerFile)
249 for (auto &diag : expectedDiagsPair.second)
250 checkExpectedDiags(diag);
251 for (auto &diag : expectedUnknownLocDiags)
252 checkExpectedDiags(diag);
253 expectedDiagsPerFile.clear();
254 return ok;
255}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
bool hasLoc(const Instruction &I)
Definition Debugify.cpp:403
static bool isValid(const char C)
Returns true if C is a valid mangled character: <0-9a-zA-Z_>.
static StringRef getDiagKindStr(SourceMgr::DiagKind kind)
Given a diagnostic kind, return a human readable string for it.
static Split data
This interface provides simple read-only access to a block of memory, and provides simple methods for...
virtual StringRef getBufferIdentifier() const
Return an identifier for this buffer, typically the filename it was read from.
StringRef getBuffer() const
Represent a mutable reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:294
static LLVM_ABI std::string escape(StringRef String)
Turn String into a regex by escaping its special characters.
Definition Regex.cpp:251
Represents a location in source code.
Definition SMLoc.h:22
static SMLoc getFromPointer(const char *Ptr)
Definition SMLoc.h:35
Represents a range in source code.
Definition SMLoc.h:47
reference emplace_back(ArgTypes &&... Args)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
MatchResult
The result of matching a single actual diagnostic against the expected diagnostics recorded for its l...
@ Matched
The diagnostic matched an expected diagnostic (kind and text).
@ Unexpected
The diagnostic did not match any expected diagnostic.
@ Ignored
The diagnostic did not match, but reportUnexpected was false, so nothing was printed and nothing need...
@ NearMiss
The diagnostic's text matched an expected diagnostic, but its kind didn't; a message about this has a...
bool verify(raw_ostream &os, SourceMgr &mgr)
Reports (through mgr) any expected diagnostic that was never matched by a call to process.
MatchResult process(raw_ostream &os, SourceMgr &mgr, SourceMgr::DiagKind kind, bool hasLoc, const MemoryBuffer *buf, unsigned lineNo, StringRef message, bool reportUnexpected=true)
Matches a single actual diagnostic against the expected diagnostics recorded for buf / lineNo,...
MutableArrayRef< ExpectedDiag > computeExpectedDiags(raw_ostream &os, SourceMgr &mgr, const MemoryBuffer *buf)
Computes and caches the list of expected diagnostics for buf, if not already cached.
std::optional< MutableArrayRef< ExpectedDiag > > getExpectedDiags(StringRef bufName)
Returns the cached expected diagnostics for the buffer named bufName, or std::nullopt if computeExpec...
This owns the files read by a parser, handles include stacks, and handles diagnostic wrangling.
Definition SourceMgr.h:34
LLVM_ABI void PrintMessage(raw_ostream &OS, SMLoc Loc, DiagKind Kind, const Twine &Msg, ArrayRef< SMRange > Ranges={}, ArrayRef< SMFixIt > FixIts={}, bool ShowColors=true) const
Emit a message about the specified location with the specified string.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
Definition StringRef.h:736
static constexpr size_t npos
Definition StringRef.h:58
bool getAsInteger(unsigned Radix, T &Result) const
Parse the current string as an integer of the specified radix.
Definition StringRef.h:490
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:141
StringRef drop_front(size_t N=1) const
Return a StringRef equal to 'this' but with the first N elements dropped.
Definition StringRef.h:635
char front() const
Get the first character in the string.
Definition StringRef.h:147
bool contains(StringRef Other) const
Return true if the given string is a substring of *this, and false otherwise.
Definition StringRef.h:446
StringRef take_front(size_t N=1) const
Return a StringRef equal to 'this' but with only the first N elements remaining.
Definition StringRef.h:606
size_t find(char C, size_t From=0) const
Search for the first character C in the string.
Definition StringRef.h:290
bool consume_front(char Prefix)
Returns true if this StringRef has the given prefix and removes that prefix.
Definition StringRef.h:661
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
A raw_ostream that writes to an std::string.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
This is an optimization pass for GlobalISel generic memory operations.
MutableArrayRef(T &OneElt) -> MutableArrayRef< T >
A single diagnostic expected via an 'expected-<kind>' comment.
std::optional< Regex > substringRegex
An optional regex matcher, if the expected diagnostic substring was a regex string.
StringRef substring
The substring that is expected to be within the diagnostic.
LLVM_ABI bool computeRegex(raw_ostream &os, SourceMgr &mgr)
Computes the regex matcher for a '-re' diagnostic's substring.
SourceMgr::DiagKind kind
The severity of the diagnostic expected.
SMLoc fileLoc
The location of the expected diagnostic within the input file.
LLVM_ABI bool match(StringRef str) const
Returns true if this diagnostic matches the given message.
unsigned lineNo
The line number the expected diagnostic should be on.
LLVM_ABI bool emitError(raw_ostream &os, SourceMgr &mgr, const Twine &msg) const
Prints msg at this diagnostic's location and returns false, for use as return emitError(....