MLIR  22.0.0git
Diagnostics.cpp
Go to the documentation of this file.
1 //===- Diagnostics.cpp - MLIR Diagnostics ---------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "mlir/IR/Diagnostics.h"
10 #include "mlir/IR/Attributes.h"
11 #include "mlir/IR/Location.h"
12 #include "mlir/IR/MLIRContext.h"
13 #include "mlir/IR/Operation.h"
14 #include "mlir/IR/Types.h"
15 #include "llvm/ADT/MapVector.h"
16 #include "llvm/ADT/SmallString.h"
17 #include "llvm/ADT/StringMap.h"
18 #include "llvm/ADT/TypeSwitch.h"
19 #include "llvm/Support/Mutex.h"
20 #include "llvm/Support/PrettyStackTrace.h"
21 #include "llvm/Support/Regex.h"
22 #include "llvm/Support/Signals.h"
23 #include "llvm/Support/SourceMgr.h"
24 #include "llvm/Support/raw_ostream.h"
25 #include <optional>
26 
27 using namespace mlir;
28 using namespace mlir::detail;
29 
30 //===----------------------------------------------------------------------===//
31 // DiagnosticArgument
32 //===----------------------------------------------------------------------===//
33 
34 /// Construct from an Attribute.
37  opaqueVal(reinterpret_cast<intptr_t>(attr.getAsOpaquePointer())) {}
38 
39 /// Construct from a Type.
42  opaqueVal(reinterpret_cast<intptr_t>(val.getAsOpaquePointer())) {}
43 
44 /// Returns this argument as an Attribute.
48  reinterpret_cast<const void *>(opaqueVal));
49 }
50 
51 /// Returns this argument as a 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) {
61  os << getAsAttribute();
62  break;
64  os << getAsDouble();
65  break;
67  os << getAsInteger();
68  break;
70  os << getAsString();
71  break;
73  os << '\'' << getAsType() << '\'';
74  break;
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 }
108  arguments.push_back(DiagnosticArgument(twineToStrRef(val, strings)));
109  return *this;
110 }
111 
113  arguments.push_back(DiagnosticArgument(val));
114  return *this;
115 }
116 
117 /// Stream in an OperationName.
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 /// Adjusts operation printing flags used in diagnostics for the given severity
126 /// level.
128  DiagnosticSeverity severity) {
129  flags.useLocalScope();
130  flags.elideLargeElementsAttrs();
131  if (severity == DiagnosticSeverity::Error)
132  flags.printGenericOpForm();
133  return flags;
134 }
135 
136 /// Stream in an Operation.
138  return appendOp(op, OpPrintingFlags());
139 }
140 
142  std::string str;
143  llvm::raw_string_ostream os(str);
144  op.print(os, adjustPrintingFlags(flags, severity));
145  // Print on a new line for better readability if the op will be printed on
146  // multiple lines.
147  if (str.find('\n') != std::string::npos)
148  *this << '\n';
149  return *this << str;
150 }
151 
152 /// Stream in a Value.
154  std::string str;
155  llvm::raw_string_ostream os(str);
156  val.print(os, adjustPrintingFlags(OpPrintingFlags(), severity));
157  return *this << str;
158 }
159 
160 /// Outputs this diagnostic to a stream.
161 void Diagnostic::print(raw_ostream &os) const {
162  for (auto &arg : getArguments())
163  arg.print(os);
164 }
165 
166 /// Convert the diagnostic to a string.
167 std::string Diagnostic::str() const {
168  std::string str;
169  llvm::raw_string_ostream os(str);
170  print(os);
171  return str;
172 }
173 
174 /// Attaches a note to this diagnostic. A new location may be optionally
175 /// provided, if not, then the location defaults to the one specified for this
176 /// diagnostic. Notes may not be attached to other notes.
177 Diagnostic &Diagnostic::attachNote(std::optional<Location> noteLoc) {
178  // We don't allow attaching notes to notes.
179  assert(severity != DiagnosticSeverity::Note &&
180  "cannot attach a note to a note");
181 
182  // If a location wasn't provided then reuse our location.
183  if (!noteLoc)
184  noteLoc = loc;
185 
186  /// Append and return a new note.
187  notes.push_back(
188  std::make_unique<Diagnostic>(*noteLoc, DiagnosticSeverity::Note));
189  return *notes.back();
190 }
191 
192 /// Allow a diagnostic to be converted to 'failure'.
193 Diagnostic::operator LogicalResult() const { return failure(); }
194 
195 //===----------------------------------------------------------------------===//
196 // InFlightDiagnostic
197 //===----------------------------------------------------------------------===//
198 
199 /// Allow an inflight diagnostic to be converted to 'failure', otherwise
200 /// 'success' if this is an empty diagnostic.
201 InFlightDiagnostic::operator LogicalResult() const {
202  return failure(isActive());
203 }
204 
205 /// Reports the diagnostic to the engine.
207  // If this diagnostic is still inflight and it hasn't been abandoned, then
208  // report it.
209  if (isInFlight()) {
210  owner->emit(std::move(*impl));
211  owner = nullptr;
212  }
213  impl.reset();
214 }
215 
216 /// Abandons this diagnostic.
217 void InFlightDiagnostic::abandon() { owner = nullptr; }
218 
219 //===----------------------------------------------------------------------===//
220 // DiagnosticEngineImpl
221 //===----------------------------------------------------------------------===//
222 
223 namespace mlir {
224 namespace detail {
226  /// Emit a diagnostic using the registered issue handle if present, or with
227  /// the default behavior if not.
228  void emit(Diagnostic &&diag);
229 
230  /// A mutex to ensure that diagnostics emission is thread-safe.
232 
233  /// These are the handlers used to report diagnostics.
235  2>
237 
238  /// This is a unique identifier counter for diagnostic handlers in the
239  /// context. This id starts at 1 to allow for 0 to be used as a sentinel.
241 };
242 } // namespace detail
243 } // namespace mlir
244 
245 /// Emit a diagnostic using the registered issue handle if present, or with
246 /// the default behavior if not.
248  llvm::sys::SmartScopedLock<true> lock(mutex);
249 
250  // Try to process the given diagnostic on one of the registered handlers.
251  // Handlers are walked in reverse order, so that the most recent handler is
252  // processed first.
253  for (auto &handlerIt : llvm::reverse(handlers))
254  if (succeeded(handlerIt.second(diag)))
255  return;
256 
257  // Otherwise, if this is an error we emit it to stderr.
258  if (diag.getSeverity() != DiagnosticSeverity::Error)
259  return;
260 
261  auto &os = llvm::errs();
262  if (!llvm::isa<UnknownLoc>(diag.getLocation()))
263  os << diag.getLocation() << ": ";
264  os << "error: ";
265 
266  // The default behavior for errors is to emit them to stderr.
267  os << diag << '\n';
268  os.flush();
269 }
270 
271 //===----------------------------------------------------------------------===//
272 // DiagnosticEngine
273 //===----------------------------------------------------------------------===//
274 
275 DiagnosticEngine::DiagnosticEngine() : impl(new DiagnosticEngineImpl()) {}
277 
278 /// Register a new handler for diagnostics to the engine. This function returns
279 /// a unique identifier for the registered handler, which can be used to
280 /// unregister this handler at a later time.
282  llvm::sys::SmartScopedLock<true> lock(impl->mutex);
283  auto uniqueID = impl->uniqueHandlerId++;
284  impl->handlers.insert({uniqueID, std::move(handler)});
285  return uniqueID;
286 }
287 
288 /// Erase the registered diagnostic handler with the given identifier.
290  llvm::sys::SmartScopedLock<true> lock(impl->mutex);
291  impl->handlers.erase(handlerID);
292 }
293 
294 /// Emit a diagnostic using the registered issue handler if present, or with
295 /// the default behavior if not.
297  assert(diag.getSeverity() != DiagnosticSeverity::Note &&
298  "notes should not be emitted directly");
299  impl->emit(std::move(diag));
300 }
301 
302 /// Helper function used to emit a diagnostic with an optionally empty twine
303 /// message. If the message is empty, then it is not inserted into the
304 /// diagnostic.
305 static InFlightDiagnostic
306 emitDiag(Location location, DiagnosticSeverity severity, const Twine &message) {
307  MLIRContext *ctx = location->getContext();
308  auto &diagEngine = ctx->getDiagEngine();
309  auto diag = diagEngine.emit(location, severity);
310  if (!message.isTriviallyEmpty())
311  diag << message;
312 
313  // Add the stack trace as a note if necessary.
315  std::string bt;
316  {
317  llvm::raw_string_ostream stream(bt);
318  llvm::sys::PrintStackTrace(stream);
319  }
320  if (!bt.empty())
321  diag.attachNote() << "diagnostic emitted with trace:\n" << bt;
322  }
323 
324  return diag;
325 }
326 
327 /// Emit an error message using this location.
329 InFlightDiagnostic mlir::emitError(Location loc, const Twine &message) {
330  return emitDiag(loc, DiagnosticSeverity::Error, message);
331 }
332 
333 /// Emit a warning message using this location.
335  return emitWarning(loc, {});
336 }
337 InFlightDiagnostic mlir::emitWarning(Location loc, const Twine &message) {
338  return emitDiag(loc, DiagnosticSeverity::Warning, message);
339 }
340 
341 /// Emit a remark message using this location.
343  return emitRemark(loc, {});
344 }
345 InFlightDiagnostic mlir::emitRemark(Location loc, const Twine &message) {
346  return emitDiag(loc, DiagnosticSeverity::Remark, message);
347 }
348 
349 //===----------------------------------------------------------------------===//
350 // ScopedDiagnosticHandler
351 //===----------------------------------------------------------------------===//
352 
354  if (handlerID)
355  ctx->getDiagEngine().eraseHandler(handlerID);
356 }
357 
358 //===----------------------------------------------------------------------===//
359 // SourceMgrDiagnosticHandler
360 //===----------------------------------------------------------------------===//
361 namespace mlir {
362 namespace detail {
364  /// Return the SrcManager buffer id for the specified file, or zero if none
365  /// can be found.
366  unsigned getSourceMgrBufferIDForFile(llvm::SourceMgr &mgr,
367  StringRef filename) {
368  // Check for an existing mapping to the buffer id for this file.
369  auto bufferIt = filenameToBufId.find(filename);
370  if (bufferIt != filenameToBufId.end())
371  return bufferIt->second;
372 
373  // Look for a buffer in the manager that has this filename.
374  for (unsigned i = 1, e = mgr.getNumBuffers() + 1; i != e; ++i) {
375  auto *buf = mgr.getMemoryBuffer(i);
376  if (buf->getBufferIdentifier() == filename)
377  return filenameToBufId[filename] = i;
378  }
379 
380  // Otherwise, try to load the source file.
381  auto bufferOrErr = llvm::MemoryBuffer::getFile(filename);
382  if (!bufferOrErr)
383  return 0;
384  unsigned id = mgr.AddNewSourceBuffer(std::move(*bufferOrErr), SMLoc());
385  filenameToBufId[filename] = id;
386  return id;
387  }
388 
389  /// Mapping between file name and buffer ID's.
390  llvm::StringMap<unsigned> filenameToBufId;
391 };
392 } // namespace detail
393 } // namespace mlir
394 
395 /// Return a processable CallSiteLoc from the given location.
396 static std::optional<CallSiteLoc> getCallSiteLoc(Location loc) {
397  if (isa<NameLoc>(loc))
398  return getCallSiteLoc(cast<NameLoc>(loc).getChildLoc());
399  if (auto callLoc = dyn_cast<CallSiteLoc>(loc))
400  return callLoc;
401  if (isa<FusedLoc>(loc)) {
402  for (auto subLoc : cast<FusedLoc>(loc).getLocations()) {
403  if (auto callLoc = getCallSiteLoc(subLoc)) {
404  return callLoc;
405  }
406  }
407  return std::nullopt;
408  }
409  return std::nullopt;
410 }
411 
412 /// Given a diagnostic kind, returns the LLVM DiagKind.
413 static llvm::SourceMgr::DiagKind getDiagKind(DiagnosticSeverity kind) {
414  switch (kind) {
416  return llvm::SourceMgr::DK_Note;
418  return llvm::SourceMgr::DK_Warning;
420  return llvm::SourceMgr::DK_Error;
422  return llvm::SourceMgr::DK_Remark;
423  }
424  llvm_unreachable("Unknown DiagnosticSeverity");
425 }
426 
428  llvm::SourceMgr &mgr, MLIRContext *ctx, raw_ostream &os,
429  ShouldShowLocFn &&shouldShowLocFn)
430  : ScopedDiagnosticHandler(ctx), mgr(mgr), os(os),
431  shouldShowLocFn(std::move(shouldShowLocFn)),
433  setHandler([this](Diagnostic &diag) { emitDiagnostic(diag); });
434 }
435 
437  llvm::SourceMgr &mgr, MLIRContext *ctx, ShouldShowLocFn &&shouldShowLocFn)
438  : SourceMgrDiagnosticHandler(mgr, ctx, llvm::errs(),
439  std::move(shouldShowLocFn)) {}
440 
442 
445  bool displaySourceLine) {
446  // Extract a file location from this loc.
447  auto fileLoc = loc->findInstanceOf<FileLineColLoc>();
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 (!llvm::isa<UnknownLoc>(loc))
454  strOS << loc << ": ";
455  strOS << message;
456  return mgr.PrintMessage(os, SMLoc(), getDiagKind(kind), 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().getValue() << ":" << fileLoc.getLine() << ":"
473  << fileLoc.getColumn();
474  llvm::SMDiagnostic diag(locStr, getDiagKind(kind), message.str());
475  diag.print(nullptr, os);
476 }
477 
478 /// Emit the given diagnostic with the held source manager.
481  auto addLocToStack = [&](Location loc, StringRef locContext) {
482  if (std::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 
525  callStackLimit = limit;
526 }
527 
528 /// Get a memory buffer for the given file, or nullptr if one is not found.
529 const llvm::MemoryBuffer *
531  if (unsigned id = impl->getSourceMgrBufferIDForFile(mgr, filename))
532  return mgr.getMemoryBuffer(id);
533  return nullptr;
534 }
535 
536 std::optional<Location>
537 SourceMgrDiagnosticHandler::findLocToShow(Location loc) {
538  if (!shouldShowLocFn)
539  return loc;
540  if (!shouldShowLocFn(loc))
541  return std::nullopt;
542 
543  // Recurse into the child locations of some of location types.
545  .Case([&](CallSiteLoc callLoc) -> std::optional<Location> {
546  // We recurse into the callee of a call site, as the caller will be
547  // emitted in a different note on the main diagnostic.
548  return findLocToShow(callLoc.getCallee());
549  })
550  .Case([&](FileLineColLoc) -> std::optional<Location> { return loc; })
551  .Case([&](FusedLoc fusedLoc) -> std::optional<Location> {
552  // Fused location is unique in that we try to find a sub-location to
553  // show, rather than the top-level location itself.
554  for (Location childLoc : fusedLoc.getLocations())
555  if (std::optional<Location> showableLoc = findLocToShow(childLoc))
556  return showableLoc;
557  return std::nullopt;
558  })
559  .Case([&](NameLoc nameLoc) -> std::optional<Location> {
560  return findLocToShow(nameLoc.getChildLoc());
561  })
562  .Case([&](OpaqueLoc opaqueLoc) -> std::optional<Location> {
563  // OpaqueLoc always falls back to a different source location.
564  return findLocToShow(opaqueLoc.getFallbackLocation());
565  })
566  .Case([](UnknownLoc) -> std::optional<Location> {
567  // Prefer not to show unknown locations.
568  return std::nullopt;
569  });
570 }
571 
572 /// Get a memory buffer for the given file, or the main file of the source
573 /// manager if one doesn't exist. This always returns non-null.
574 SMLoc SourceMgrDiagnosticHandler::convertLocToSMLoc(FileLineColLoc loc) {
575  // The column and line may be zero to represent unknown column and/or unknown
576  /// line/column information.
577  if (loc.getLine() == 0 || loc.getColumn() == 0)
578  return SMLoc();
579 
580  unsigned bufferId = impl->getSourceMgrBufferIDForFile(mgr, loc.getFilename());
581  if (!bufferId)
582  return SMLoc();
583  return mgr.FindLocForLineAndColumn(bufferId, loc.getLine(), loc.getColumn());
584 }
585 
586 //===----------------------------------------------------------------------===//
587 // SourceMgrDiagnosticVerifierHandler
588 //===----------------------------------------------------------------------===//
589 
590 namespace mlir {
591 namespace detail {
592 /// This class represents an expected output diagnostic.
593 struct ExpectedDiag {
595  StringRef substring)
597 
598  /// Emit an error at the location referenced by this diagnostic.
599  LogicalResult emitError(raw_ostream &os, llvm::SourceMgr &mgr,
600  const Twine &msg) {
601  SMRange range(fileLoc, SMLoc::getFromPointer(fileLoc.getPointer() +
602  substring.size()));
603  mgr.PrintMessage(os, fileLoc, llvm::SourceMgr::DK_Error, msg, range);
604  return failure();
605  }
606 
607  /// Returns true if this diagnostic matches the given string.
608  bool match(StringRef str) const {
609  // If this isn't a regex diagnostic, we simply check if the string was
610  // contained.
611  if (substringRegex)
612  return substringRegex->match(str);
613  return str.contains(substring);
614  }
615 
616  /// Compute the regex matcher for this diagnostic, using the provided stream
617  /// and manager to emit diagnostics as necessary.
618  LogicalResult computeRegex(raw_ostream &os, llvm::SourceMgr &mgr) {
619  std::string regexStr;
620  llvm::raw_string_ostream regexOS(regexStr);
621  StringRef strToProcess = substring;
622  while (!strToProcess.empty()) {
623  // Find the next regex block.
624  size_t regexIt = strToProcess.find("{{");
625  if (regexIt == StringRef::npos) {
626  regexOS << llvm::Regex::escape(strToProcess);
627  break;
628  }
629  regexOS << llvm::Regex::escape(strToProcess.take_front(regexIt));
630  strToProcess = strToProcess.drop_front(regexIt + 2);
631 
632  // Find the end of the regex block.
633  size_t regexEndIt = strToProcess.find("}}");
634  if (regexEndIt == StringRef::npos)
635  return emitError(os, mgr, "found start of regex with no end '}}'");
636  StringRef regexStr = strToProcess.take_front(regexEndIt);
637 
638  // Validate that the regex is actually valid.
639  std::string regexError;
640  if (!llvm::Regex(regexStr).isValid(regexError))
641  return emitError(os, mgr, "invalid regex: " + regexError);
642 
643  regexOS << '(' << regexStr << ')';
644  strToProcess = strToProcess.drop_front(regexEndIt + 2);
645  }
646  substringRegex = llvm::Regex(regexStr);
647  return success();
648  }
649 
650  /// The severity of the diagnosic expected.
652  /// The line number the expected diagnostic should be on.
653  unsigned lineNo;
654  /// The location of the expected diagnostic within the input file.
655  SMLoc fileLoc;
656  /// A flag indicating if the expected diagnostic has been matched yet.
657  bool matched = false;
658  /// The substring that is expected to be within the diagnostic.
659  StringRef substring;
660  /// An optional regex matcher, if the expected diagnostic sub-string was a
661  /// regex string.
662  std::optional<llvm::Regex> substringRegex;
663 };
664 
668  : status(success()), level(level) {}
669 
670  /// Returns the expected diagnostics for the given source file.
671  std::optional<MutableArrayRef<ExpectedDiag>>
672  getExpectedDiags(StringRef bufName);
673 
674  /// Computes the expected diagnostics for the given source buffer.
676  computeExpectedDiags(raw_ostream &os, llvm::SourceMgr &mgr,
677  const llvm::MemoryBuffer *buf);
678 
680  return level;
681  }
682 
683  /// The current status of the verifier.
684  LogicalResult status;
685 
686  /// A list of expected diagnostics for each buffer of the source manager.
687  llvm::StringMap<SmallVector<ExpectedDiag, 2>> expectedDiagsPerFile;
688 
689  /// A list of expected diagnostics with unknown locations.
691 
692  /// Regex to match the expected diagnostics format.
693  llvm::Regex expected =
694  llvm::Regex("expected-(error|note|remark|warning)(-re)? "
695  "*(@([+-][0-9]+|above|below|unknown))? *{{(.*)}}$");
696 
697  /// Verification level.
700 };
701 } // namespace detail
702 } // namespace mlir
703 
704 /// Given a diagnostic kind, return a human readable string for it.
706  switch (kind) {
708  return "note";
710  return "warning";
712  return "error";
714  return "remark";
715  }
716  llvm_unreachable("Unknown DiagnosticSeverity");
717 }
718 
719 std::optional<MutableArrayRef<ExpectedDiag>>
720 SourceMgrDiagnosticVerifierHandlerImpl::getExpectedDiags(StringRef bufName) {
721  auto expectedDiags = expectedDiagsPerFile.find(bufName);
722  if (expectedDiags != expectedDiagsPerFile.end())
723  return MutableArrayRef<ExpectedDiag>(expectedDiags->second);
724  return std::nullopt;
725 }
726 
728 SourceMgrDiagnosticVerifierHandlerImpl::computeExpectedDiags(
729  raw_ostream &os, llvm::SourceMgr &mgr, const llvm::MemoryBuffer *buf) {
730  // If the buffer is invalid, return an empty list.
731  if (!buf)
732  return {};
733  auto &expectedDiags = expectedDiagsPerFile[buf->getBufferIdentifier()];
734 
735  // The number of the last line that did not correlate to a designator.
736  unsigned lastNonDesignatorLine = 0;
737 
738  // The indices of designators that apply to the next non designator line.
739  SmallVector<unsigned, 1> designatorsForNextLine;
740 
741  // Scan the file for expected-* designators.
743  buf->getBuffer().split(lines, '\n');
744  for (unsigned lineNo = 0, e = lines.size(); lineNo < e; ++lineNo) {
746  if (!expected.match(lines[lineNo].rtrim(), &matches)) {
747  // Check for designators that apply to this line.
748  if (!designatorsForNextLine.empty()) {
749  for (unsigned diagIndex : designatorsForNextLine)
750  expectedDiags[diagIndex].lineNo = lineNo + 1;
751  designatorsForNextLine.clear();
752  }
753  lastNonDesignatorLine = lineNo;
754  continue;
755  }
756 
757  // Point to the start of expected-*.
758  SMLoc expectedStart = SMLoc::getFromPointer(matches[0].data());
759 
761  if (matches[1] == "error")
763  else if (matches[1] == "warning")
765  else if (matches[1] == "remark")
767  else {
768  assert(matches[1] == "note");
770  }
771  ExpectedDiag record(kind, lineNo + 1, expectedStart, matches[5]);
772 
773  // Check to see if this is a regex match, i.e. it includes the `-re`.
774  if (!matches[2].empty() && failed(record.computeRegex(os, mgr))) {
775  status = failure();
776  continue;
777  }
778 
779  StringRef offsetMatch = matches[3];
780  if (!offsetMatch.empty()) {
781  offsetMatch = offsetMatch.drop_front(1);
782 
783  // Get the integer value without the @ and +/- prefix.
784  if (offsetMatch[0] == '+' || offsetMatch[0] == '-') {
785  int offset;
786  offsetMatch.drop_front().getAsInteger(0, offset);
787 
788  if (offsetMatch.front() == '+')
789  record.lineNo += offset;
790  else
791  record.lineNo -= offset;
792  } else if (offsetMatch.consume_front("unknown")) {
793  // This is matching unknown locations.
794  record.fileLoc = SMLoc();
795  expectedUnknownLocDiags.emplace_back(std::move(record));
796  continue;
797  } else if (offsetMatch.consume_front("above")) {
798  // If the designator applies 'above' we add it to the last non
799  // designator line.
800  record.lineNo = lastNonDesignatorLine + 1;
801  } else {
802  // Otherwise, this is a 'below' designator and applies to the next
803  // non-designator line.
804  assert(offsetMatch.consume_front("below"));
805  designatorsForNextLine.push_back(expectedDiags.size());
806 
807  // Set the line number to the last in the case that this designator ends
808  // up dangling.
809  record.lineNo = e;
810  }
811  }
812  expectedDiags.emplace_back(std::move(record));
813  }
814  return expectedDiags;
815 }
816 
818  llvm::SourceMgr &srcMgr, MLIRContext *ctx, raw_ostream &out, Level level)
819  : SourceMgrDiagnosticHandler(srcMgr, ctx, out),
821  // Compute the expected diagnostics for each of the current files in the
822  // source manager.
823  for (unsigned i = 0, e = mgr.getNumBuffers(); i != e; ++i)
824  (void)impl->computeExpectedDiags(out, mgr, mgr.getMemoryBuffer(i + 1));
825 
826  registerInContext(ctx);
827 }
828 
830  llvm::SourceMgr &srcMgr, MLIRContext *ctx, Level level)
831  : SourceMgrDiagnosticVerifierHandler(srcMgr, ctx, llvm::errs(), level) {}
832 
834  // Ensure that all expected diagnostics were handled.
835  (void)verify();
836 }
837 
838 /// Returns the status of the verifier and verifies that all expected
839 /// diagnostics were emitted. This return success if all diagnostics were
840 /// verified correctly, failure otherwise.
842  // Verify that all expected errors were seen.
843  auto checkExpectedDiags = [&](ExpectedDiag &err) {
844  if (!err.matched)
845  impl->status =
846  err.emitError(os, mgr,
847  "expected " + getDiagKindStr(err.kind) + " \"" +
848  err.substring + "\" was not produced");
849  };
850  for (auto &expectedDiagsPair : impl->expectedDiagsPerFile)
851  for (auto &err : expectedDiagsPair.second)
852  checkExpectedDiags(err);
853  for (auto &err : impl->expectedUnknownLocDiags)
854  checkExpectedDiags(err);
855  impl->expectedDiagsPerFile.clear();
856  return impl->status;
857 }
858 
861  // Process the main diagnostics.
862  process(diag);
863 
864  // Process each of the notes.
865  for (auto &note : diag.getNotes())
866  process(note);
867  });
868 }
869 
870 /// Process a single diagnostic.
871 void SourceMgrDiagnosticVerifierHandler::process(Diagnostic &diag) {
872  return process(diag.getLocation(), diag.str(), diag.getSeverity());
873 }
874 
875 /// Process a diagnostic at a certain location.
876 void SourceMgrDiagnosticVerifierHandler::process(LocationAttr loc,
877  StringRef msg,
881 
882  if (fileLoc) {
883  // Get the expected diagnostics for this file.
884  if (auto maybeDiags = impl->getExpectedDiags(fileLoc.getFilename())) {
885  diags = *maybeDiags;
886  } else {
887  diags = impl->computeExpectedDiags(
888  os, mgr, getBufferForFile(fileLoc.getFilename()));
889  }
890  } else {
891  // Get all expected diagnostics at unknown locations.
892  diags = impl->expectedUnknownLocDiags;
893  }
894 
895  // Search for a matching expected diagnostic.
896  // If we find something that is close then emit a more specific error.
897  ExpectedDiag *nearMiss = nullptr;
898 
899  // If this was an expected error, remember that we saw it and return.
900  for (auto &e : diags) {
901  // File line must match (unless it's an unknown location).
902  if (fileLoc && fileLoc.getLine() != e.lineNo)
903  continue;
904  if (e.match(msg)) {
905  if (e.kind == kind) {
906  e.matched = true;
907  return;
908  }
909 
910  // If this only differs based on the diagnostic kind, then consider it
911  // to be a near miss.
912  nearMiss = &e;
913  }
914  }
915 
916  if (impl->getVerifyLevel() == Level::OnlyExpected)
917  return;
918 
919  // Otherwise, emit an error for the near miss.
920  if (nearMiss)
921  mgr.PrintMessage(os, nearMiss->fileLoc, llvm::SourceMgr::DK_Error,
922  "'" + getDiagKindStr(kind) +
923  "' diagnostic emitted when expecting a '" +
924  getDiagKindStr(nearMiss->kind) + "'");
925  else
926  emitDiagnostic(loc, "unexpected " + getDiagKindStr(kind) + ": " + msg,
928  impl->status = failure();
929 }
930 
931 //===----------------------------------------------------------------------===//
932 // ParallelDiagnosticHandler
933 //===----------------------------------------------------------------------===//
934 
935 namespace mlir {
936 namespace detail {
937 struct ParallelDiagnosticHandlerImpl : public llvm::PrettyStackTraceEntry {
940  : id(id), diag(std::move(diag)) {}
941  bool operator<(const ThreadDiagnostic &rhs) const { return id < rhs.id; }
942 
943  /// The id for this diagnostic, this is used for ordering.
944  /// Note: This id corresponds to the ordered position of the current element
945  /// being processed by a given thread.
946  size_t id;
947 
948  /// The diagnostic.
950  };
951 
954  uint64_t tid = llvm::get_threadid();
955  llvm::sys::SmartScopedLock<true> lock(mutex);
956 
957  // If this thread is not tracked, then return failure to let another
958  // handler process this diagnostic.
959  if (!threadToOrderID.count(tid))
960  return failure();
961 
962  // Append a new diagnostic.
963  diagnostics.emplace_back(threadToOrderID[tid], std::move(diag));
964  return success();
965  });
966  }
967 
969  // Erase this handler from the context.
971 
972  // Early exit if there are no diagnostics, this is the common case.
973  if (diagnostics.empty())
974  return;
975 
976  // Emit the diagnostics back to the context.
978  return context->getDiagEngine().emit(std::move(diag));
979  });
980  }
981 
982  /// Utility method to emit any held diagnostics.
983  void emitDiagnostics(llvm::function_ref<void(Diagnostic &)> emitFn) const {
984  // Stable sort all of the diagnostics that were emitted. This creates a
985  // deterministic ordering for the diagnostics based upon which order id they
986  // were emitted for.
987  llvm::stable_sort(diagnostics);
988 
989  // Emit each diagnostic to the context again.
991  emitFn(diag.diag);
992  }
993 
994  /// Set the order id for the current thread.
995  void setOrderIDForThread(size_t orderID) {
996  uint64_t tid = llvm::get_threadid();
997  llvm::sys::SmartScopedLock<true> lock(mutex);
998  threadToOrderID[tid] = orderID;
999  }
1000 
1001  /// Remove the order id for the current thread.
1003  uint64_t tid = llvm::get_threadid();
1004  llvm::sys::SmartScopedLock<true> lock(mutex);
1005  threadToOrderID.erase(tid);
1006  }
1007 
1008  /// Dump the current diagnostics that were inflight.
1009  void print(raw_ostream &os) const override {
1010  // Early exit if there are no diagnostics, this is the common case.
1011  if (diagnostics.empty())
1012  return;
1013 
1014  os << "In-Flight Diagnostics:\n";
1015  emitDiagnostics([&](const Diagnostic &diag) {
1016  os.indent(4);
1017 
1018  // Print each diagnostic with the format:
1019  // "<location>: <kind>: <msg>"
1020  if (!llvm::isa<UnknownLoc>(diag.getLocation()))
1021  os << diag.getLocation() << ": ";
1022  switch (diag.getSeverity()) {
1023  case DiagnosticSeverity::Error:
1024  os << "error: ";
1025  break;
1026  case DiagnosticSeverity::Warning:
1027  os << "warning: ";
1028  break;
1029  case DiagnosticSeverity::Note:
1030  os << "note: ";
1031  break;
1032  case DiagnosticSeverity::Remark:
1033  os << "remark: ";
1034  break;
1035  }
1036  os << diag << '\n';
1037  });
1038  }
1039 
1040  /// A smart mutex to lock access to the internal state.
1042 
1043  /// A mapping between the thread id and the current order id.
1045 
1046  /// An unordered list of diagnostics that were emitted.
1047  mutable std::vector<ThreadDiagnostic> diagnostics;
1048 
1049  /// The unique id for the parallel handler.
1051 
1052  /// The context to emit the diagnostics to.
1054 };
1055 } // namespace detail
1056 } // namespace mlir
1057 
1059  : impl(new ParallelDiagnosticHandlerImpl(ctx)) {}
1061 
1062 /// Set the order id for the current thread.
1064  impl->setOrderIDForThread(orderID);
1065 }
1066 
1067 /// Remove the order id for the current thread. This removes the thread from
1068 /// diagnostics tracking.
1070  impl->eraseOrderIDForThread();
1071 }
static OpPrintingFlags adjustPrintingFlags(OpPrintingFlags flags, DiagnosticSeverity severity)
Adjusts operation printing flags used in diagnostics for the given severity level.
static InFlightDiagnostic emitDiag(Location location, DiagnosticSeverity severity, const Twine &message)
Helper function used to emit a diagnostic with an optionally empty twine message.
static StringRef getDiagKindStr(DiagnosticSeverity kind)
Given a diagnostic kind, return a human readable string for it.
static StringRef twineToStrRef(const Twine &val, std::vector< std::unique_ptr< char[]>> &strings)
Convert a Twine to a StringRef.
Definition: Diagnostics.cpp:87
static std::optional< CallSiteLoc > getCallSiteLoc(Location loc)
Return a processable CallSiteLoc from the given location.
static llvm::SourceMgr::DiagKind getDiagKind(DiagnosticSeverity kind)
Given a diagnostic kind, returns the LLVM DiagKind.
union mlir::linalg::@1254::ArityGroupAndKind::Kind kind
static std::string diag(const llvm::Value &value)
static LogicalResult emit(SolverOp solver, const SMTEmissionOptions &options, mlir::raw_indented_ostream &stream)
Emit the SMT operations in the given 'solver' to the 'stream'.
Attributes are known-constant values of operations.
Definition: Attributes.h:25
MLIRContext * getContext() const
Return the context this attribute belongs to.
Definition: Attributes.cpp:37
static Attribute getFromOpaquePointer(const void *ptr)
Construct an attribute from the opaque pointer representation.
Definition: Attributes.h:75
A variant type that holds a single argument for a diagnostic.
Definition: Diagnostics.h:52
DiagnosticArgumentKind
Enum that represents the different kinds of diagnostic arguments supported.
Definition: Diagnostics.h:84
StringRef getAsString() const
Returns this argument as a string.
Definition: Diagnostics.h:115
double getAsDouble() const
Returns this argument as a double.
Definition: Diagnostics.h:103
DiagnosticArgument(Attribute attr)
Note: The constructors below are only exposed due to problems accessing constructors from type traits...
Definition: Diagnostics.cpp:35
Type getAsType() const
Returns this argument as a Type.
Definition: Diagnostics.cpp:52
int64_t getAsInteger() const
Returns this argument as a signed integer.
Definition: Diagnostics.h:109
DiagnosticArgumentKind getKind() const
Returns the kind of this argument.
Definition: Diagnostics.h:97
Attribute getAsAttribute() const
Returns this argument as an Attribute.
Definition: Diagnostics.cpp:45
void print(raw_ostream &os) const
Outputs this argument to a stream.
Definition: Diagnostics.cpp:58
uint64_t getAsUnsigned() const
Returns this argument as an unsigned integer.
Definition: Diagnostics.h:124
uint64_t HandlerID
A handle to a specific registered handler object.
Definition: Diagnostics.h:434
InFlightDiagnostic emit(Location loc, DiagnosticSeverity severity)
Create a new inflight diagnostic with the given location and severity.
Definition: Diagnostics.h:460
void eraseHandler(HandlerID id)
Erase the registered diagnostic handler with the given identifier.
llvm::unique_function< LogicalResult(Diagnostic &)> HandlerTy
The handler type for MLIR diagnostics.
Definition: Diagnostics.h:431
HandlerID registerHandler(HandlerTy handler)
Register a new handler for diagnostics to the engine.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
Definition: Diagnostics.h:155
std::string str() const
Converts the diagnostic to a string.
Diagnostic & attachNote(std::optional< Location > noteLoc=std::nullopt)
Attaches a note to this diagnostic.
MutableArrayRef< DiagnosticArgument > getArguments()
Returns the current list of diagnostic arguments.
Definition: Diagnostics.h:171
std::enable_if_t<!std::is_convertible< Arg, StringRef >::value &&std::is_constructible< DiagnosticArgument, Arg >::value, Diagnostic & > operator<<(Arg &&val)
Stream operator for inserting new diagnostic arguments.
Definition: Diagnostics.h:179
void print(raw_ostream &os) const
Outputs this diagnostic to a stream.
Diagnostic & appendOp(Operation &op, const OpPrintingFlags &flags)
Append an operation with the given printing flags.
An instance of this location represents a tuple of file, line number, and column number.
Definition: Location.h:174
unsigned getLine() const
Definition: Location.cpp:173
StringAttr getFilename() const
Definition: Location.cpp:169
unsigned getColumn() const
Definition: Location.cpp:175
This class represents a diagnostic that is inflight and set to be reported.
Definition: Diagnostics.h:314
void report()
Reports the diagnostic to the engine.
void abandon()
Abandons this diagnostic so that it will no longer be reported.
Location objects represent source locations information in MLIR.
Definition: Location.h:32
T findInstanceOf()
Return an instance of the given location type if one is nested under the current location.
Definition: Location.h:45
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:63
bool shouldPrintStackTraceOnDiagnostic()
Return true if we should attach the current stacktrace to diagnostics when emitted.
DiagnosticEngine & getDiagEngine()
Returns the diagnostic engine for this context.
Set of flags used to control the behavior of the various IR print methods (e.g.
OpPrintingFlags & elideLargeElementsAttrs(int64_t largeElementLimit=16)
Enables the elision of large elements attributes by printing a lexically valid but otherwise meaningl...
Definition: AsmPrinter.cpp:249
OpPrintingFlags & printGenericOpForm(bool enable=true)
Always print operations in the generic form.
Definition: AsmPrinter.cpp:276
OpPrintingFlags & useLocalScope(bool enable=true)
Use local scope when printing the operation.
Definition: AsmPrinter.cpp:296
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
void print(raw_ostream &os, const OpPrintingFlags &flags={})
void eraseOrderIDForThread()
Remove the order id for the current thread.
ParallelDiagnosticHandler(MLIRContext *ctx)
void setOrderIDForThread(size_t orderID)
Set the order id for the current thread.
This diagnostic handler is a simple RAII class that registers and erases a diagnostic handler on a gi...
Definition: Diagnostics.h:522
void setHandler(FuncTy &&handler)
Set the handler to manage via RAII.
Definition: Diagnostics.h:535
This class is a utility diagnostic handler for use with llvm::SourceMgr.
Definition: Diagnostics.h:559
void setCallStackLimit(unsigned limit)
Set the maximum depth that a call stack will be printed. Defaults to 10.
void emitDiagnostic(Location loc, Twine message, DiagnosticSeverity kind, bool displaySourceLine=true)
Emit the given diagnostic information with the held source manager.
raw_ostream & os
The output stream to use when printing diagnostics.
Definition: Diagnostics.h:596
SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr, MLIRContext *ctx, raw_ostream &os, ShouldShowLocFn &&shouldShowLocFn={})
ShouldShowLocFn shouldShowLocFn
A functor used when determining if a location for a diagnostic should be shown.
Definition: Diagnostics.h:600
const llvm::MemoryBuffer * getBufferForFile(StringRef filename)
Get a memory buffer for the given file, or nullptr if no file is available.
llvm::SourceMgr & mgr
The source manager that we are wrapping.
Definition: Diagnostics.h:593
llvm::unique_function< bool(Location)> ShouldShowLocFn
This type represents a functor used to filter out locations when printing a diagnostic.
Definition: Diagnostics.h:568
This class is a utility diagnostic handler for use with llvm::SourceMgr that verifies that emitted di...
Definition: Diagnostics.h:627
SourceMgrDiagnosticVerifierHandler(llvm::SourceMgr &srcMgr, MLIRContext *ctx, raw_ostream &out, Level level=Level::All)
void registerInContext(MLIRContext *ctx)
Register this handler with the given context.
LogicalResult verify()
Returns the status of the handler and verifies that all expected diagnostics were emitted.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
static Type getFromOpaquePointer(const void *pointer)
Definition: Types.h:167
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
void print(raw_ostream &os) const
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition: CallGraph.h:229
AttrTypeReplacer.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition: Remarks.h:561
Include the generated interface declarations.
InFlightDiagnostic emitWarning(Location loc)
Utility method to emit a warning message using this location.
DiagnosticSeverity
Defines the different supported severity of a diagnostic.
Definition: Diagnostics.h:40
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
InFlightDiagnostic emitRemark(Location loc)
Utility method to emit a remark message using this location.
DiagnosticEngine::HandlerID uniqueHandlerId
This is a unique identifier counter for diagnostic handlers in the context.
llvm::sys::SmartMutex< true > mutex
A mutex to ensure that diagnostics emission is thread-safe.
void emit(Diagnostic &&diag)
Emit a diagnostic using the registered issue handle if present, or with the default behavior if not.
llvm::SmallMapVector< DiagnosticEngine::HandlerID, DiagnosticEngine::HandlerTy, 2 > handlers
These are the handlers used to report diagnostics.
This class represents an expected output diagnostic.
LogicalResult emitError(raw_ostream &os, llvm::SourceMgr &mgr, const Twine &msg)
Emit an error at the location referenced by this diagnostic.
unsigned lineNo
The line number the expected diagnostic should be on.
LogicalResult computeRegex(raw_ostream &os, llvm::SourceMgr &mgr)
Compute the regex matcher for this diagnostic, using the provided stream and manager to emit diagnost...
bool match(StringRef str) const
Returns true if this diagnostic matches the given string.
bool matched
A flag indicating if the expected diagnostic has been matched yet.
DiagnosticSeverity kind
The severity of the diagnosic expected.
StringRef substring
The substring that is expected to be within the diagnostic.
std::optional< llvm::Regex > substringRegex
An optional regex matcher, if the expected diagnostic sub-string was a regex string.
SMLoc fileLoc
The location of the expected diagnostic within the input file.
ExpectedDiag(DiagnosticSeverity kind, unsigned lineNo, SMLoc fileLoc, StringRef substring)
size_t id
The id for this diagnostic, this is used for ordering.
bool operator<(const ThreadDiagnostic &rhs) const
void print(raw_ostream &os) const override
Dump the current diagnostics that were inflight.
MLIRContext * context
The context to emit the diagnostics to.
void emitDiagnostics(llvm::function_ref< void(Diagnostic &)> emitFn) const
Utility method to emit any held diagnostics.
DiagnosticEngine::HandlerID handlerID
The unique id for the parallel handler.
DenseMap< uint64_t, size_t > threadToOrderID
A mapping between the thread id and the current order id.
void setOrderIDForThread(size_t orderID)
Set the order id for the current thread.
std::vector< ThreadDiagnostic > diagnostics
An unordered list of diagnostics that were emitted.
void eraseOrderIDForThread()
Remove the order id for the current thread.
llvm::sys::SmartMutex< true > mutex
A smart mutex to lock access to the internal state.
llvm::StringMap< unsigned > filenameToBufId
Mapping between file name and buffer ID's.
unsigned getSourceMgrBufferIDForFile(llvm::SourceMgr &mgr, StringRef filename)
Return the SrcManager buffer id for the specified file, or zero if none can be found.
llvm::StringMap< SmallVector< ExpectedDiag, 2 > > expectedDiagsPerFile
A list of expected diagnostics for each buffer of the source manager.
LogicalResult status
The current status of the verifier.
MutableArrayRef< ExpectedDiag > computeExpectedDiags(raw_ostream &os, llvm::SourceMgr &mgr, const llvm::MemoryBuffer *buf)
Computes the expected diagnostics for the given source buffer.
SourceMgrDiagnosticVerifierHandler::Level getVerifyLevel() const
llvm::Regex expected
Regex to match the expected diagnostics format.
SourceMgrDiagnosticVerifierHandler::Level level
Verification level.
SmallVector< ExpectedDiag, 2 > expectedUnknownLocDiags
A list of expected diagnostics with unknown locations.
SourceMgrDiagnosticVerifierHandlerImpl(SourceMgrDiagnosticVerifierHandler::Level level)
std::optional< MutableArrayRef< ExpectedDiag > > getExpectedDiags(StringRef bufName)
Returns the expected diagnostics for the given source file.