1 //===- Diagnostics.cpp - MLIR Diagnostics ---------------------------------===//
2 //
3 // Part of the MLIR 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 
9 #include "mlir/IR/Diagnostics.h"
10 #include "mlir/IR/Attributes.h"
11 #include "mlir/IR/Identifier.h"
12 #include "mlir/IR/Location.h"
13 #include "mlir/IR/MLIRContext.h"
14 #include "mlir/IR/Operation.h"
15 #include "mlir/IR/Types.h"
16 #include "llvm/ADT/MapVector.h"
17 #include "llvm/ADT/SmallString.h"
18 #include "llvm/ADT/StringMap.h"
19 #include "llvm/Support/CommandLine.h"
20 #include "llvm/Support/Mutex.h"
21 #include "llvm/Support/PrettyStackTrace.h"
22 #include "llvm/Support/Regex.h"
23 #include "llvm/Support/Signals.h"
24 #include "llvm/Support/SourceMgr.h"
25 #include "llvm/Support/raw_ostream.h"
26 
27 using namespace mlir;
28 using namespace mlir::detail;
29 
30 static llvm::cl::opt<bool> printStackTraceOnDiagnostic(
31     "mlir-print-stacktrace-on-diagnostic",
32     llvm::cl::desc("When a diagnostic is emitted, also print the stack trace "
33                    "as an attached note"));
34 
35 //===----------------------------------------------------------------------===//
36 // DiagnosticArgument
37 //===----------------------------------------------------------------------===//
38 
39 // Construct from an Attribute.
40 DiagnosticArgument::DiagnosticArgument(Attribute attr)
41     : kind(DiagnosticArgumentKind::Attribute),
42       opaqueVal(reinterpret_cast<intptr_t>(attr.getAsOpaquePointer())) {}
43 
44 // Construct from a Type.
45 DiagnosticArgument::DiagnosticArgument(Type val)
46     : kind(DiagnosticArgumentKind::Type),
47       opaqueVal(reinterpret_cast<intptr_t>(val.getAsOpaquePointer())) {}
48 
49 /// Returns this argument as an Attribute.
50 Attribute DiagnosticArgument::getAsAttribute() const {
51   assert(getKind() == DiagnosticArgumentKind::Attribute);
52   return Attribute::getFromOpaquePointer(
53       reinterpret_cast<const void *>(opaqueVal));
54 }
55 
56 /// Returns this argument as a Type.
57 Type DiagnosticArgument::getAsType() const {
58   assert(getKind() == DiagnosticArgumentKind::Type);
59   return Type::getFromOpaquePointer(reinterpret_cast<const void *>(opaqueVal));
60 }
61 
62 /// Outputs this argument to a stream.
63 void DiagnosticArgument::print(raw_ostream &os) const {
64   switch (kind) {
65   case DiagnosticArgumentKind::Attribute:
66     os << getAsAttribute();
67     break;
68   case DiagnosticArgumentKind::Double:
69     os << getAsDouble();
70     break;
71   case DiagnosticArgumentKind::Integer:
72     os << getAsInteger();
73     break;
74   case DiagnosticArgumentKind::Operation:
75     getAsOperation().print(os, OpPrintingFlags().useLocalScope());
76     break;
77   case DiagnosticArgumentKind::String:
78     os << getAsString();
79     break;
80   case DiagnosticArgumentKind::Type:
81     os << '\'' << getAsType() << '\'';
82     break;
83   case DiagnosticArgumentKind::Unsigned:
84     os << getAsUnsigned();
85     break;
86   }
87 }
88 
89 //===----------------------------------------------------------------------===//
90 // Diagnostic
91 //===----------------------------------------------------------------------===//
92 
93 /// Convert a Twine to a StringRef. Memory used for generating the StringRef is
94 /// stored in 'strings'.
95 static StringRef twineToStrRef(const Twine &val,
96                                std::vector<std::unique_ptr<char[]>> &strings) {
97   // Allocate memory to hold this string.
98   SmallString<64> data;
99   auto strRef = val.toStringRef(data);
100   strings.push_back(std::unique_ptr<char[]>(new char[strRef.size()]));
101   memcpy(&strings.back()[0], strRef.data(), strRef.size());
102 
103   // Return a reference to the new string.
104   return StringRef(&strings.back()[0], strRef.size());
105 }
106 
107 /// Stream in a Twine argument.
108 Diagnostic &Diagnostic::operator<<(char val) { return *this << Twine(val); }
109 Diagnostic &Diagnostic::operator<<(const Twine &val) {
110   arguments.push_back(DiagnosticArgument(twineToStrRef(val, strings)));
111   return *this;
112 }
113 Diagnostic &Diagnostic::operator<<(Twine &&val) {
114   arguments.push_back(DiagnosticArgument(twineToStrRef(val, strings)));
115   return *this;
116 }
117 
118 /// Stream in an Identifier.
119 Diagnostic &Diagnostic::operator<<(Identifier val) {
120   // An identifier is stored in the context, so we don't need to worry about the
121   // lifetime of its data.
122   arguments.push_back(DiagnosticArgument(val.strref()));
123   return *this;
124 }
125 
126 /// Stream in an OperationName.
127 Diagnostic &Diagnostic::operator<<(OperationName val) {
128   // An OperationName is stored in the context, so we don't need to worry about
129   // the lifetime of its data.
130   arguments.push_back(DiagnosticArgument(val.getStringRef()));
131   return *this;
132 }
133 
134 /// Outputs this diagnostic to a stream.
135 void Diagnostic::print(raw_ostream &os) const {
136   for (auto &arg : getArguments())
137     arg.print(os);
138 }
139 
140 /// Convert the diagnostic to a string.
141 std::string Diagnostic::str() const {
142   std::string str;
143   llvm::raw_string_ostream os(str);
144   print(os);
145   return os.str();
146 }
147 
148 /// Attaches a note to this diagnostic. A new location may be optionally
149 /// provided, if not, then the location defaults to the one specified for this
150 /// diagnostic. Notes may not be attached to other notes.
151 Diagnostic &Diagnostic::attachNote(Optional<Location> noteLoc) {
152   // We don't allow attaching notes to notes.
153   assert(severity != DiagnosticSeverity::Note &&
154          "cannot attach a note to a note");
155 
156   // If a location wasn't provided then reuse our location.
157   if (!noteLoc)
158     noteLoc = loc;
159 
160   /// Append and return a new note.
161   notes.push_back(
162       std::make_unique<Diagnostic>(*noteLoc, DiagnosticSeverity::Note));
163   return *notes.back();
164 }
165 
166 /// Allow a diagnostic to be converted to 'failure'.
167 Diagnostic::operator LogicalResult() const { return failure(); }
168 
169 //===----------------------------------------------------------------------===//
170 // InFlightDiagnostic
171 //===----------------------------------------------------------------------===//
172 
173 /// Allow an inflight diagnostic to be converted to 'failure', otherwise
174 /// 'success' if this is an empty diagnostic.
175 InFlightDiagnostic::operator LogicalResult() const {
176   return failure(isActive());
177 }
178 
179 /// Reports the diagnostic to the engine.
180 void InFlightDiagnostic::report() {
181   // If this diagnostic is still inflight and it hasn't been abandoned, then
182   // report it.
183   if (isInFlight()) {
184     owner->emit(std::move(*impl));
185     owner = nullptr;
186   }
187   impl.reset();
188 }
189 
190 /// Abandons this diagnostic.
191 void InFlightDiagnostic::abandon() { owner = nullptr; }
192 
193 //===----------------------------------------------------------------------===//
194 // DiagnosticEngineImpl
195 //===----------------------------------------------------------------------===//
196 
197 namespace mlir {
198 namespace detail {
199 struct DiagnosticEngineImpl {
200   /// Emit a diagnostic using the registered issue handle if present, or with
201   /// the default behavior if not.
202   void emit(Diagnostic diag);
203 
204   /// A mutex to ensure that diagnostics emission is thread-safe.
205   llvm::sys::SmartMutex<true> mutex;
206 
207   /// These are the handlers used to report diagnostics.
208   llvm::SmallMapVector<DiagnosticEngine::HandlerID, DiagnosticEngine::HandlerTy,
209                        2>
210       handlers;
211 
212   /// This is a unique identifier counter for diagnostic handlers in the
213   /// context. This id starts at 1 to allow for 0 to be used as a sentinel.
214   DiagnosticEngine::HandlerID uniqueHandlerId = 1;
215 };
216 } // namespace detail
217 } // namespace mlir
218 
219 /// Emit a diagnostic using the registered issue handle if present, or with
220 /// the default behavior if not.
221 void DiagnosticEngineImpl::emit(Diagnostic diag) {
222   llvm::sys::SmartScopedLock<true> lock(mutex);
223 
224   // Try to process the given diagnostic on one of the registered handlers.
225   // Handlers are walked in reverse order, so that the most recent handler is
226   // processed first.
227   for (auto &handlerIt : llvm::reverse(handlers))
228     if (succeeded(handlerIt.second(diag)))
229       return;
230 
231   // Otherwise, if this is an error we emit it to stderr.
232   if (diag.getSeverity() != DiagnosticSeverity::Error)
233     return;
234 
235   auto &os = llvm::errs();
236   if (!diag.getLocation().isa<UnknownLoc>())
237     os << diag.getLocation() << ": ";
238   os << "error: ";
239 
240   // The default behavior for errors is to emit them to stderr.
241   os << diag << '\n';
242   os.flush();
243 }
244 
245 //===----------------------------------------------------------------------===//
246 // DiagnosticEngine
247 //===----------------------------------------------------------------------===//
248 
249 DiagnosticEngine::DiagnosticEngine() : impl(new DiagnosticEngineImpl()) {}
250 DiagnosticEngine::~DiagnosticEngine() {}
251 
252 /// Register a new handler for diagnostics to the engine. This function returns
253 /// a unique identifier for the registered handler, which can be used to
254 /// unregister this handler at a later time.
255 auto DiagnosticEngine::registerHandler(const HandlerTy &handler) -> HandlerID {
256   llvm::sys::SmartScopedLock<true> lock(impl->mutex);
257   auto uniqueID = impl->uniqueHandlerId++;
258   impl->handlers.insert({uniqueID, handler});
259   return uniqueID;
260 }
261 
262 /// Erase the registered diagnostic handler with the given identifier.
263 void DiagnosticEngine::eraseHandler(HandlerID handlerID) {
264   llvm::sys::SmartScopedLock<true> lock(impl->mutex);
265   impl->handlers.erase(handlerID);
266 }
267 
268 /// Emit a diagnostic using the registered issue handler if present, or with
269 /// the default behavior if not.
270 void DiagnosticEngine::emit(Diagnostic diag) {
271   assert(diag.getSeverity() != DiagnosticSeverity::Note &&
272          "notes should not be emitted directly");
273   impl->emit(std::move(diag));
274 }
275 
276 /// Helper function used to emit a diagnostic with an optionally empty twine
277 /// message. If the message is empty, then it is not inserted into the
278 /// diagnostic.
279 static InFlightDiagnostic
280 emitDiag(Location location, DiagnosticSeverity severity, const Twine &message) {
281   auto &diagEngine = location->getContext()->getDiagEngine();
282   auto diag = diagEngine.emit(location, severity);
283   if (!message.isTriviallyEmpty())
284     diag << message;
285 
286   // Add the stack trace as a note if necessary.
287   if (printStackTraceOnDiagnostic) {
288     std::string bt;
289     {
290       llvm::raw_string_ostream stream(bt);
291       llvm::sys::PrintStackTrace(stream);
292     }
293     if (!bt.empty())
294       diag.attachNote() << "diagnostic emitted with trace:\n" << bt;
295   }
296 
297   return diag;
298 }
299 
300 /// Emit an error message using this location.
301 InFlightDiagnostic mlir::emitError(Location loc) { return emitError(loc, {}); }
302 InFlightDiagnostic mlir::emitError(Location loc, const Twine &message) {
303   return emitDiag(loc, DiagnosticSeverity::Error, message);
304 }
305 
306 /// Emit a warning message using this location.
307 InFlightDiagnostic mlir::emitWarning(Location loc) {
308   return emitWarning(loc, {});
309 }
310 InFlightDiagnostic mlir::emitWarning(Location loc, const Twine &message) {
311   return emitDiag(loc, DiagnosticSeverity::Warning, message);
312 }
313 
314 /// Emit a remark message using this location.
315 InFlightDiagnostic mlir::emitRemark(Location loc) {
316   return emitRemark(loc, {});
317 }
318 InFlightDiagnostic mlir::emitRemark(Location loc, const Twine &message) {
319   return emitDiag(loc, DiagnosticSeverity::Remark, message);
320 }
321 
322 //===----------------------------------------------------------------------===//
323 // ScopedDiagnosticHandler
324 //===----------------------------------------------------------------------===//
325 
326 ScopedDiagnosticHandler::~ScopedDiagnosticHandler() {
327   if (handlerID)
328     ctx->getDiagEngine().eraseHandler(handlerID);
329 }
330 
331 //===----------------------------------------------------------------------===//
332 // SourceMgrDiagnosticHandler
333 //===----------------------------------------------------------------------===//
334 namespace mlir {
335 namespace detail {
336 struct SourceMgrDiagnosticHandlerImpl {
337   /// Get a memory buffer for the given file, or nullptr if one is not found.
338   const llvm::MemoryBuffer *getBufferForFile(llvm::SourceMgr &mgr,
339                                              StringRef filename) {
340     // Check for an existing mapping to the buffer id for this file.
341     auto bufferIt = filenameToBuf.find(filename);
342     if (bufferIt != filenameToBuf.end())
343       return bufferIt->second;
344 
345     // Look for a buffer in the manager that has this filename.
346     for (unsigned i = 1, e = mgr.getNumBuffers() + 1; i != e; ++i) {
347       auto *buf = mgr.getMemoryBuffer(i);
348       if (buf->getBufferIdentifier() == filename)
349         return filenameToBuf[filename] = buf;
350     }
351 
352     // Otherwise, try to load the source file.
353     const llvm::MemoryBuffer *newBuf = nullptr;
354     std::string ignored;
355     if (auto newBufID = mgr.AddIncludeFile(filename, llvm::SMLoc(), ignored))
356       newBuf = mgr.getMemoryBuffer(newBufID);
357     return filenameToBuf[filename] = newBuf;
358   }
359 
360   /// Mapping between file name and buffer pointer.
361   llvm::StringMap<const llvm::MemoryBuffer *> filenameToBuf;
362 };
363 } // end namespace detail
364 } // end namespace mlir
365 
366 /// Return a processable FileLineColLoc from the given location.
367 static Optional<FileLineColLoc> getFileLineColLoc(Location loc) {
368   switch (loc->getKind()) {
369   case StandardAttributes::NameLocation:
370     return getFileLineColLoc(loc.cast<NameLoc>().getChildLoc());
371   case StandardAttributes::FileLineColLocation:
372     return loc.cast<FileLineColLoc>();
373   case StandardAttributes::CallSiteLocation:
374     // Process the callee of a callsite location.
375     return getFileLineColLoc(loc.cast<CallSiteLoc>().getCallee());
376   default:
377     return llvm::None;
378   }
379 }
380 
381 /// Given a diagnostic kind, returns the LLVM DiagKind.
382 static llvm::SourceMgr::DiagKind getDiagKind(DiagnosticSeverity kind) {
383   switch (kind) {
384   case DiagnosticSeverity::Note:
385     return llvm::SourceMgr::DK_Note;
386   case DiagnosticSeverity::Warning:
387     return llvm::SourceMgr::DK_Warning;
388   case DiagnosticSeverity::Error:
389     return llvm::SourceMgr::DK_Error;
390   case DiagnosticSeverity::Remark:
391     return llvm::SourceMgr::DK_Remark;
392   }
393   llvm_unreachable("Unknown DiagnosticSeverity");
394 }
395 
396 SourceMgrDiagnosticHandler::SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr,
397                                                        MLIRContext *ctx,
398                                                        raw_ostream &os)
399     : ScopedDiagnosticHandler(ctx), mgr(mgr), os(os),
400       impl(new SourceMgrDiagnosticHandlerImpl()) {
401   setHandler([this](Diagnostic &diag) { emitDiagnostic(diag); });
402 }
403 
404 SourceMgrDiagnosticHandler::SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr,
405                                                        MLIRContext *ctx)
406     : SourceMgrDiagnosticHandler(mgr, ctx, llvm::errs()) {}
407 
408 SourceMgrDiagnosticHandler::~SourceMgrDiagnosticHandler() {}
409 
410 void SourceMgrDiagnosticHandler::emitDiagnostic(Location loc, Twine message,
411                                                 DiagnosticSeverity kind) {
412   // Extract a file location from this loc.
413   auto fileLoc = getFileLineColLoc(loc);
414 
415   // If one doesn't exist, then print the raw message without a source location.
416   if (!fileLoc) {
417     std::string str;
418     llvm::raw_string_ostream strOS(str);
419     if (!loc.isa<UnknownLoc>())
420       strOS << loc << ": ";
421     strOS << message;
422     return mgr.PrintMessage(os, llvm::SMLoc(), getDiagKind(kind), strOS.str());
423   }
424 
425   // Otherwise, try to convert the file location to an SMLoc.
426   auto smloc = convertLocToSMLoc(*fileLoc);
427   if (smloc.isValid())
428     return mgr.PrintMessage(os, smloc, getDiagKind(kind), message);
429 
430   // If the conversion was unsuccessful, create a diagnostic with the file
431   // information.
432   llvm::SMDiagnostic diag(fileLoc->getFilename(), getDiagKind(kind),
433                           message.str());
434   diag.print(nullptr, os);
435 }
436 
437 /// Emit the given diagnostic with the held source manager.
438 void SourceMgrDiagnosticHandler::emitDiagnostic(Diagnostic &diag) {
439   // Emit the diagnostic.
440   auto loc = diag.getLocation();
441   emitDiagnostic(loc, diag.str(), diag.getSeverity());
442 
443   // If the diagnostic location was a call site location, then print the call
444   // stack as well.
445   if (auto callLoc = loc.dyn_cast<CallSiteLoc>()) {
446     // Print the call stack while valid, or until the limit is reached.
447     Location callerLoc = callLoc.getCaller();
448     for (unsigned curDepth = 0; curDepth < callStackLimit; ++curDepth) {
449       emitDiagnostic(callerLoc, "called from", DiagnosticSeverity::Note);
450       if ((callLoc = callerLoc.dyn_cast<CallSiteLoc>()))
451         callerLoc = callLoc.getCaller();
452       else
453         break;
454     }
455   }
456 
457   // Emit each of the notes.
458   for (auto &note : diag.getNotes())
459     emitDiagnostic(note.getLocation(), note.str(), note.getSeverity());
460 }
461 
462 /// Get a memory buffer for the given file, or nullptr if one is not found.
463 const llvm::MemoryBuffer *
464 SourceMgrDiagnosticHandler::getBufferForFile(StringRef filename) {
465   return impl->getBufferForFile(mgr, filename);
466 }
467 
468 /// Get a memory buffer for the given file, or the main file of the source
469 /// manager if one doesn't exist. This always returns non-null.
470 llvm::SMLoc SourceMgrDiagnosticHandler::convertLocToSMLoc(FileLineColLoc loc) {
471   // Get the buffer for this filename.
472   auto *membuf = getBufferForFile(loc.getFilename());
473   if (!membuf)
474     return llvm::SMLoc();
475 
476   // TODO: This should really be upstreamed to be a method on llvm::SourceMgr.
477   // Doing so would allow it to use the offset cache that is already maintained
478   // by SrcBuffer, making this more efficient.
479   unsigned lineNo = loc.getLine();
480   unsigned columnNo = loc.getColumn();
481 
482   // Scan for the correct line number.
483   const char *position = membuf->getBufferStart();
484   const char *end = membuf->getBufferEnd();
485 
486   // We start counting line and column numbers from 1.
487   if (lineNo != 0)
488     --lineNo;
489   if (columnNo != 0)
490     --columnNo;
491 
492   while (position < end && lineNo) {
493     auto curChar = *position++;
494 
495     // Scan for newlines.  If this isn't one, ignore it.
496     if (curChar != '\r' && curChar != '\n')
497       continue;
498 
499     // We saw a line break, decrement our counter.
500     --lineNo;
501 
502     // Check for \r\n and \n\r and treat it as a single escape.  We know that
503     // looking past one character is safe because MemoryBuffer's are always nul
504     // terminated.
505     if (*position != curChar && (*position == '\r' || *position == '\n'))
506       ++position;
507   }
508 
509   // If the line/column counter was invalid, return a pointer to the start of
510   // the buffer.
511   if (lineNo || position + columnNo > end)
512     return llvm::SMLoc::getFromPointer(membuf->getBufferStart());
513 
514   // If the column is zero, try to skip to the first non-whitespace character.
515   if (columnNo == 0) {
516     auto isNewline = [](char c) { return c == '\n' || c == '\r'; };
517     auto isWhitespace = [](char c) { return c == ' ' || c == '\t'; };
518 
519     // Look for a valid non-whitespace character before the next line.
520     for (auto *newPos = position; newPos < end && !isNewline(*newPos); ++newPos)
521       if (!isWhitespace(*newPos))
522         return llvm::SMLoc::getFromPointer(newPos);
523   }
524 
525   // Otherwise return the right pointer.
526   return llvm::SMLoc::getFromPointer(position + columnNo);
527 }
528 
529 //===----------------------------------------------------------------------===//
530 // SourceMgrDiagnosticVerifierHandler
531 //===----------------------------------------------------------------------===//
532 
533 namespace mlir {
534 namespace detail {
535 // Record the expected diagnostic's position, substring and whether it was
536 // seen.
537 struct ExpectedDiag {
538   DiagnosticSeverity kind;
539   unsigned lineNo;
540   StringRef substring;
541   llvm::SMLoc fileLoc;
542   bool matched;
543 };
544 
545 struct SourceMgrDiagnosticVerifierHandlerImpl {
546   SourceMgrDiagnosticVerifierHandlerImpl() : status(success()) {}
547 
548   /// Returns the expected diagnostics for the given source file.
549   Optional<MutableArrayRef<ExpectedDiag>> getExpectedDiags(StringRef bufName);
550 
551   /// Computes the expected diagnostics for the given source buffer.
552   MutableArrayRef<ExpectedDiag>
553   computeExpectedDiags(const llvm::MemoryBuffer *buf);
554 
555   /// The current status of the verifier.
556   LogicalResult status;
557 
558   /// A list of expected diagnostics for each buffer of the source manager.
559   llvm::StringMap<SmallVector<ExpectedDiag, 2>> expectedDiagsPerFile;
560 
561   /// Regex to match the expected diagnostics format.
562   llvm::Regex expected = llvm::Regex("expected-(error|note|remark|warning) "
563                                      "*(@([+-][0-9]+|above|below))? *{{(.*)}}");
564 };
565 } // end namespace detail
566 } // end namespace mlir
567 
568 /// Given a diagnostic kind, return a human readable string for it.
569 static StringRef getDiagKindStr(DiagnosticSeverity kind) {
570   switch (kind) {
571   case DiagnosticSeverity::Note:
572     return "note";
573   case DiagnosticSeverity::Warning:
574     return "warning";
575   case DiagnosticSeverity::Error:
576     return "error";
577   case DiagnosticSeverity::Remark:
578     return "remark";
579   }
580   llvm_unreachable("Unknown DiagnosticSeverity");
581 }
582 
583 /// Returns the expected diagnostics for the given source file.
584 Optional<MutableArrayRef<ExpectedDiag>>
585 SourceMgrDiagnosticVerifierHandlerImpl::getExpectedDiags(StringRef bufName) {
586   auto expectedDiags = expectedDiagsPerFile.find(bufName);
587   if (expectedDiags != expectedDiagsPerFile.end())
588     return MutableArrayRef<ExpectedDiag>(expectedDiags->second);
589   return llvm::None;
590 }
591 
592 /// Computes the expected diagnostics for the given source buffer.
593 MutableArrayRef<ExpectedDiag>
594 SourceMgrDiagnosticVerifierHandlerImpl::computeExpectedDiags(
595     const llvm::MemoryBuffer *buf) {
596   // If the buffer is invalid, return an empty list.
597   if (!buf)
598     return llvm::None;
599   auto &expectedDiags = expectedDiagsPerFile[buf->getBufferIdentifier()];
600 
601   // The number of the last line that did not correlate to a designator.
602   unsigned lastNonDesignatorLine = 0;
603 
604   // The indices of designators that apply to the next non designator line.
605   SmallVector<unsigned, 1> designatorsForNextLine;
606 
607   // Scan the file for expected-* designators.
608   SmallVector<StringRef, 100> lines;
609   buf->getBuffer().split(lines, '\n');
610   for (unsigned lineNo = 0, e = lines.size(); lineNo < e; ++lineNo) {
611     SmallVector<StringRef, 4> matches;
612     if (!expected.match(lines[lineNo], &matches)) {
613       // Check for designators that apply to this line.
614       if (!designatorsForNextLine.empty()) {
615         for (unsigned diagIndex : designatorsForNextLine)
616           expectedDiags[diagIndex].lineNo = lineNo + 1;
617         designatorsForNextLine.clear();
618       }
619       lastNonDesignatorLine = lineNo;
620       continue;
621     }
622 
623     // Point to the start of expected-*.
624     auto expectedStart = llvm::SMLoc::getFromPointer(matches[0].data());
625 
626     DiagnosticSeverity kind;
627     if (matches[1] == "error")
628       kind = DiagnosticSeverity::Error;
629     else if (matches[1] == "warning")
630       kind = DiagnosticSeverity::Warning;
631     else if (matches[1] == "remark")
632       kind = DiagnosticSeverity::Remark;
633     else {
634       assert(matches[1] == "note");
635       kind = DiagnosticSeverity::Note;
636     }
637 
638     ExpectedDiag record{kind, lineNo + 1, matches[4], expectedStart, false};
639     auto offsetMatch = matches[2];
640     if (!offsetMatch.empty()) {
641       offsetMatch = offsetMatch.drop_front(1);
642 
643       // Get the integer value without the @ and +/- prefix.
644       if (offsetMatch[0] == '+' || offsetMatch[0] == '-') {
645         int offset;
646         offsetMatch.drop_front().getAsInteger(0, offset);
647 
648         if (offsetMatch.front() == '+')
649           record.lineNo += offset;
650         else
651           record.lineNo -= offset;
652       } else if (offsetMatch.consume_front("above")) {
653         // If the designator applies 'above' we add it to the last non
654         // designator line.
655         record.lineNo = lastNonDesignatorLine + 1;
656       } else {
657         // Otherwise, this is a 'below' designator and applies to the next
658         // non-designator line.
659         assert(offsetMatch.consume_front("below"));
660         designatorsForNextLine.push_back(expectedDiags.size());
661 
662         // Set the line number to the last in the case that this designator ends
663         // up dangling.
664         record.lineNo = e;
665       }
666     }
667     expectedDiags.push_back(record);
668   }
669   return expectedDiags;
670 }
671 
672 SourceMgrDiagnosticVerifierHandler::SourceMgrDiagnosticVerifierHandler(
673     llvm::SourceMgr &srcMgr, MLIRContext *ctx, raw_ostream &out)
674     : SourceMgrDiagnosticHandler(srcMgr, ctx, out),
675       impl(new SourceMgrDiagnosticVerifierHandlerImpl()) {
676   // Compute the expected diagnostics for each of the current files in the
677   // source manager.
678   for (unsigned i = 0, e = mgr.getNumBuffers(); i != e; ++i)
679     (void)impl->computeExpectedDiags(mgr.getMemoryBuffer(i + 1));
680 
681   // Register a handler to verify the diagnostics.
682   setHandler([&](Diagnostic &diag) {
683     // Process the main diagnostics.
684     process(diag);
685 
686     // Process each of the notes.
687     for (auto &note : diag.getNotes())
688       process(note);
689   });
690 }
691 
692 SourceMgrDiagnosticVerifierHandler::SourceMgrDiagnosticVerifierHandler(
693     llvm::SourceMgr &srcMgr, MLIRContext *ctx)
694     : SourceMgrDiagnosticVerifierHandler(srcMgr, ctx, llvm::errs()) {}
695 
696 SourceMgrDiagnosticVerifierHandler::~SourceMgrDiagnosticVerifierHandler() {
697   // Ensure that all expected diagnostics were handled.
698   (void)verify();
699 }
700 
701 /// Returns the status of the verifier and verifies that all expected
702 /// diagnostics were emitted. This return success if all diagnostics were
703 /// verified correctly, failure otherwise.
704 LogicalResult SourceMgrDiagnosticVerifierHandler::verify() {
705   // Verify that all expected errors were seen.
706   for (auto &expectedDiagsPair : impl->expectedDiagsPerFile) {
707     for (auto &err : expectedDiagsPair.second) {
708       if (err.matched)
709         continue;
710       llvm::SMRange range(err.fileLoc,
711                           llvm::SMLoc::getFromPointer(err.fileLoc.getPointer() +
712                                                       err.substring.size()));
713       mgr.PrintMessage(os, err.fileLoc, llvm::SourceMgr::DK_Error,
714                        "expected " + getDiagKindStr(err.kind) + " \"" +
715                            err.substring + "\" was not produced",
716                        range);
717       impl->status = failure();
718     }
719   }
720   impl->expectedDiagsPerFile.clear();
721   return impl->status;
722 }
723 
724 /// Process a single diagnostic.
725 void SourceMgrDiagnosticVerifierHandler::process(Diagnostic &diag) {
726   auto kind = diag.getSeverity();
727 
728   // Process a FileLineColLoc.
729   if (auto fileLoc = getFileLineColLoc(diag.getLocation()))
730     return process(*fileLoc, diag.str(), kind);
731 
732   emitDiagnostic(diag.getLocation(),
733                  "unexpected " + getDiagKindStr(kind) + ": " + diag.str(),
734                  DiagnosticSeverity::Error);
735   impl->status = failure();
736 }
737 
738 /// Process a FileLineColLoc diagnostic.
739 void SourceMgrDiagnosticVerifierHandler::process(FileLineColLoc loc,
740                                                  StringRef msg,
741                                                  DiagnosticSeverity kind) {
742   // Get the expected diagnostics for this file.
743   auto diags = impl->getExpectedDiags(loc.getFilename());
744   if (!diags)
745     diags = impl->computeExpectedDiags(getBufferForFile(loc.getFilename()));
746 
747   // Search for a matching expected diagnostic.
748   // If we find something that is close then emit a more specific error.
749   ExpectedDiag *nearMiss = nullptr;
750 
751   // If this was an expected error, remember that we saw it and return.
752   unsigned line = loc.getLine();
753   for (auto &e : *diags) {
754     if (line == e.lineNo && msg.contains(e.substring)) {
755       if (e.kind == kind) {
756         e.matched = true;
757         return;
758       }
759 
760       // If this only differs based on the diagnostic kind, then consider it
761       // to be a near miss.
762       nearMiss = &e;
763     }
764   }
765 
766   // Otherwise, emit an error for the near miss.
767   if (nearMiss)
768     mgr.PrintMessage(os, nearMiss->fileLoc, llvm::SourceMgr::DK_Error,
769                      "'" + getDiagKindStr(kind) +
770                          "' diagnostic emitted when expecting a '" +
771                          getDiagKindStr(nearMiss->kind) + "'");
772   else
773     emitDiagnostic(loc, "unexpected " + getDiagKindStr(kind) + ": " + msg,
774                    DiagnosticSeverity::Error);
775   impl->status = failure();
776 }
777 
778 //===----------------------------------------------------------------------===//
779 // ParallelDiagnosticHandler
780 //===----------------------------------------------------------------------===//
781 
782 namespace mlir {
783 namespace detail {
784 struct ParallelDiagnosticHandlerImpl : public llvm::PrettyStackTraceEntry {
785   struct ThreadDiagnostic {
786     ThreadDiagnostic(size_t id, Diagnostic diag)
787         : id(id), diag(std::move(diag)) {}
788     bool operator<(const ThreadDiagnostic &rhs) const { return id < rhs.id; }
789 
790     /// The id for this diagnostic, this is used for ordering.
791     /// Note: This id corresponds to the ordered position of the current element
792     ///       being processed by a given thread.
793     size_t id;
794 
795     /// The diagnostic.
796     Diagnostic diag;
797   };
798 
799   ParallelDiagnosticHandlerImpl(MLIRContext *ctx) : handlerID(0), context(ctx) {
800     handlerID = ctx->getDiagEngine().registerHandler([this](Diagnostic &diag) {
801       uint64_t tid = llvm::get_threadid();
802       llvm::sys::SmartScopedLock<true> lock(mutex);
803 
804       // If this thread is not tracked, then return failure to let another
805       // handler process this diagnostic.
806       if (!threadToOrderID.count(tid))
807         return failure();
808 
809       // Append a new diagnostic.
810       diagnostics.emplace_back(threadToOrderID[tid], std::move(diag));
811       return success();
812     });
813   }
814 
815   ~ParallelDiagnosticHandlerImpl() override {
816     // Erase this handler from the context.
817     context->getDiagEngine().eraseHandler(handlerID);
818 
819     // Early exit if there are no diagnostics, this is the common case.
820     if (diagnostics.empty())
821       return;
822 
823     // Emit the diagnostics back to the context.
824     emitDiagnostics([&](Diagnostic diag) {
825       return context->getDiagEngine().emit(std::move(diag));
826     });
827   }
828 
829   /// Utility method to emit any held diagnostics.
830   void emitDiagnostics(std::function<void(Diagnostic)> emitFn) const {
831     // Stable sort all of the diagnostics that were emitted. This creates a
832     // deterministic ordering for the diagnostics based upon which order id they
833     // were emitted for.
834     std::stable_sort(diagnostics.begin(), diagnostics.end());
835 
836     // Emit each diagnostic to the context again.
837     for (ThreadDiagnostic &diag : diagnostics)
838       emitFn(std::move(diag.diag));
839   }
840 
841   /// Set the order id for the current thread.
842   void setOrderIDForThread(size_t orderID) {
843     uint64_t tid = llvm::get_threadid();
844     llvm::sys::SmartScopedLock<true> lock(mutex);
845     threadToOrderID[tid] = orderID;
846   }
847 
848   /// Remove the order id for the current thread.
849   void eraseOrderIDForThread() {
850     uint64_t tid = llvm::get_threadid();
851     llvm::sys::SmartScopedLock<true> lock(mutex);
852     threadToOrderID.erase(tid);
853   }
854 
855   /// Dump the current diagnostics that were inflight.
856   void print(raw_ostream &os) const override {
857     // Early exit if there are no diagnostics, this is the common case.
858     if (diagnostics.empty())
859       return;
860 
861     os << "In-Flight Diagnostics:\n";
862     emitDiagnostics([&](Diagnostic diag) {
863       os.indent(4);
864 
865       // Print each diagnostic with the format:
866       //   "<location>: <kind>: <msg>"
867       if (!diag.getLocation().isa<UnknownLoc>())
868         os << diag.getLocation() << ": ";
869       switch (diag.getSeverity()) {
870       case DiagnosticSeverity::Error:
871         os << "error: ";
872         break;
873       case DiagnosticSeverity::Warning:
874         os << "warning: ";
875         break;
876       case DiagnosticSeverity::Note:
877         os << "note: ";
878         break;
879       case DiagnosticSeverity::Remark:
880         os << "remark: ";
881         break;
882       }
883       os << diag << '\n';
884     });
885   }
886 
887   /// A smart mutex to lock access to the internal state.
888   llvm::sys::SmartMutex<true> mutex;
889 
890   /// A mapping between the thread id and the current order id.
891   DenseMap<uint64_t, size_t> threadToOrderID;
892 
893   /// An unordered list of diagnostics that were emitted.
894   mutable std::vector<ThreadDiagnostic> diagnostics;
895 
896   /// The unique id for the parallel handler.
897   DiagnosticEngine::HandlerID handlerID;
898 
899   /// The context to emit the diagnostics to.
900   MLIRContext *context;
901 };
902 } // end namespace detail
903 } // end namespace mlir
904 
905 ParallelDiagnosticHandler::ParallelDiagnosticHandler(MLIRContext *ctx)
906     : impl(new ParallelDiagnosticHandlerImpl(ctx)) {}
907 ParallelDiagnosticHandler::~ParallelDiagnosticHandler() {}
908 
909 /// Set the order id for the current thread.
910 void ParallelDiagnosticHandler::setOrderIDForThread(size_t orderID) {
911   impl->setOrderIDForThread(orderID);
912 }
913 
914 /// Remove the order id for the current thread. This removes the thread from
915 /// diagnostics tracking.
916 void ParallelDiagnosticHandler::eraseOrderIDForThread() {
917   impl->eraseOrderIDForThread();
918 }
919