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