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