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