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