Continue revising diagnostic handling to simplify and generalize it, and improve related infra.
 - Add a new -verify mode to the mlir-opt tool that allows writing test cases
   for optimization and other passes that produce diagnostics.
 - Refactor existing the -check-parser-errors flag to mlir-opt into a new
   -split-input-file option which is orthogonal to -verify.
 - Eliminate the special error hook the parser maintained and use the standard
   MLIRContext's one instead.
 - Enhance the default MLIRContext error reporter to print file/line/col of
   errors when it is available.
 - Add new createChecked() methods to the builder that create ops and invoke
   the verify hook on them, use this to detected unhandled code in the
   RaiseControlFlow pass.
 - Teach mlir-opt about expected-error @+, it previously only worked with @-

PiperOrigin-RevId: 211305770
diff --git a/lib/Parser/Lexer.cpp b/lib/Parser/Lexer.cpp
index 043acd7..b4f8e1d 100644
--- a/lib/Parser/Lexer.cpp
+++ b/lib/Parser/Lexer.cpp
@@ -20,8 +20,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "Lexer.h"
+#include "mlir/IR/Location.h"
+#include "mlir/IR/MLIRContext.h"
 #include "llvm/Support/SourceMgr.h"
-#include <cctype>
 using namespace mlir;
 using llvm::SMLoc;
 using llvm::SourceMgr;
@@ -32,17 +33,30 @@
   return c == '$' || c == '.' || c == '_' || c == '-';
 }
 
-Lexer::Lexer(llvm::SourceMgr &sourceMgr, SMDiagnosticHandlerTy errorReporter)
-    : sourceMgr(sourceMgr), errorReporter(errorReporter) {
+Lexer::Lexer(llvm::SourceMgr &sourceMgr, MLIRContext *context)
+    : sourceMgr(sourceMgr), context(context) {
   auto bufferID = sourceMgr.getMainFileID();
   curBuffer = sourceMgr.getMemoryBuffer(bufferID)->getBuffer();
   curPtr = curBuffer.begin();
 }
 
+/// Encode the specified source location information into an attribute for
+/// attachment to the IR.
+Location *Lexer::getEncodedSourceLocation(llvm::SMLoc loc) {
+  auto &sourceMgr = getSourceMgr();
+  unsigned mainFileID = sourceMgr.getMainFileID();
+  auto lineAndColumn = sourceMgr.getLineAndColumn(loc, mainFileID);
+  auto *buffer = sourceMgr.getMemoryBuffer(mainFileID);
+  auto filename = UniquedFilename::get(buffer->getBufferIdentifier(), context);
+
+  return FileLineColLoc::get(filename, lineAndColumn.first,
+                             lineAndColumn.second, context);
+}
+
 /// emitError - Emit an error message and return an Token::error token.
 Token Lexer::emitError(const char *loc, const Twine &message) {
-  errorReporter(sourceMgr.GetMessage(SMLoc::getFromPointer(loc),
-                                     SourceMgr::DK_Error, message));
+  context->emitDiagnostic(getEncodedSourceLocation(SMLoc::getFromPointer(loc)),
+                          message, MLIRContext::DiagnosticKind::Error);
   return formToken(Token::error, loc);
 }
 
diff --git a/lib/Parser/Lexer.h b/lib/Parser/Lexer.h
index 51962fa..cbd4d0d 100644
--- a/lib/Parser/Lexer.h
+++ b/lib/Parser/Lexer.h
@@ -26,11 +26,12 @@
 #include "Token.h"
 
 namespace mlir {
+class Location;
 
 /// This class breaks up the current file into a token stream.
 class Lexer {
   llvm::SourceMgr &sourceMgr;
-  const SMDiagnosticHandlerTy errorReporter;
+  MLIRContext *context;
 
   StringRef curBuffer;
   const char *curPtr;
@@ -38,16 +39,20 @@
   Lexer(const Lexer&) = delete;
   void operator=(const Lexer&) = delete;
 public:
- explicit Lexer(llvm::SourceMgr &sourceMgr,
-                SMDiagnosticHandlerTy errorReporter);
+  explicit Lexer(llvm::SourceMgr &sourceMgr, MLIRContext *context);
 
- llvm::SourceMgr &getSourceMgr() { return sourceMgr; }
+  llvm::SourceMgr &getSourceMgr() { return sourceMgr; }
 
- Token lexToken();
+  Token lexToken();
 
- /// Change the position of the lexer cursor.  The next token we lex will start
- /// at the designated point in the input.
- void resetPointer(const char *newPointer) { curPtr = newPointer; }
+  /// Encode the specified source location information into a Location object
+  /// for attachment to the IR or error reporting.
+  Location *getEncodedSourceLocation(llvm::SMLoc loc);
+
+  /// Change the position of the lexer cursor.  The next token we lex will start
+  /// at the designated point in the input.
+  void resetPointer(const char *newPointer) { curPtr = newPointer; }
+
 private:
   // Helpers.
   Token formToken(Token::Kind kind, const char *tokStart) {
diff --git a/lib/Parser/Parser.cpp b/lib/Parser/Parser.cpp
index 0935090..1de8a52 100644
--- a/lib/Parser/Parser.cpp
+++ b/lib/Parser/Parser.cpp
@@ -45,14 +45,6 @@
 /// bool value.  Failure is "true" in a boolean context.
 enum ParseResult { ParseSuccess, ParseFailure };
 
-/// Return a uniqued filename for the main file the specified SourceMgr is
-/// looking at.
-static UniquedFilename getUniquedFilename(llvm::SourceMgr &sourceMgr,
-                                          MLIRContext *context) {
-  auto *buffer = sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID());
-  return UniquedFilename::get(buffer->getBufferIdentifier(), context);
-}
-
 namespace {
 class Parser;
 
@@ -61,13 +53,9 @@
 /// methods to access this.
 class ParserState {
 public:
-  ParserState(llvm::SourceMgr &sourceMgr, Module *module,
-              SMDiagnosticHandlerTy errorReporter)
-      : context(module->getContext()), module(module),
-        filename(getUniquedFilename(sourceMgr, context)),
-        lex(sourceMgr, errorReporter), curToken(lex.lexToken()),
-        errorReporter(errorReporter), operationSet(OperationSet::get(context)) {
-  }
+  ParserState(llvm::SourceMgr &sourceMgr, Module *module)
+      : context(module->getContext()), module(module), lex(sourceMgr, context),
+        curToken(lex.lexToken()), operationSet(OperationSet::get(context)) {}
 
   // A map from affine map identifier to AffineMap.
   llvm::StringMap<AffineMap *> affineMapDefinitions;
@@ -92,18 +80,12 @@
   // This is the module we are parsing into.
   Module *const module;
 
-  /// The filename to use for location generation.
-  UniquedFilename filename;
-
   // The lexer for the source file we're parsing.
   Lexer lex;
 
   // This is the next token that hasn't been consumed yet.
   Token curToken;
 
-  // The diagnostic error reporter.
-  SMDiagnosticHandlerTy const errorReporter;
-
   // The active OperationSet we're parsing with.
   OperationSet &operationSet;
 };
@@ -136,7 +118,9 @@
 
   /// Encode the specified source location information into an attribute for
   /// attachment to the IR.
-  Location *getEncodedSourceLocation(llvm::SMLoc loc);
+  Location *getEncodedSourceLocation(llvm::SMLoc loc) {
+    return state.lex.getEncodedSourceLocation(loc);
+  }
 
   /// Emit an error and return failure.
   ParseResult emitError(const Twine &message) {
@@ -221,25 +205,14 @@
 // Helper methods.
 //===----------------------------------------------------------------------===//
 
-/// Encode the specified source location information into an attribute for
-/// attachment to the IR.
-Location *Parser::getEncodedSourceLocation(llvm::SMLoc loc) {
-  auto &sourceMgr = getSourceMgr();
-  auto lineAndColumn =
-      sourceMgr.getLineAndColumn(loc, sourceMgr.getMainFileID());
-
-  return FileLineColLoc::get(state.filename, lineAndColumn.first,
-                             lineAndColumn.second, getContext());
-}
-
 ParseResult Parser::emitError(SMLoc loc, const Twine &message) {
   // If we hit a parse error in response to a lexer error, then the lexer
   // already reported the error.
   if (getToken().is(Token::error))
     return ParseFailure;
 
-  auto &sourceMgr = state.lex.getSourceMgr();
-  state.errorReporter(sourceMgr.GetMessage(loc, SourceMgr::DK_Error, message));
+  getContext()->emitDiagnostic(getEncodedSourceLocation(loc), message,
+                               MLIRContext::DiagnosticKind::Error);
   return ParseFailure;
 }
 
@@ -3026,78 +2999,34 @@
 
 //===----------------------------------------------------------------------===//
 
-void mlir::defaultErrorReporter(const llvm::SMDiagnostic &error) {
-  const auto &sourceMgr = *error.getSourceMgr();
-  sourceMgr.PrintMessage(error.getLoc(), error.getKind(), error.getMessage());
-}
-
 /// This parses the file specified by the indicated SourceMgr and returns an
 /// MLIR module if it was valid.  If not, it emits diagnostics and returns null.
-Module *mlir::parseSourceFile(llvm::SourceMgr &sourceMgr, MLIRContext *context,
-                              SMDiagnosticHandlerTy errorReporter) {
-  if (!errorReporter)
-    errorReporter = defaultErrorReporter;
-
-  // We are going to replace the context's handler and redirect it to use the
-  // error reporter.  Save the existing handler and reinstate it when we're
-  // done.
-  auto existingContextHandler = context->getDiagnosticHandler();
-
-  // Install a new handler that uses the error reporter.
-  context->registerDiagnosticHandler([&](Location *location, StringRef message,
-                                         MLIRContext::DiagnosticKind kind) {
-    SourceMgr::DiagKind diagKind;
-    switch (kind) {
-    case MLIRContext::DiagnosticKind::Error:
-      diagKind = SourceMgr::DK_Error;
-      break;
-    case MLIRContext::DiagnosticKind::Warning:
-      diagKind = SourceMgr::DK_Warning;
-      break;
-    case MLIRContext::DiagnosticKind::Note:
-      diagKind = SourceMgr::DK_Note;
-      break;
-    }
-
-    StringRef filename;
-    unsigned line = 0, column = 0;
-    if (auto fileLoc = dyn_cast<FileLineColLoc>(location)) {
-      filename = fileLoc->getFilename();
-      line = fileLoc->getLine();
-      column = fileLoc->getColumn();
-    }
-
-    auto diag = llvm::SMDiagnostic(sourceMgr, SMLoc(), filename, line, column,
-                                   diagKind, message, /*LineStr=*/StringRef(),
-                                   /*Ranges=*/{}, /*FixIts=*/{});
-
-    errorReporter(diag);
-  });
+Module *mlir::parseSourceFile(llvm::SourceMgr &sourceMgr,
+                              MLIRContext *context) {
 
   // This is the result module we are parsing into.
   std::unique_ptr<Module> module(new Module(context));
 
-  ParserState state(sourceMgr, module.get(), errorReporter);
+  ParserState state(sourceMgr, module.get());
   if (ModuleParser(state).parseModule()) {
-    context->registerDiagnosticHandler(existingContextHandler);
     return nullptr;
   }
 
   // Make sure the parse module has no other structural problems detected by the
   // verifier.
+  //
+  // TODO(clattner): The verifier should always emit diagnostics when we have
+  // more location information available.  We shouldn't need this hook.
   std::string errorResult;
   module->verify(&errorResult);
 
   // We don't have location information for general verifier errors, so emit the
-  // error on the first line.
+  // error with an unknown location.
   if (!errorResult.empty()) {
-    auto *mainBuffer = sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID());
-    errorReporter(sourceMgr.GetMessage(
-        SMLoc::getFromPointer(mainBuffer->getBufferStart()),
-        SourceMgr::DK_Error, errorResult));
+    context->emitDiagnostic(UnknownLoc::get(context), errorResult,
+                            MLIRContext::DiagnosticKind::Error);
     return nullptr;
   }
 
-  context->registerDiagnosticHandler(existingContextHandler);
   return module.release();
 }