github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/third_party/mlir/lib/IR/Diagnostics.cpp (about)

     1  //===- Diagnostics.cpp - MLIR Diagnostics ---------------------------------===//
     2  //
     3  // Copyright 2019 The MLIR Authors.
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //   http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  // =============================================================================
    17  
    18  #include "mlir/IR/Diagnostics.h"
    19  #include "mlir/IR/Attributes.h"
    20  #include "mlir/IR/Identifier.h"
    21  #include "mlir/IR/Location.h"
    22  #include "mlir/IR/MLIRContext.h"
    23  #include "mlir/IR/Operation.h"
    24  #include "mlir/IR/Types.h"
    25  #include "llvm/ADT/SmallString.h"
    26  #include "llvm/ADT/StringMap.h"
    27  #include "llvm/Support/Mutex.h"
    28  #include "llvm/Support/PrettyStackTrace.h"
    29  #include "llvm/Support/Regex.h"
    30  #include "llvm/Support/SourceMgr.h"
    31  #include "llvm/Support/raw_ostream.h"
    32  
    33  using namespace mlir;
    34  using namespace mlir::detail;
    35  
    36  //===----------------------------------------------------------------------===//
    37  // DiagnosticArgument
    38  //===----------------------------------------------------------------------===//
    39  
    40  // Construct from an Attribute.
    41  DiagnosticArgument::DiagnosticArgument(Attribute attr)
    42      : kind(DiagnosticArgumentKind::Attribute),
    43        opaqueVal(reinterpret_cast<intptr_t>(attr.getAsOpaquePointer())) {}
    44  
    45  // Construct from a Type.
    46  DiagnosticArgument::DiagnosticArgument(Type val)
    47      : kind(DiagnosticArgumentKind::Type),
    48        opaqueVal(reinterpret_cast<intptr_t>(val.getAsOpaquePointer())) {}
    49  
    50  /// Returns this argument as an Attribute.
    51  Attribute DiagnosticArgument::getAsAttribute() const {
    52    assert(getKind() == DiagnosticArgumentKind::Attribute);
    53    return Attribute::getFromOpaquePointer(
    54        reinterpret_cast<const void *>(opaqueVal));
    55  }
    56  
    57  /// Returns this argument as a Type.
    58  Type DiagnosticArgument::getAsType() const {
    59    assert(getKind() == DiagnosticArgumentKind::Type);
    60    return Type::getFromOpaquePointer(reinterpret_cast<const void *>(opaqueVal));
    61  }
    62  
    63  /// Outputs this argument to a stream.
    64  void DiagnosticArgument::print(raw_ostream &os) const {
    65    switch (kind) {
    66    case DiagnosticArgumentKind::Attribute:
    67      os << getAsAttribute();
    68      break;
    69    case DiagnosticArgumentKind::Double:
    70      os << getAsDouble();
    71      break;
    72    case DiagnosticArgumentKind::Integer:
    73      os << getAsInteger();
    74      break;
    75    case DiagnosticArgumentKind::Operation:
    76      os << getAsOperation();
    77      break;
    78    case DiagnosticArgumentKind::String:
    79      os << getAsString();
    80      break;
    81    case DiagnosticArgumentKind::Type:
    82      os << '\'' << getAsType() << '\'';
    83      break;
    84    case DiagnosticArgumentKind::Unsigned:
    85      os << getAsUnsigned();
    86      break;
    87    }
    88  }
    89  
    90  //===----------------------------------------------------------------------===//
    91  // Diagnostic
    92  //===----------------------------------------------------------------------===//
    93  
    94  /// Convert a Twine to a StringRef. Memory used for generating the StringRef is
    95  /// stored in 'strings'.
    96  static StringRef twineToStrRef(const Twine &val,
    97                                 std::vector<std::unique_ptr<char[]>> &strings) {
    98    // Allocate memory to hold this string.
    99    llvm::SmallString<64> data;
   100    auto strRef = val.toStringRef(data);
   101    strings.push_back(std::unique_ptr<char[]>(new char[strRef.size()]));
   102    memcpy(&strings.back()[0], strRef.data(), strRef.size());
   103  
   104    // Return a reference to the new string.
   105    return StringRef(&strings.back()[0], strRef.size());
   106  }
   107  
   108  /// Stream in a Twine argument.
   109  Diagnostic &Diagnostic::operator<<(char val) { return *this << Twine(val); }
   110  Diagnostic &Diagnostic::operator<<(const Twine &val) {
   111    arguments.push_back(DiagnosticArgument(twineToStrRef(val, strings)));
   112    return *this;
   113  }
   114  Diagnostic &Diagnostic::operator<<(Twine &&val) {
   115    arguments.push_back(DiagnosticArgument(twineToStrRef(val, strings)));
   116    return *this;
   117  }
   118  
   119  /// Stream in an Identifier.
   120  Diagnostic &Diagnostic::operator<<(Identifier val) {
   121    // An identifier is stored in the context, so we don't need to worry about the
   122    // lifetime of its data.
   123    arguments.push_back(DiagnosticArgument(val.strref()));
   124    return *this;
   125  }
   126  
   127  /// Stream in an OperationName.
   128  Diagnostic &Diagnostic::operator<<(OperationName val) {
   129    // An OperationName is stored in the context, so we don't need to worry about
   130    // the lifetime of its data.
   131    arguments.push_back(DiagnosticArgument(val.getStringRef()));
   132    return *this;
   133  }
   134  
   135  /// Outputs this diagnostic to a stream.
   136  void Diagnostic::print(raw_ostream &os) const {
   137    for (auto &arg : getArguments())
   138      arg.print(os);
   139  }
   140  
   141  /// Convert the diagnostic to a string.
   142  std::string Diagnostic::str() const {
   143    std::string str;
   144    llvm::raw_string_ostream os(str);
   145    print(os);
   146    return os.str();
   147  }
   148  
   149  /// Attaches a note to this diagnostic. A new location may be optionally
   150  /// provided, if not, then the location defaults to the one specified for this
   151  /// diagnostic. Notes may not be attached to other notes.
   152  Diagnostic &Diagnostic::attachNote(llvm::Optional<Location> noteLoc) {
   153    // We don't allow attaching notes to notes.
   154    assert(severity != DiagnosticSeverity::Note &&
   155           "cannot attach a note to a note");
   156  
   157    // If a location wasn't provided then reuse our location.
   158    if (!noteLoc)
   159      noteLoc = loc;
   160  
   161    /// Append and return a new note.
   162    notes.push_back(
   163        std::make_unique<Diagnostic>(*noteLoc, DiagnosticSeverity::Note));
   164    return *notes.back();
   165  }
   166  
   167  /// Allow a diagnostic to be converted to 'failure'.
   168  Diagnostic::operator LogicalResult() const { return failure(); }
   169  
   170  //===----------------------------------------------------------------------===//
   171  // InFlightDiagnostic
   172  //===----------------------------------------------------------------------===//
   173  
   174  /// Allow an inflight diagnostic to be converted to 'failure', otherwise
   175  /// 'success' if this is an empty diagnostic.
   176  InFlightDiagnostic::operator LogicalResult() const {
   177    return failure(isActive());
   178  }
   179  
   180  /// Reports the diagnostic to the engine.
   181  void InFlightDiagnostic::report() {
   182    // If this diagnostic is still inflight and it hasn't been abandoned, then
   183    // report it.
   184    if (isInFlight()) {
   185      owner->emit(std::move(*impl));
   186      owner = nullptr;
   187    }
   188    impl.reset();
   189  }
   190  
   191  /// Abandons this diagnostic.
   192  void InFlightDiagnostic::abandon() { owner = nullptr; }
   193  
   194  //===----------------------------------------------------------------------===//
   195  // DiagnosticEngineImpl
   196  //===----------------------------------------------------------------------===//
   197  
   198  namespace mlir {
   199  namespace detail {
   200  struct DiagnosticEngineImpl {
   201    /// Emit a diagnostic using the registered issue handle if present, or with
   202    /// the default behavior if not.
   203    void emit(Diagnostic diag);
   204  
   205    /// A mutex to ensure that diagnostics emission is thread-safe.
   206    llvm::sys::SmartMutex<true> mutex;
   207  
   208    /// This is the handler to use to report diagnostics, or null if not
   209    /// registered.
   210    DiagnosticEngine::HandlerTy handler;
   211  };
   212  } // namespace detail
   213  } // namespace mlir
   214  
   215  /// Emit a diagnostic using the registered issue handle if present, or with
   216  /// the default behavior if not.
   217  void DiagnosticEngineImpl::emit(Diagnostic diag) {
   218    llvm::sys::SmartScopedLock<true> lock(mutex);
   219  
   220    // If we had a handler registered, emit the diagnostic using it.
   221    if (handler)
   222      return handler(std::move(diag));
   223  
   224    // Otherwise, if this is an error we emit it to stderr.
   225    if (diag.getSeverity() != DiagnosticSeverity::Error)
   226      return;
   227  
   228    auto &os = llvm::errs();
   229    if (!diag.getLocation().isa<UnknownLoc>())
   230      os << diag.getLocation() << ": ";
   231    os << "error: ";
   232  
   233    // The default behavior for errors is to emit them to stderr.
   234    os << diag << '\n';
   235    os.flush();
   236  }
   237  
   238  //===----------------------------------------------------------------------===//
   239  // DiagnosticEngine
   240  //===----------------------------------------------------------------------===//
   241  
   242  DiagnosticEngine::DiagnosticEngine() : impl(new DiagnosticEngineImpl()) {}
   243  DiagnosticEngine::~DiagnosticEngine() {}
   244  
   245  /// Set the diagnostic handler for this engine.  The handler is passed
   246  /// location information if present (nullptr if not) along with a message and
   247  /// a severity that indicates whether this is an error, warning, etc. Note
   248  /// that this replaces any existing handler.
   249  void DiagnosticEngine::setHandler(const HandlerTy &handler) {
   250    impl->handler = handler;
   251  }
   252  
   253  /// Return the current diagnostic handler, or null if none is present.
   254  auto DiagnosticEngine::getHandler() -> HandlerTy {
   255    llvm::sys::SmartScopedLock<true> lock(impl->mutex);
   256    return impl->handler;
   257  }
   258  
   259  /// Emit a diagnostic using the registered issue handler if present, or with
   260  /// the default behavior if not.
   261  void DiagnosticEngine::emit(Diagnostic diag) {
   262    assert(diag.getSeverity() != DiagnosticSeverity::Note &&
   263           "notes should not be emitted directly");
   264    impl->emit(std::move(diag));
   265  }
   266  
   267  /// Helper function used to emit a diagnostic with an optionally empty twine
   268  /// message. If the message is empty, then it is not inserted into the
   269  /// diagnostic.
   270  static InFlightDiagnostic emitDiag(Location location,
   271                                     DiagnosticSeverity severity,
   272                                     const llvm::Twine &message) {
   273    auto &diagEngine = location->getContext()->getDiagEngine();
   274    auto diag = diagEngine.emit(location, severity);
   275    if (!message.isTriviallyEmpty())
   276      diag << message;
   277    return diag;
   278  }
   279  
   280  /// Emit an error message using this location.
   281  InFlightDiagnostic mlir::emitError(Location loc) { return emitError(loc, {}); }
   282  InFlightDiagnostic mlir::emitError(Location loc, const Twine &message) {
   283    return emitDiag(loc, DiagnosticSeverity::Error, message);
   284  }
   285  
   286  /// Emit a warning message using this location.
   287  InFlightDiagnostic mlir::emitWarning(Location loc) {
   288    return emitWarning(loc, {});
   289  }
   290  InFlightDiagnostic mlir::emitWarning(Location loc, const Twine &message) {
   291    return emitDiag(loc, DiagnosticSeverity::Warning, message);
   292  }
   293  
   294  /// Emit a remark message using this location.
   295  InFlightDiagnostic mlir::emitRemark(Location loc) {
   296    return emitRemark(loc, {});
   297  }
   298  InFlightDiagnostic mlir::emitRemark(Location loc, const Twine &message) {
   299    return emitDiag(loc, DiagnosticSeverity::Remark, message);
   300  }
   301  
   302  //===----------------------------------------------------------------------===//
   303  // ScopedDiagnosticHandler
   304  //===----------------------------------------------------------------------===//
   305  
   306  ScopedDiagnosticHandler::ScopedDiagnosticHandler(MLIRContext *ctx)
   307      : existingHandler(ctx->getDiagEngine().getHandler()), ctx(ctx) {}
   308  ScopedDiagnosticHandler::ScopedDiagnosticHandler(
   309      MLIRContext *ctx, const DiagnosticEngine::HandlerTy &handler)
   310      : ScopedDiagnosticHandler(ctx) {
   311    ctx->getDiagEngine().setHandler(handler);
   312  }
   313  ScopedDiagnosticHandler::~ScopedDiagnosticHandler() {
   314    ctx->getDiagEngine().setHandler(existingHandler);
   315  }
   316  
   317  //===----------------------------------------------------------------------===//
   318  // SourceMgrDiagnosticHandler
   319  //===----------------------------------------------------------------------===//
   320  namespace mlir {
   321  namespace detail {
   322  struct SourceMgrDiagnosticHandlerImpl {
   323    /// Get a memory buffer for the given file, or nullptr if one is not found.
   324    const llvm::MemoryBuffer *getBufferForFile(llvm::SourceMgr &mgr,
   325                                               StringRef filename) {
   326      // Check for an existing mapping to the buffer id for this file.
   327      auto bufferIt = filenameToBuf.find(filename);
   328      if (bufferIt != filenameToBuf.end())
   329        return bufferIt->second;
   330  
   331      // Look for a buffer in the manager that has this filename.
   332      for (unsigned i = 1, e = mgr.getNumBuffers() + 1; i != e; ++i) {
   333        auto *buf = mgr.getMemoryBuffer(i);
   334        if (buf->getBufferIdentifier() == filename)
   335          return filenameToBuf[filename] = buf;
   336      }
   337  
   338      // Otherwise, try to load the source file.
   339      const llvm::MemoryBuffer *newBuf = nullptr;
   340      std::string ignored;
   341      if (auto newBufID = mgr.AddIncludeFile(filename, llvm::SMLoc(), ignored))
   342        newBuf = mgr.getMemoryBuffer(newBufID);
   343      return filenameToBuf[filename] = newBuf;
   344    }
   345  
   346    /// Mapping between file name and buffer pointer.
   347    llvm::StringMap<const llvm::MemoryBuffer *> filenameToBuf;
   348  };
   349  } // end namespace detail
   350  } // end namespace mlir
   351  
   352  /// Return a processable FileLineColLoc from the given location.
   353  static llvm::Optional<FileLineColLoc> getFileLineColLoc(Location loc) {
   354    switch (loc->getKind()) {
   355    case StandardAttributes::NameLocation:
   356      return getFileLineColLoc(loc.cast<NameLoc>().getChildLoc());
   357    case StandardAttributes::FileLineColLocation:
   358      return loc.cast<FileLineColLoc>();
   359    case StandardAttributes::CallSiteLocation:
   360      // Process the callee of a callsite location.
   361      return getFileLineColLoc(loc.cast<CallSiteLoc>().getCallee());
   362    default:
   363      return llvm::None;
   364    }
   365  }
   366  
   367  /// Given a diagnostic kind, returns the LLVM DiagKind.
   368  static llvm::SourceMgr::DiagKind getDiagKind(DiagnosticSeverity kind) {
   369    switch (kind) {
   370    case DiagnosticSeverity::Note:
   371      return llvm::SourceMgr::DK_Note;
   372    case DiagnosticSeverity::Warning:
   373      return llvm::SourceMgr::DK_Warning;
   374    case DiagnosticSeverity::Error:
   375      return llvm::SourceMgr::DK_Error;
   376    case DiagnosticSeverity::Remark:
   377      return llvm::SourceMgr::DK_Remark;
   378    }
   379    llvm_unreachable("Unknown DiagnosticSeverity");
   380  }
   381  
   382  SourceMgrDiagnosticHandler::SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr,
   383                                                         MLIRContext *ctx,
   384                                                         llvm::raw_ostream &os)
   385      : ScopedDiagnosticHandler(ctx), mgr(mgr), os(os),
   386        impl(new SourceMgrDiagnosticHandlerImpl()) {
   387    // Register a simple diagnostic handler.
   388    ctx->getDiagEngine().setHandler(
   389        [this](Diagnostic diag) { emitDiagnostic(diag); });
   390  }
   391  
   392  SourceMgrDiagnosticHandler::SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr,
   393                                                         MLIRContext *ctx)
   394      : SourceMgrDiagnosticHandler(mgr, ctx, llvm::errs()) {}
   395  
   396  SourceMgrDiagnosticHandler::~SourceMgrDiagnosticHandler() {}
   397  
   398  void SourceMgrDiagnosticHandler::emitDiagnostic(Location loc, Twine message,
   399                                                  DiagnosticSeverity kind) {
   400    // Extract a file location from this loc.
   401    auto fileLoc = getFileLineColLoc(loc);
   402  
   403    // If one doesn't exist, then print the raw message without a source location.
   404    if (!fileLoc) {
   405      std::string str;
   406      llvm::raw_string_ostream strOS(str);
   407      if (!loc.isa<UnknownLoc>())
   408        strOS << loc << ": ";
   409      strOS << message;
   410      return mgr.PrintMessage(os, llvm::SMLoc(), getDiagKind(kind), strOS.str());
   411    }
   412  
   413    // Otherwise, try to convert the file location to an SMLoc.
   414    auto smloc = convertLocToSMLoc(*fileLoc);
   415    if (smloc.isValid())
   416      return mgr.PrintMessage(os, smloc, getDiagKind(kind), message);
   417  
   418    // If the conversion was unsuccessful, create a diagnostic with the file
   419    // information.
   420    llvm::SMDiagnostic diag(fileLoc->getFilename(), getDiagKind(kind),
   421                            message.str());
   422    diag.print(nullptr, os);
   423  }
   424  
   425  /// Emit the given diagnostic with the held source manager.
   426  void SourceMgrDiagnosticHandler::emitDiagnostic(Diagnostic &diag) {
   427    // Emit the diagnostic.
   428    auto loc = diag.getLocation();
   429    emitDiagnostic(loc, diag.str(), diag.getSeverity());
   430  
   431    // If the diagnostic location was a call site location, then print the call
   432    // stack as well.
   433    if (auto callLoc = loc.dyn_cast<CallSiteLoc>()) {
   434      // Print the call stack while valid, or until the limit is reached.
   435      Location callerLoc = callLoc.getCaller();
   436      for (unsigned curDepth = 0; curDepth < callStackLimit; ++curDepth) {
   437        emitDiagnostic(callerLoc, "called from", DiagnosticSeverity::Note);
   438        if ((callLoc = callerLoc.dyn_cast<CallSiteLoc>()))
   439          callerLoc = callLoc.getCaller();
   440        else
   441          break;
   442      }
   443    }
   444  
   445    // Emit each of the notes.
   446    for (auto &note : diag.getNotes())
   447      emitDiagnostic(note.getLocation(), note.str(), note.getSeverity());
   448  }
   449  
   450  /// Get a memory buffer for the given file, or nullptr if one is not found.
   451  const llvm::MemoryBuffer *
   452  SourceMgrDiagnosticHandler::getBufferForFile(StringRef filename) {
   453    return impl->getBufferForFile(mgr, filename);
   454  }
   455  
   456  /// Get a memory buffer for the given file, or the main file of the source
   457  /// manager if one doesn't exist. This always returns non-null.
   458  llvm::SMLoc SourceMgrDiagnosticHandler::convertLocToSMLoc(FileLineColLoc loc) {
   459    // Get the buffer for this filename.
   460    auto *membuf = getBufferForFile(loc.getFilename());
   461    if (!membuf)
   462      return llvm::SMLoc();
   463  
   464    // TODO: This should really be upstreamed to be a method on llvm::SourceMgr.
   465    // Doing so would allow it to use the offset cache that is already maintained
   466    // by SrcBuffer, making this more efficient.
   467    unsigned lineNo = loc.getLine();
   468    unsigned columnNo = loc.getColumn();
   469  
   470    // Scan for the correct line number.
   471    const char *position = membuf->getBufferStart();
   472    const char *end = membuf->getBufferEnd();
   473  
   474    // We start counting line and column numbers from 1.
   475    if (lineNo != 0)
   476      --lineNo;
   477    if (columnNo != 0)
   478      --columnNo;
   479  
   480    while (position < end && lineNo) {
   481      auto curChar = *position++;
   482  
   483      // Scan for newlines.  If this isn't one, ignore it.
   484      if (curChar != '\r' && curChar != '\n')
   485        continue;
   486  
   487      // We saw a line break, decrement our counter.
   488      --lineNo;
   489  
   490      // Check for \r\n and \n\r and treat it as a single escape.  We know that
   491      // looking past one character is safe because MemoryBuffer's are always nul
   492      // terminated.
   493      if (*position != curChar && (*position == '\r' || *position == '\n'))
   494        ++position;
   495    }
   496  
   497    // If the line/column counter was invalid, return a pointer to the start of
   498    // the buffer.
   499    if (lineNo || position + columnNo > end)
   500      return llvm::SMLoc::getFromPointer(membuf->getBufferStart());
   501  
   502    // If the column is zero, try to skip to the first non-whitespace character.
   503    if (columnNo == 0) {
   504      auto isNewline = [](char c) { return c == '\n' || c == '\r'; };
   505      auto isWhitespace = [](char c) { return c == ' ' || c == '\t'; };
   506  
   507      // Look for a valid non-whitespace character before the next line.
   508      for (auto *newPos = position; newPos < end && !isNewline(*newPos); ++newPos)
   509        if (!isWhitespace(*newPos))
   510          return llvm::SMLoc::getFromPointer(newPos);
   511    }
   512  
   513    // Otherwise return the right pointer.
   514    return llvm::SMLoc::getFromPointer(position + columnNo);
   515  }
   516  
   517  //===----------------------------------------------------------------------===//
   518  // SourceMgrDiagnosticVerifierHandler
   519  //===----------------------------------------------------------------------===//
   520  
   521  namespace mlir {
   522  namespace detail {
   523  // Record the expected diagnostic's position, substring and whether it was
   524  // seen.
   525  struct ExpectedDiag {
   526    DiagnosticSeverity kind;
   527    unsigned lineNo;
   528    StringRef substring;
   529    llvm::SMLoc fileLoc;
   530    bool matched;
   531  };
   532  
   533  struct SourceMgrDiagnosticVerifierHandlerImpl {
   534    SourceMgrDiagnosticVerifierHandlerImpl() : status(success()) {}
   535  
   536    /// Returns the expected diagnostics for the given source file.
   537    llvm::Optional<MutableArrayRef<ExpectedDiag>>
   538    getExpectedDiags(StringRef bufName);
   539  
   540    /// Computes the expected diagnostics for the given source buffer.
   541    MutableArrayRef<ExpectedDiag>
   542    computeExpectedDiags(const llvm::MemoryBuffer *buf);
   543  
   544    /// The current status of the verifier.
   545    LogicalResult status;
   546  
   547    /// A list of expected diagnostics for each buffer of the source manager.
   548    llvm::StringMap<SmallVector<ExpectedDiag, 2>> expectedDiagsPerFile;
   549  
   550    /// Regex to match the expected diagnostics format.
   551    llvm::Regex expected = llvm::Regex(
   552        "expected-(error|note|remark|warning) *(@[+-][0-9]+)? *{{(.*)}}");
   553  };
   554  } // end namespace detail
   555  } // end namespace mlir
   556  
   557  /// Given a diagnostic kind, return a human readable string for it.
   558  static StringRef getDiagKindStr(DiagnosticSeverity kind) {
   559    switch (kind) {
   560    case DiagnosticSeverity::Note:
   561      return "note";
   562    case DiagnosticSeverity::Warning:
   563      return "warning";
   564    case DiagnosticSeverity::Error:
   565      return "error";
   566    case DiagnosticSeverity::Remark:
   567      return "remark";
   568    }
   569    llvm_unreachable("Unknown DiagnosticSeverity");
   570  }
   571  
   572  /// Returns the expected diagnostics for the given source file.
   573  llvm::Optional<MutableArrayRef<ExpectedDiag>>
   574  SourceMgrDiagnosticVerifierHandlerImpl::getExpectedDiags(StringRef bufName) {
   575    auto expectedDiags = expectedDiagsPerFile.find(bufName);
   576    if (expectedDiags != expectedDiagsPerFile.end())
   577      return MutableArrayRef<ExpectedDiag>(expectedDiags->second);
   578    return llvm::None;
   579  }
   580  
   581  /// Computes the expected diagnostics for the given source buffer.
   582  MutableArrayRef<ExpectedDiag>
   583  SourceMgrDiagnosticVerifierHandlerImpl::computeExpectedDiags(
   584      const llvm::MemoryBuffer *buf) {
   585    // If the buffer is invalid, return an empty list.
   586    if (!buf)
   587      return llvm::None;
   588    auto &expectedDiags = expectedDiagsPerFile[buf->getBufferIdentifier()];
   589  
   590    // Scan the file for expected-* designators.
   591    SmallVector<StringRef, 100> lines;
   592    buf->getBuffer().split(lines, '\n');
   593    for (unsigned lineNo = 0, e = lines.size(); lineNo < e; ++lineNo) {
   594      SmallVector<StringRef, 3> matches;
   595      if (!expected.match(lines[lineNo], &matches))
   596        continue;
   597      // Point to the start of expected-*.
   598      auto expectedStart = llvm::SMLoc::getFromPointer(matches[0].data());
   599  
   600      DiagnosticSeverity kind;
   601      if (matches[1] == "error")
   602        kind = DiagnosticSeverity::Error;
   603      else if (matches[1] == "warning")
   604        kind = DiagnosticSeverity::Warning;
   605      else if (matches[1] == "remark")
   606        kind = DiagnosticSeverity::Remark;
   607      else {
   608        assert(matches[1] == "note");
   609        kind = DiagnosticSeverity::Note;
   610      }
   611  
   612      ExpectedDiag record{kind, lineNo + 1, matches[3], expectedStart, false};
   613      auto offsetMatch = matches[2];
   614      if (!offsetMatch.empty()) {
   615        int offset;
   616        // Get the integer value without the @ and +/- prefix.
   617        if (!offsetMatch.drop_front(2).getAsInteger(0, offset)) {
   618          if (offsetMatch[1] == '+')
   619            record.lineNo += offset;
   620          else
   621            record.lineNo -= offset;
   622        }
   623      }
   624      expectedDiags.push_back(record);
   625    }
   626    return expectedDiags;
   627  }
   628  
   629  SourceMgrDiagnosticVerifierHandler::SourceMgrDiagnosticVerifierHandler(
   630      llvm::SourceMgr &srcMgr, MLIRContext *ctx, llvm::raw_ostream &out)
   631      : SourceMgrDiagnosticHandler(srcMgr, ctx, out),
   632        impl(new SourceMgrDiagnosticVerifierHandlerImpl()) {
   633    // Compute the expected diagnostics for each of the current files in the
   634    // source manager.
   635    for (unsigned i = 0, e = mgr.getNumBuffers(); i != e; ++i)
   636      (void)impl->computeExpectedDiags(mgr.getMemoryBuffer(i + 1));
   637  
   638    // Register a handler to verfy the diagnostics.
   639    ctx->getDiagEngine().setHandler([&](Diagnostic diag) {
   640      // Process the main diagnostics.
   641      process(diag);
   642  
   643      // Process each of the notes.
   644      for (auto &note : diag.getNotes())
   645        process(note);
   646    });
   647  }
   648  
   649  SourceMgrDiagnosticVerifierHandler::SourceMgrDiagnosticVerifierHandler(
   650      llvm::SourceMgr &srcMgr, MLIRContext *ctx)
   651      : SourceMgrDiagnosticVerifierHandler(srcMgr, ctx, llvm::errs()) {}
   652  
   653  SourceMgrDiagnosticVerifierHandler::~SourceMgrDiagnosticVerifierHandler() {
   654    // Ensure that all expected diagnosics were handled.
   655    (void)verify();
   656  }
   657  
   658  /// Returns the status of the verifier and verifies that all expected
   659  /// diagnostics were emitted. This return success if all diagnostics were
   660  /// verified correctly, failure otherwise.
   661  LogicalResult SourceMgrDiagnosticVerifierHandler::verify() {
   662    // Verify that all expected errors were seen.
   663    for (auto &expectedDiagsPair : impl->expectedDiagsPerFile) {
   664      for (auto &err : expectedDiagsPair.second) {
   665        if (err.matched)
   666          continue;
   667        llvm::SMRange range(err.fileLoc,
   668                            llvm::SMLoc::getFromPointer(err.fileLoc.getPointer() +
   669                                                        err.substring.size()));
   670        mgr.PrintMessage(os, err.fileLoc, llvm::SourceMgr::DK_Error,
   671                         "expected " + getDiagKindStr(err.kind) + " \"" +
   672                             err.substring + "\" was not produced",
   673                         range);
   674        impl->status = failure();
   675      }
   676    }
   677    impl->expectedDiagsPerFile.clear();
   678    return impl->status;
   679  }
   680  
   681  /// Process a single diagnostic.
   682  void SourceMgrDiagnosticVerifierHandler::process(Diagnostic &diag) {
   683    auto kind = diag.getSeverity();
   684  
   685    // Process a FileLineColLoc.
   686    if (auto fileLoc = getFileLineColLoc(diag.getLocation()))
   687      return process(*fileLoc, diag.str(), kind);
   688  
   689    emitDiagnostic(diag.getLocation(),
   690                   "unexpected " + getDiagKindStr(kind) + ": " + diag.str(),
   691                   DiagnosticSeverity::Error);
   692    impl->status = failure();
   693  }
   694  
   695  /// Process a FileLineColLoc diagnostic.
   696  void SourceMgrDiagnosticVerifierHandler::process(FileLineColLoc loc,
   697                                                   StringRef msg,
   698                                                   DiagnosticSeverity kind) {
   699    // Get the expected diagnostics for this file.
   700    auto diags = impl->getExpectedDiags(loc.getFilename());
   701    if (!diags)
   702      diags = impl->computeExpectedDiags(getBufferForFile(loc.getFilename()));
   703  
   704    // Search for a matching expected diagnostic.
   705    // If we find something that is close then emit a more specific error.
   706    ExpectedDiag *nearMiss = nullptr;
   707  
   708    // If this was an expected error, remember that we saw it and return.
   709    unsigned line = loc.getLine();
   710    for (auto &e : *diags) {
   711      if (line == e.lineNo && msg.contains(e.substring)) {
   712        if (e.kind == kind) {
   713          e.matched = true;
   714          return;
   715        }
   716  
   717        // If this only differs based on the diagnostic kind, then consider it
   718        // to be a near miss.
   719        nearMiss = &e;
   720      }
   721    }
   722  
   723    // Otherwise, emit an error for the near miss.
   724    if (nearMiss)
   725      mgr.PrintMessage(os, nearMiss->fileLoc, llvm::SourceMgr::DK_Error,
   726                       "'" + getDiagKindStr(kind) +
   727                           "' diagnostic emitted when expecting a '" +
   728                           getDiagKindStr(nearMiss->kind) + "'");
   729    else
   730      emitDiagnostic(loc, "unexpected " + getDiagKindStr(kind) + ": " + msg,
   731                     DiagnosticSeverity::Error);
   732    impl->status = failure();
   733  }
   734  
   735  //===----------------------------------------------------------------------===//
   736  // ParallelDiagnosticHandler
   737  //===----------------------------------------------------------------------===//
   738  
   739  namespace mlir {
   740  namespace detail {
   741  struct ParallelDiagnosticHandlerImpl : public llvm::PrettyStackTraceEntry {
   742    struct ThreadDiagnostic {
   743      ThreadDiagnostic(size_t id, Diagnostic diag)
   744          : id(id), diag(std::move(diag)) {}
   745      bool operator<(const ThreadDiagnostic &rhs) const { return id < rhs.id; }
   746  
   747      /// The id for this diagnostic, this is used for ordering.
   748      /// Note: This id corresponds to the ordered position of the current element
   749      ///       being processed by a given thread.
   750      size_t id;
   751  
   752      /// The diagnostic.
   753      Diagnostic diag;
   754    };
   755  
   756    ParallelDiagnosticHandlerImpl(MLIRContext *ctx)
   757        : prevHandler(ctx->getDiagEngine().getHandler()), context(ctx) {
   758      ctx->getDiagEngine().setHandler([this](Diagnostic diag) {
   759        uint64_t tid = llvm::get_threadid();
   760        llvm::sys::SmartScopedLock<true> lock(mutex);
   761        assert(threadToOrderID.count(tid) &&
   762               "current thread does not have a valid orderID");
   763  
   764        // Append a new diagnostic.
   765        diagnostics.emplace_back(threadToOrderID[tid], std::move(diag));
   766      });
   767    }
   768  
   769    ~ParallelDiagnosticHandlerImpl() {
   770      // Restore the previous diagnostic handler.
   771      context->getDiagEngine().setHandler(prevHandler);
   772  
   773      // Early exit if there are no diagnostics, this is the common case.
   774      if (diagnostics.empty())
   775        return;
   776  
   777      // Emit the diagnostics back to the context.
   778      emitDiagnostics([&](Diagnostic diag) {
   779        return context->getDiagEngine().emit(std::move(diag));
   780      });
   781    }
   782  
   783    /// Utility method to emit any held diagnostics.
   784    void emitDiagnostics(std::function<void(Diagnostic)> emitFn) {
   785      // Stable sort all of the diagnostics that were emitted. This creates a
   786      // deterministic ordering for the diagnostics based upon which order id they
   787      // were emitted for.
   788      std::stable_sort(diagnostics.begin(), diagnostics.end());
   789  
   790      // Emit each diagnostic to the context again.
   791      for (ThreadDiagnostic &diag : diagnostics)
   792        emitFn(std::move(diag.diag));
   793    }
   794  
   795    /// Set the order id for the current thread.
   796    void setOrderIDForThread(size_t orderID) {
   797      uint64_t tid = llvm::get_threadid();
   798      llvm::sys::SmartScopedLock<true> lock(mutex);
   799      threadToOrderID[tid] = orderID;
   800    }
   801  
   802    /// Dump the current diagnostics that were inflight.
   803    void print(raw_ostream &os) const override {
   804      // Early exit if there are no diagnostics, this is the common case.
   805      if (diagnostics.empty())
   806        return;
   807  
   808      os << "In-Flight Diagnostics:\n";
   809      const_cast<ParallelDiagnosticHandlerImpl *>(this)->emitDiagnostics(
   810          [&](Diagnostic diag) {
   811            os.indent(4);
   812  
   813            // Print each diagnostic with the format:
   814            //   "<location>: <kind>: <msg>"
   815            if (!diag.getLocation().isa<UnknownLoc>())
   816              os << diag.getLocation() << ": ";
   817            switch (diag.getSeverity()) {
   818            case DiagnosticSeverity::Error:
   819              os << "error: ";
   820              break;
   821            case DiagnosticSeverity::Warning:
   822              os << "warning: ";
   823              break;
   824            case DiagnosticSeverity::Note:
   825              os << "note: ";
   826              break;
   827            case DiagnosticSeverity::Remark:
   828              os << "remark: ";
   829              break;
   830            }
   831            os << diag << '\n';
   832          });
   833    }
   834  
   835    /// The previous context diagnostic handler.
   836    DiagnosticEngine::HandlerTy prevHandler;
   837  
   838    /// A smart mutex to lock access to the internal state.
   839    llvm::sys::SmartMutex<true> mutex;
   840  
   841    /// A mapping between the thread id and the current order id.
   842    DenseMap<uint64_t, size_t> threadToOrderID;
   843  
   844    /// An unordered list of diagnostics that were emitted.
   845    std::vector<ThreadDiagnostic> diagnostics;
   846  
   847    /// The context to emit the diagnostics to.
   848    MLIRContext *context;
   849  };
   850  } // end namespace detail
   851  } // end namespace mlir
   852  
   853  ParallelDiagnosticHandler::ParallelDiagnosticHandler(MLIRContext *ctx)
   854      : impl(new ParallelDiagnosticHandlerImpl(ctx)) {}
   855  ParallelDiagnosticHandler::~ParallelDiagnosticHandler() {}
   856  
   857  /// Set the order id for the current thread.
   858  void ParallelDiagnosticHandler::setOrderIDForThread(size_t orderID) {
   859    impl->setOrderIDForThread(orderID);
   860  }