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 ¬e : 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 ¬e : 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 }