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