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