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