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
10#include "llvm/ADT/STLExtras.h"
13
14using namespace llvm;
15
17 const SourceMgr &mgr, SMLoc loc,
18 const Twine &msg,
19 ArrayRef<SMRange> ranges) {
21 mgr.GetMessage(loc, SourceMgr::DK_Error, msg, ranges).print(nullptr, os);
22}
23
26 switch (kind) {
28 return "note";
30 return "warning";
32 return "error";
34 return "remark";
35 }
36 llvm_unreachable("Unknown SourceMgr::DiagKind");
37}
38
40 raw_ostream &os, SourceMgr &mgr, const Twine &msg) const {
41 if (fileLoc.isValid()) {
42 SMRange range(fileLoc, SMLoc::getFromPointer(fileLoc.getPointer() +
43 substring.size()));
44 printMessage(os, mgr, fileLoc, msg, range);
45 } else {
46 printMessage(os, mgr, fileLoc, msg);
47 }
48 return false;
49}
50
52 // If this isn't a regex diagnostic, we simply check if the string was
53 // contained.
55 return substringRegex->match(str);
56 return str.contains(substring);
57}
58
60 SourceMgr &mgr) {
61 std::string regexStr;
62 raw_string_ostream regexOS(regexStr);
63 StringRef strToProcess = substring;
64 while (!strToProcess.empty()) {
65 // Find the next regex block.
66 size_t regexIt = strToProcess.find("{{");
67 if (regexIt == StringRef::npos) {
68 regexOS << Regex::escape(strToProcess);
69 break;
70 }
71 regexOS << Regex::escape(strToProcess.take_front(regexIt));
72 strToProcess = strToProcess.drop_front(regexIt + 2);
73
74 // Find the end of the regex block.
75 size_t regexEndIt = strToProcess.find("}}");
76 if (regexEndIt == StringRef::npos)
77 return emitError(os, mgr, "found start of regex with no end '}}'");
78 StringRef regexBlock = strToProcess.take_front(regexEndIt);
79
80 // Validate that the regex is actually valid.
81 std::string regexError;
82 if (!Regex(regexBlock).isValid(regexError))
83 return emitError(os, mgr, "invalid regex: " + regexError);
84
85 regexOS << '(' << regexBlock << ')';
86 strToProcess = strToProcess.drop_front(regexEndIt + 2);
87 }
88 substringRegex = Regex(regexStr);
89 return true;
90}
91
93 assert(!prefixes.empty() && "Diagnostic verifier needs at least one prefix");
94 std::string pattern = "(^|[^[:alnum:]_-])(";
96 prefixes,
97 [&](StringRef prefix) {
98 assert(!prefix.empty() && "Diagnostic verifier prefix cannot be empty");
99 pattern += Regex::escape(prefix);
100 },
101 [&] { pattern += '|'; });
102 pattern += ")-(error|note|remark|warning)(-re)?"
103 "( *@([+-][0-9]+|above|below|unknown))?(:([0-9]+))?"
104 " *{{(.*)}}$";
105 return Regex(pattern);
106}
107
111
114 SourceMgr &mgr,
115 const MemoryBuffer *buf) {
116 // If the buffer is invalid, return an empty list.
117 if (!buf)
118 return {};
119 auto &expectedDiags = expectedDiagsPerFile[buf->getBufferIdentifier()];
120
121 // The number of the last line that did not correlate to a designator.
122 unsigned lastNonDesignatorLine = 0;
123
124 // The indices of designators that apply to the next non designator line.
125 SmallVector<unsigned, 1> designatorsForNextLine;
126
127 // Scan the file for expected-* designators.
129 buf->getBuffer().split(lines, '\n');
130 for (unsigned lineNo = 0, e = lines.size(); lineNo < e; ++lineNo) {
132 if (!expected.match(lines[lineNo].rtrim(), &matches)) {
133 // Check for designators that apply to this line.
134 if (!designatorsForNextLine.empty()) {
135 for (unsigned diagIndex : designatorsForNextLine)
136 expectedDiags[diagIndex].lineNo = lineNo + 1;
137 designatorsForNextLine.clear();
138 }
139 lastNonDesignatorLine = lineNo;
140 continue;
141 }
142
143 // Point to the start of <prefix>-<kind>.
144 SMLoc expectedStart = SMLoc::getFromPointer(matches[2].data());
145
147 if (matches[3] == "error")
148 kind = SourceMgr::DK_Error;
149 else if (matches[3] == "warning")
151 else if (matches[3] == "remark")
153 else {
154 assert(matches[3] == "note");
155 kind = SourceMgr::DK_Note;
156 }
157 ExpectedDiag record(kind, lineNo + 1, expectedStart, matches[9]);
158
159 // Check to see if this is a regex match, i.e. it includes the `-re`.
160 if (!matches[4].empty() && !record.computeRegex(os, mgr)) {
161 ok = false;
162 continue;
163 }
164
165 if (!matches[8].empty() &&
166 (matches[8].getAsInteger(10, record.colNo) || record.colNo == 0)) {
167 record.emitError(os, mgr, "invalid column '" + matches[7] + "'");
168 ok = false;
169 continue;
170 }
171
172 StringRef offsetMatch = matches[6];
173 if (!offsetMatch.empty()) {
174 // Get the integer value without the @ and +/- prefix.
175 if (offsetMatch[0] == '+' || offsetMatch[0] == '-') {
176 int offset;
177 offsetMatch.drop_front().getAsInteger(0, offset);
178
179 if (offsetMatch.front() == '+')
180 record.lineNo += offset;
181 else
182 record.lineNo -= offset;
183 } else if (offsetMatch.consume_front("unknown")) {
184 if (record.colNo != 0) {
185 record.emitError(os, mgr, "cannot specify a column with '@unknown'");
186 ok = false;
187 continue;
188 }
189 // This is matching unknown locations.
190 record.fileLoc = SMLoc();
191 expectedUnknownLocDiags.emplace_back(std::move(record));
192 continue;
193 } else if (offsetMatch.consume_front("above")) {
194 // If the designator applies 'above' we add it to the last non
195 // designator line.
196 record.lineNo = lastNonDesignatorLine + 1;
197 } else {
198 // Otherwise, this is a 'below' designator and applies to the next
199 // non-designator line.
200 assert(offsetMatch.consume_front("below"));
201 designatorsForNextLine.push_back(expectedDiags.size());
202
203 // Set the line number to the last in the case that this designator
204 // ends up dangling.
205 record.lineNo = e;
206 }
207 }
208 expectedDiags.emplace_back(std::move(record));
209 }
210 return expectedDiags;
211}
212
213std::optional<MutableArrayRef<SourceMgrDiagnosticVerifier::ExpectedDiag>>
215 auto expectedDiags = expectedDiagsPerFile.find(bufName);
216 if (expectedDiags != expectedDiagsPerFile.end())
217 return MutableArrayRef<ExpectedDiag>(expectedDiags->second);
218 return std::nullopt;
219}
220
222 raw_ostream &os, SourceMgr &mgr, SourceMgr::DiagKind kind, bool hasLoc,
223 const MemoryBuffer *buf, unsigned lineNo, unsigned colNo, StringRef message,
224 bool reportUnexpected) {
226 if (hasLoc) {
227 // If the buffer couldn't be resolved, `diags` stays empty: a diagnostic
228 // with a location in an unknown file can never match anything.
229 if (buf) {
230 if (auto maybeDiags = getExpectedDiags(buf->getBufferIdentifier()))
231 diags = *maybeDiags;
232 else
233 diags = computeExpectedDiags(os, mgr, buf);
234 }
235 } else {
236 diags = expectedUnknownLocDiags;
237 }
238
239 // Search for a matching expected diagnostic.
240 // If we find something that is close then emit a more specific error.
241 ExpectedDiag *nearMiss = nullptr;
242
243 // If this was an expected error, remember that we saw it and return.
244 for (auto &e : diags) {
245 // File line must match (unless it's an unknown location).
246 if (hasLoc && e.lineNo != lineNo)
247 continue;
248 if (hasLoc && e.colNo != 0 && e.colNo != colNo)
249 continue;
250 if (e.match(message)) {
251 if (e.kind == kind) {
252 e.matched = true;
254 }
255
256 // If this only differs based on the diagnostic kind, then consider it
257 // to be a near miss.
258 nearMiss = &e;
259 }
260 }
261
262 if (!reportUnexpected)
264
265 ok = false;
266
267 // Otherwise, emit an error for the near miss.
268 if (nearMiss) {
269 printMessage(os, mgr, nearMiss->fileLoc,
270 "'" + getDiagKindStr(kind) +
271 "' diagnostic emitted when expecting a '" +
272 getDiagKindStr(nearMiss->kind) + "'");
274 }
276}
277
279 // Verify that all expected errors were seen.
280 auto checkExpectedDiags = [&](ExpectedDiag &diag) {
281 if (!diag.matched) {
282 diag.emitError(os, mgr,
283 "expected " + getDiagKindStr(diag.kind) + " \"" +
284 diag.substring + "\" was not produced");
285 ok = false;
286 }
287 };
288 for (auto &expectedDiagsPair : expectedDiagsPerFile)
289 for (auto &diag : expectedDiagsPair.second)
290 checkExpectedDiags(diag);
291 for (auto &diag : expectedUnknownLocDiags)
292 checkExpectedDiags(diag);
293 expectedDiagsPerFile.clear();
294 expectedUnknownLocDiags.clear();
295 return ok;
296}
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_>.
This file contains some templates that are useful if you are working with the STL at all.
static Regex buildExpectedRegex(ArrayRef< StringRef > prefixes)
static Split data
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
bool empty() const
Check if the array is empty.
Definition ArrayRef.h:136
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
LLVM_ABI void print(const char *ProgName, raw_ostream &S, bool ShowColors=true, bool ShowKindLabel=true, bool ShowLocation=true) const
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.
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...
static void printMessage(raw_ostream &os, const SourceMgr &mgr, SMLoc loc, const Twine &msg, ArrayRef< SMRange > ranges={})
Prints an error to os without going through mgr's diagnostic handler, which may be the one feeding th...
MatchResult process(raw_ostream &os, SourceMgr &mgr, SourceMgr::DiagKind kind, bool hasLoc, const MemoryBuffer *buf, unsigned lineNo, unsigned colNo, StringRef message, bool reportUnexpected=true)
Matches a single actual diagnostic against the expected diagnostics recorded for buf / lineNo / colNo...
static StringRef getDiagKindStr(SourceMgr::DiagKind kind)
Given a diagnostic kind, return a human readable string for it.
SourceMgrDiagnosticVerifier(ArrayRef< StringRef > prefixes={ "expected"})
Recognizes directives starting with any of prefixes (e.g.
This owns the files read by a parser, handles include stacks, and handles diagnostic wrangling.
Definition SourceMgr.h:34
LLVM_ABI void printIncludeStackForDiagnostic(SMLoc Loc, raw_ostream &OS) const
Prints the include stack of a buffer unless it is a macro instantiation buffer.
LLVM_ABI SMDiagnostic GetMessage(SMLoc Loc, DiagKind Kind, const Twine &Msg, ArrayRef< SMRange > Ranges={}, ArrayRef< SMFixIt > FixIts={}) const
Return an SMDiagnostic at 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.
void interleave(ForwardIterator begin, ForwardIterator end, UnaryFunctor each_fn, NullaryFunctor between_fn)
An STL-style algorithm similar to std::for_each that applies a second functor between every pair of e...
Definition STLExtras.h:2291
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(....
unsigned colNo
The column the expected diagnostic should be at, or 0 if any column matches.