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   os << val;
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   switch (loc->getKind()) {
370   case StandardAttributes::NameLocation:
371     return getFileLineColLoc(loc.cast<NameLoc>().getChildLoc());
372   case StandardAttributes::FileLineColLocation:
373     return loc.cast<FileLineColLoc>();
374   case StandardAttributes::CallSiteLocation:
375     // Process the callee of a callsite location.
376     return getFileLineColLoc(loc.cast<CallSiteLoc>().getCallee());
377   case StandardAttributes::FusedLocation:
378     for (auto subLoc : loc.cast<FusedLoc>().getLocations()) {
379       if (auto callLoc = getFileLineColLoc(subLoc)) {
380         return callLoc;
381       }
382     }
383     return llvm::None;
384   default:
385     return llvm::None;
386   }
387 }
388 
389 /// Return a processable CallSiteLoc from the given location.
390 static Optional<CallSiteLoc> getCallSiteLoc(Location loc) {
391   switch (loc->getKind()) {
392   case StandardAttributes::NameLocation:
393     return getCallSiteLoc(loc.cast<NameLoc>().getChildLoc());
394   case StandardAttributes::CallSiteLocation:
395     return loc.cast<CallSiteLoc>();
396   case StandardAttributes::FusedLocation:
397     for (auto subLoc : loc.cast<FusedLoc>().getLocations()) {
398       if (auto callLoc = getCallSiteLoc(subLoc)) {
399         return callLoc;
400       }
401     }
402     return llvm::None;
403   default:
404     return llvm::None;
405   }
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(llvm::SourceMgr &mgr,
424                                                        MLIRContext *ctx,
425                                                        raw_ostream &os)
426     : ScopedDiagnosticHandler(ctx), mgr(mgr), os(os),
427       impl(new SourceMgrDiagnosticHandlerImpl()) {
428   setHandler([this](Diagnostic &diag) { emitDiagnostic(diag); });
429 }
430 
431 SourceMgrDiagnosticHandler::SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr,
432                                                        MLIRContext *ctx)
433     : SourceMgrDiagnosticHandler(mgr, ctx, llvm::errs()) {}
434 
435 SourceMgrDiagnosticHandler::~SourceMgrDiagnosticHandler() {}
436 
437 void SourceMgrDiagnosticHandler::emitDiagnostic(Location loc, Twine message,
438                                                 DiagnosticSeverity kind,
439                                                 bool displaySourceLine) {
440   // Extract a file location from this loc.
441   auto fileLoc = getFileLineColLoc(loc);
442 
443   // If one doesn't exist, then print the raw message without a source location.
444   if (!fileLoc) {
445     std::string str;
446     llvm::raw_string_ostream strOS(str);
447     if (!loc.isa<UnknownLoc>())
448       strOS << loc << ": ";
449     strOS << message;
450     return mgr.PrintMessage(os, llvm::SMLoc(), getDiagKind(kind), strOS.str());
451   }
452 
453   // Otherwise if we are displaying the source line, try to convert the file
454   // location to an SMLoc.
455   if (displaySourceLine) {
456     auto smloc = convertLocToSMLoc(*fileLoc);
457     if (smloc.isValid())
458       return mgr.PrintMessage(os, smloc, getDiagKind(kind), message);
459   }
460 
461   // If the conversion was unsuccessful, create a diagnostic with the file
462   // information. We manually combine the line and column to avoid asserts in
463   // the constructor of SMDiagnostic that takes a location.
464   std::string locStr;
465   llvm::raw_string_ostream locOS(locStr);
466   locOS << fileLoc->getFilename() << ":" << fileLoc->getLine() << ":"
467         << fileLoc->getColumn();
468   llvm::SMDiagnostic diag(locOS.str(), getDiagKind(kind), message.str());
469   diag.print(nullptr, os);
470 }
471 
472 /// Emit the given diagnostic with the held source manager.
473 void SourceMgrDiagnosticHandler::emitDiagnostic(Diagnostic &diag) {
474   // Emit the diagnostic.
475   Location loc = diag.getLocation();
476   emitDiagnostic(loc, diag.str(), diag.getSeverity());
477 
478   // If the diagnostic location was a call site location, then print the call
479   // stack as well.
480   if (auto callLoc = getCallSiteLoc(loc)) {
481     // Print the call stack while valid, or until the limit is reached.
482     loc = callLoc->getCaller();
483     for (unsigned curDepth = 0; curDepth < callStackLimit; ++curDepth) {
484       emitDiagnostic(loc, "called from", DiagnosticSeverity::Note);
485       if ((callLoc = getCallSiteLoc(loc)))
486         loc = callLoc->getCaller();
487       else
488         break;
489     }
490   }
491 
492   // Emit each of the notes. Only display the source code if the location is
493   // different from the previous location.
494   for (auto &note : diag.getNotes()) {
495     emitDiagnostic(note.getLocation(), note.str(), note.getSeverity(),
496                    /*displaySourceLine=*/loc != note.getLocation());
497     loc = note.getLocation();
498   }
499 }
500 
501 /// Get a memory buffer for the given file, or nullptr if one is not found.
502 const llvm::MemoryBuffer *
503 SourceMgrDiagnosticHandler::getBufferForFile(StringRef filename) {
504   if (unsigned id = impl->getSourceMgrBufferIDForFile(mgr, filename))
505     return mgr.getMemoryBuffer(id);
506   return nullptr;
507 }
508 
509 /// Get a memory buffer for the given file, or the main file of the source
510 /// manager if one doesn't exist. This always returns non-null.
511 llvm::SMLoc SourceMgrDiagnosticHandler::convertLocToSMLoc(FileLineColLoc loc) {
512   // The column and line may be zero to represent unknown column and/or unknown
513   /// line/column information.
514   if (loc.getLine() == 0 || loc.getColumn() == 0)
515     return llvm::SMLoc();
516 
517   unsigned bufferId = impl->getSourceMgrBufferIDForFile(mgr, loc.getFilename());
518   if (!bufferId)
519     return llvm::SMLoc();
520   return mgr.FindLocForLineAndColumn(bufferId, loc.getLine(), loc.getColumn());
521 }
522 
523 //===----------------------------------------------------------------------===//
524 // SourceMgrDiagnosticVerifierHandler
525 //===----------------------------------------------------------------------===//
526 
527 namespace mlir {
528 namespace detail {
529 // Record the expected diagnostic's position, substring and whether it was
530 // seen.
531 struct ExpectedDiag {
532   DiagnosticSeverity kind;
533   unsigned lineNo;
534   StringRef substring;
535   llvm::SMLoc fileLoc;
536   bool matched;
537 };
538 
539 struct SourceMgrDiagnosticVerifierHandlerImpl {
540   SourceMgrDiagnosticVerifierHandlerImpl() : status(success()) {}
541 
542   /// Returns the expected diagnostics for the given source file.
543   Optional<MutableArrayRef<ExpectedDiag>> getExpectedDiags(StringRef bufName);
544 
545   /// Computes the expected diagnostics for the given source buffer.
546   MutableArrayRef<ExpectedDiag>
547   computeExpectedDiags(const llvm::MemoryBuffer *buf);
548 
549   /// The current status of the verifier.
550   LogicalResult status;
551 
552   /// A list of expected diagnostics for each buffer of the source manager.
553   llvm::StringMap<SmallVector<ExpectedDiag, 2>> expectedDiagsPerFile;
554 
555   /// Regex to match the expected diagnostics format.
556   llvm::Regex expected = llvm::Regex("expected-(error|note|remark|warning) "
557                                      "*(@([+-][0-9]+|above|below))? *{{(.*)}}");
558 };
559 } // end namespace detail
560 } // end namespace mlir
561 
562 /// Given a diagnostic kind, return a human readable string for it.
563 static StringRef getDiagKindStr(DiagnosticSeverity kind) {
564   switch (kind) {
565   case DiagnosticSeverity::Note:
566     return "note";
567   case DiagnosticSeverity::Warning:
568     return "warning";
569   case DiagnosticSeverity::Error:
570     return "error";
571   case DiagnosticSeverity::Remark:
572     return "remark";
573   }
574   llvm_unreachable("Unknown DiagnosticSeverity");
575 }
576 
577 /// Returns the expected diagnostics for the given source file.
578 Optional<MutableArrayRef<ExpectedDiag>>
579 SourceMgrDiagnosticVerifierHandlerImpl::getExpectedDiags(StringRef bufName) {
580   auto expectedDiags = expectedDiagsPerFile.find(bufName);
581   if (expectedDiags != expectedDiagsPerFile.end())
582     return MutableArrayRef<ExpectedDiag>(expectedDiags->second);
583   return llvm::None;
584 }
585 
586 /// Computes the expected diagnostics for the given source buffer.
587 MutableArrayRef<ExpectedDiag>
588 SourceMgrDiagnosticVerifierHandlerImpl::computeExpectedDiags(
589     const llvm::MemoryBuffer *buf) {
590   // If the buffer is invalid, return an empty list.
591   if (!buf)
592     return llvm::None;
593   auto &expectedDiags = expectedDiagsPerFile[buf->getBufferIdentifier()];
594 
595   // The number of the last line that did not correlate to a designator.
596   unsigned lastNonDesignatorLine = 0;
597 
598   // The indices of designators that apply to the next non designator line.
599   SmallVector<unsigned, 1> designatorsForNextLine;
600 
601   // Scan the file for expected-* designators.
602   SmallVector<StringRef, 100> lines;
603   buf->getBuffer().split(lines, '\n');
604   for (unsigned lineNo = 0, e = lines.size(); lineNo < e; ++lineNo) {
605     SmallVector<StringRef, 4> matches;
606     if (!expected.match(lines[lineNo], &matches)) {
607       // Check for designators that apply to this line.
608       if (!designatorsForNextLine.empty()) {
609         for (unsigned diagIndex : designatorsForNextLine)
610           expectedDiags[diagIndex].lineNo = lineNo + 1;
611         designatorsForNextLine.clear();
612       }
613       lastNonDesignatorLine = lineNo;
614       continue;
615     }
616 
617     // Point to the start of expected-*.
618     auto expectedStart = llvm::SMLoc::getFromPointer(matches[0].data());
619 
620     DiagnosticSeverity kind;
621     if (matches[1] == "error")
622       kind = DiagnosticSeverity::Error;
623     else if (matches[1] == "warning")
624       kind = DiagnosticSeverity::Warning;
625     else if (matches[1] == "remark")
626       kind = DiagnosticSeverity::Remark;
627     else {
628       assert(matches[1] == "note");
629       kind = DiagnosticSeverity::Note;
630     }
631 
632     ExpectedDiag record{kind, lineNo + 1, matches[4], expectedStart, false};
633     auto offsetMatch = matches[2];
634     if (!offsetMatch.empty()) {
635       offsetMatch = offsetMatch.drop_front(1);
636 
637       // Get the integer value without the @ and +/- prefix.
638       if (offsetMatch[0] == '+' || offsetMatch[0] == '-') {
639         int offset;
640         offsetMatch.drop_front().getAsInteger(0, offset);
641 
642         if (offsetMatch.front() == '+')
643           record.lineNo += offset;
644         else
645           record.lineNo -= offset;
646       } else if (offsetMatch.consume_front("above")) {
647         // If the designator applies 'above' we add it to the last non
648         // designator line.
649         record.lineNo = lastNonDesignatorLine + 1;
650       } else {
651         // Otherwise, this is a 'below' designator and applies to the next
652         // non-designator line.
653         assert(offsetMatch.consume_front("below"));
654         designatorsForNextLine.push_back(expectedDiags.size());
655 
656         // Set the line number to the last in the case that this designator ends
657         // up dangling.
658         record.lineNo = e;
659       }
660     }
661     expectedDiags.push_back(record);
662   }
663   return expectedDiags;
664 }
665 
666 SourceMgrDiagnosticVerifierHandler::SourceMgrDiagnosticVerifierHandler(
667     llvm::SourceMgr &srcMgr, MLIRContext *ctx, raw_ostream &out)
668     : SourceMgrDiagnosticHandler(srcMgr, ctx, out),
669       impl(new SourceMgrDiagnosticVerifierHandlerImpl()) {
670   // Compute the expected diagnostics for each of the current files in the
671   // source manager.
672   for (unsigned i = 0, e = mgr.getNumBuffers(); i != e; ++i)
673     (void)impl->computeExpectedDiags(mgr.getMemoryBuffer(i + 1));
674 
675   // Register a handler to verify the diagnostics.
676   setHandler([&](Diagnostic &diag) {
677     // Process the main diagnostics.
678     process(diag);
679 
680     // Process each of the notes.
681     for (auto &note : diag.getNotes())
682       process(note);
683   });
684 }
685 
686 SourceMgrDiagnosticVerifierHandler::SourceMgrDiagnosticVerifierHandler(
687     llvm::SourceMgr &srcMgr, MLIRContext *ctx)
688     : SourceMgrDiagnosticVerifierHandler(srcMgr, ctx, llvm::errs()) {}
689 
690 SourceMgrDiagnosticVerifierHandler::~SourceMgrDiagnosticVerifierHandler() {
691   // Ensure that all expected diagnostics were handled.
692   (void)verify();
693 }
694 
695 /// Returns the status of the verifier and verifies that all expected
696 /// diagnostics were emitted. This return success if all diagnostics were
697 /// verified correctly, failure otherwise.
698 LogicalResult SourceMgrDiagnosticVerifierHandler::verify() {
699   // Verify that all expected errors were seen.
700   for (auto &expectedDiagsPair : impl->expectedDiagsPerFile) {
701     for (auto &err : expectedDiagsPair.second) {
702       if (err.matched)
703         continue;
704       llvm::SMRange range(err.fileLoc,
705                           llvm::SMLoc::getFromPointer(err.fileLoc.getPointer() +
706                                                       err.substring.size()));
707       mgr.PrintMessage(os, err.fileLoc, llvm::SourceMgr::DK_Error,
708                        "expected " + getDiagKindStr(err.kind) + " \"" +
709                            err.substring + "\" was not produced",
710                        range);
711       impl->status = failure();
712     }
713   }
714   impl->expectedDiagsPerFile.clear();
715   return impl->status;
716 }
717 
718 /// Process a single diagnostic.
719 void SourceMgrDiagnosticVerifierHandler::process(Diagnostic &diag) {
720   auto kind = diag.getSeverity();
721 
722   // Process a FileLineColLoc.
723   if (auto fileLoc = getFileLineColLoc(diag.getLocation()))
724     return process(*fileLoc, diag.str(), kind);
725 
726   emitDiagnostic(diag.getLocation(),
727                  "unexpected " + getDiagKindStr(kind) + ": " + diag.str(),
728                  DiagnosticSeverity::Error);
729   impl->status = failure();
730 }
731 
732 /// Process a FileLineColLoc diagnostic.
733 void SourceMgrDiagnosticVerifierHandler::process(FileLineColLoc loc,
734                                                  StringRef msg,
735                                                  DiagnosticSeverity kind) {
736   // Get the expected diagnostics for this file.
737   auto diags = impl->getExpectedDiags(loc.getFilename());
738   if (!diags)
739     diags = impl->computeExpectedDiags(getBufferForFile(loc.getFilename()));
740 
741   // Search for a matching expected diagnostic.
742   // If we find something that is close then emit a more specific error.
743   ExpectedDiag *nearMiss = nullptr;
744 
745   // If this was an expected error, remember that we saw it and return.
746   unsigned line = loc.getLine();
747   for (auto &e : *diags) {
748     if (line == e.lineNo && msg.contains(e.substring)) {
749       if (e.kind == kind) {
750         e.matched = true;
751         return;
752       }
753 
754       // If this only differs based on the diagnostic kind, then consider it
755       // to be a near miss.
756       nearMiss = &e;
757     }
758   }
759 
760   // Otherwise, emit an error for the near miss.
761   if (nearMiss)
762     mgr.PrintMessage(os, nearMiss->fileLoc, llvm::SourceMgr::DK_Error,
763                      "'" + getDiagKindStr(kind) +
764                          "' diagnostic emitted when expecting a '" +
765                          getDiagKindStr(nearMiss->kind) + "'");
766   else
767     emitDiagnostic(loc, "unexpected " + getDiagKindStr(kind) + ": " + msg,
768                    DiagnosticSeverity::Error);
769   impl->status = failure();
770 }
771 
772 //===----------------------------------------------------------------------===//
773 // ParallelDiagnosticHandler
774 //===----------------------------------------------------------------------===//
775 
776 namespace mlir {
777 namespace detail {
778 struct ParallelDiagnosticHandlerImpl : public llvm::PrettyStackTraceEntry {
779   struct ThreadDiagnostic {
780     ThreadDiagnostic(size_t id, Diagnostic diag)
781         : id(id), diag(std::move(diag)) {}
782     bool operator<(const ThreadDiagnostic &rhs) const { return id < rhs.id; }
783 
784     /// The id for this diagnostic, this is used for ordering.
785     /// Note: This id corresponds to the ordered position of the current element
786     ///       being processed by a given thread.
787     size_t id;
788 
789     /// The diagnostic.
790     Diagnostic diag;
791   };
792 
793   ParallelDiagnosticHandlerImpl(MLIRContext *ctx) : handlerID(0), context(ctx) {
794     handlerID = ctx->getDiagEngine().registerHandler([this](Diagnostic &diag) {
795       uint64_t tid = llvm::get_threadid();
796       llvm::sys::SmartScopedLock<true> lock(mutex);
797 
798       // If this thread is not tracked, then return failure to let another
799       // handler process this diagnostic.
800       if (!threadToOrderID.count(tid))
801         return failure();
802 
803       // Append a new diagnostic.
804       diagnostics.emplace_back(threadToOrderID[tid], std::move(diag));
805       return success();
806     });
807   }
808 
809   ~ParallelDiagnosticHandlerImpl() override {
810     // Erase this handler from the context.
811     context->getDiagEngine().eraseHandler(handlerID);
812 
813     // Early exit if there are no diagnostics, this is the common case.
814     if (diagnostics.empty())
815       return;
816 
817     // Emit the diagnostics back to the context.
818     emitDiagnostics([&](Diagnostic diag) {
819       return context->getDiagEngine().emit(std::move(diag));
820     });
821   }
822 
823   /// Utility method to emit any held diagnostics.
824   void emitDiagnostics(std::function<void(Diagnostic)> emitFn) const {
825     // Stable sort all of the diagnostics that were emitted. This creates a
826     // deterministic ordering for the diagnostics based upon which order id they
827     // were emitted for.
828     std::stable_sort(diagnostics.begin(), diagnostics.end());
829 
830     // Emit each diagnostic to the context again.
831     for (ThreadDiagnostic &diag : diagnostics)
832       emitFn(std::move(diag.diag));
833   }
834 
835   /// Set the order id for the current thread.
836   void setOrderIDForThread(size_t orderID) {
837     uint64_t tid = llvm::get_threadid();
838     llvm::sys::SmartScopedLock<true> lock(mutex);
839     threadToOrderID[tid] = orderID;
840   }
841 
842   /// Remove the order id for the current thread.
843   void eraseOrderIDForThread() {
844     uint64_t tid = llvm::get_threadid();
845     llvm::sys::SmartScopedLock<true> lock(mutex);
846     threadToOrderID.erase(tid);
847   }
848 
849   /// Dump the current diagnostics that were inflight.
850   void print(raw_ostream &os) const override {
851     // Early exit if there are no diagnostics, this is the common case.
852     if (diagnostics.empty())
853       return;
854 
855     os << "In-Flight Diagnostics:\n";
856     emitDiagnostics([&](Diagnostic diag) {
857       os.indent(4);
858 
859       // Print each diagnostic with the format:
860       //   "<location>: <kind>: <msg>"
861       if (!diag.getLocation().isa<UnknownLoc>())
862         os << diag.getLocation() << ": ";
863       switch (diag.getSeverity()) {
864       case DiagnosticSeverity::Error:
865         os << "error: ";
866         break;
867       case DiagnosticSeverity::Warning:
868         os << "warning: ";
869         break;
870       case DiagnosticSeverity::Note:
871         os << "note: ";
872         break;
873       case DiagnosticSeverity::Remark:
874         os << "remark: ";
875         break;
876       }
877       os << diag << '\n';
878     });
879   }
880 
881   /// A smart mutex to lock access to the internal state.
882   llvm::sys::SmartMutex<true> mutex;
883 
884   /// A mapping between the thread id and the current order id.
885   DenseMap<uint64_t, size_t> threadToOrderID;
886 
887   /// An unordered list of diagnostics that were emitted.
888   mutable std::vector<ThreadDiagnostic> diagnostics;
889 
890   /// The unique id for the parallel handler.
891   DiagnosticEngine::HandlerID handlerID;
892 
893   /// The context to emit the diagnostics to.
894   MLIRContext *context;
895 };
896 } // end namespace detail
897 } // end namespace mlir
898 
899 ParallelDiagnosticHandler::ParallelDiagnosticHandler(MLIRContext *ctx)
900     : impl(new ParallelDiagnosticHandlerImpl(ctx)) {}
901 ParallelDiagnosticHandler::~ParallelDiagnosticHandler() {}
902 
903 /// Set the order id for the current thread.
904 void ParallelDiagnosticHandler::setOrderIDForThread(size_t orderID) {
905   impl->setOrderIDForThread(orderID);
906 }
907 
908 /// Remove the order id for the current thread. This removes the thread from
909 /// diagnostics tracking.
910 void ParallelDiagnosticHandler::eraseOrderIDForThread() {
911   impl->eraseOrderIDForThread();
912 }
913