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