15 #include "llvm/ADT/MapVector.h"
16 #include "llvm/ADT/SmallString.h"
17 #include "llvm/ADT/StringMap.h"
18 #include "llvm/ADT/TypeSwitch.h"
19 #include "llvm/Support/Mutex.h"
20 #include "llvm/Support/PrettyStackTrace.h"
21 #include "llvm/Support/Regex.h"
22 #include "llvm/Support/Signals.h"
23 #include "llvm/Support/SourceMgr.h"
24 #include "llvm/Support/raw_ostream.h"
37 opaqueVal(reinterpret_cast<intptr_t>(attr.getAsOpaquePointer())) {}
42 opaqueVal(reinterpret_cast<intptr_t>(val.getAsOpaquePointer())) {}
48 reinterpret_cast<const void *
>(
opaqueVal));
88 std::vector<std::unique_ptr<
char[]>> &strings) {
91 auto strRef = val.toStringRef(data);
95 strings.push_back(std::unique_ptr<
char[]>(
new char[strRef.size()]));
96 memcpy(&strings.back()[0], strRef.data(), strRef.size());
98 return StringRef(&strings.back()[0], strRef.size());
143 llvm::raw_string_ostream os(
str);
147 if (
str.find(
'\n') != std::string::npos)
155 llvm::raw_string_ostream os(
str);
169 llvm::raw_string_ostream os(
str);
180 "cannot attach a note to a note");
189 return *notes.back();
193 Diagnostic::operator LogicalResult()
const {
return failure(); }
201 InFlightDiagnostic::operator LogicalResult()
const {
202 return failure(isActive());
248 llvm::sys::SmartScopedLock<true> lock(mutex);
253 for (
auto &handlerIt : llvm::reverse(handlers))
254 if (succeeded(handlerIt.second(
diag)))
261 auto &os = llvm::errs();
262 if (!llvm::isa<UnknownLoc>(
diag.getLocation()))
263 os <<
diag.getLocation() <<
": ";
282 llvm::sys::SmartScopedLock<true> lock(
impl->mutex);
283 auto uniqueID =
impl->uniqueHandlerId++;
284 impl->handlers.insert({uniqueID, std::move(handler)});
290 llvm::sys::SmartScopedLock<true> lock(
impl->mutex);
291 impl->handlers.erase(handlerID);
298 "notes should not be emitted directly");
309 auto diag = diagEngine.emit(location, severity);
310 if (!message.isTriviallyEmpty())
317 llvm::raw_string_ostream stream(bt);
318 llvm::sys::PrintStackTrace(stream);
321 diag.attachNote() <<
"diagnostic emitted with trace:\n" << bt;
355 ctx->getDiagEngine().eraseHandler(handlerID);
367 StringRef filename) {
369 auto bufferIt = filenameToBufId.find(filename);
370 if (bufferIt != filenameToBufId.end())
371 return bufferIt->second;
374 for (
unsigned i = 1, e = mgr.getNumBuffers() + 1; i != e; ++i) {
375 auto *buf = mgr.getMemoryBuffer(i);
376 if (buf->getBufferIdentifier() == filename)
377 return filenameToBufId[filename] = i;
382 unsigned id = mgr.AddIncludeFile(std::string(filename), SMLoc(), ignored);
383 filenameToBufId[filename] = id;
395 if (dyn_cast<NameLoc>(loc))
397 if (
auto callLoc = dyn_cast<CallSiteLoc>(loc))
399 if (dyn_cast<FusedLoc>(loc)) {
400 for (
auto subLoc : cast<FusedLoc>(loc).getLocations()) {
414 return llvm::SourceMgr::DK_Note;
416 return llvm::SourceMgr::DK_Warning;
418 return llvm::SourceMgr::DK_Error;
420 return llvm::SourceMgr::DK_Remark;
422 llvm_unreachable(
"Unknown DiagnosticSeverity");
426 llvm::SourceMgr &mgr,
MLIRContext *ctx, raw_ostream &os,
429 shouldShowLocFn(std::move(shouldShowLocFn)),
437 std::move(shouldShowLocFn)) {}
443 bool displaySourceLine) {
450 llvm::raw_string_ostream strOS(str);
451 if (!llvm::isa<UnknownLoc>(loc))
452 strOS << loc <<
": ";
459 if (displaySourceLine) {
460 auto smloc = convertLocToSMLoc(fileLoc);
469 llvm::raw_string_ostream locOS(locStr);
470 locOS << fileLoc.getFilename().getValue() <<
":" << fileLoc.getLine() <<
":"
471 << fileLoc.getColumn();
479 auto addLocToStack = [&](
Location loc, StringRef locContext) {
480 if (std::optional<Location> showableLoc = findLocToShow(loc))
481 locationStack.emplace_back(*showableLoc, locContext);
486 addLocToStack(loc, {});
492 loc = callLoc->getCaller();
493 for (
unsigned curDepth = 0; curDepth < callStackLimit; ++curDepth) {
494 addLocToStack(loc,
"called from");
496 loc = callLoc->getCaller();
503 if (locationStack.empty()) {
509 for (
auto &it : llvm::drop_begin(locationStack))
515 for (
auto ¬e :
diag.getNotes()) {
516 emitDiagnostic(note.getLocation(), note.str(), note.getSeverity(),
517 loc != note.getLocation());
518 loc = note.getLocation();
523 const llvm::MemoryBuffer *
525 if (
unsigned id =
impl->getSourceMgrBufferIDForFile(
mgr, filename))
526 return mgr.getMemoryBuffer(
id);
530 std::optional<Location>
531 SourceMgrDiagnosticHandler::findLocToShow(
Location loc) {
539 .Case([&](CallSiteLoc callLoc) -> std::optional<Location> {
542 return findLocToShow(callLoc.getCallee());
544 .Case([&](FileLineColLoc) -> std::optional<Location> {
return loc; })
545 .Case([&](
FusedLoc fusedLoc) -> std::optional<Location> {
548 for (
Location childLoc : fusedLoc.getLocations())
549 if (std::optional<Location> showableLoc = findLocToShow(childLoc))
553 .Case([&](NameLoc nameLoc) -> std::optional<Location> {
554 return findLocToShow(nameLoc.getChildLoc());
556 .Case([&](OpaqueLoc opaqueLoc) -> std::optional<Location> {
558 return findLocToShow(opaqueLoc.getFallbackLocation());
560 .Case([](UnknownLoc) -> std::optional<Location> {
568 SMLoc SourceMgrDiagnosticHandler::convertLocToSMLoc(FileLineColLoc loc) {
571 if (loc.getLine() == 0 || loc.getColumn() == 0)
574 unsigned bufferId =
impl->getSourceMgrBufferIDForFile(
mgr, loc.getFilename());
577 return mgr.FindLocForLineAndColumn(bufferId, loc.getLine(), loc.getColumn());
593 LogicalResult
emitError(raw_ostream &os, llvm::SourceMgr &mgr,
595 SMRange range(
fileLoc, SMLoc::getFromPointer(
fileLoc.getPointer() +
597 mgr.PrintMessage(os,
fileLoc, llvm::SourceMgr::DK_Error, msg, range);
613 std::string regexStr;
614 llvm::raw_string_ostream regexOS(regexStr);
616 while (!strToProcess.empty()) {
618 size_t regexIt = strToProcess.find(
"{{");
619 if (regexIt == StringRef::npos) {
620 regexOS << llvm::Regex::escape(strToProcess);
623 regexOS << llvm::Regex::escape(strToProcess.take_front(regexIt));
624 strToProcess = strToProcess.drop_front(regexIt + 2);
627 size_t regexEndIt = strToProcess.find(
"}}");
628 if (regexEndIt == StringRef::npos)
629 return emitError(os, mgr,
"found start of regex with no end '}}'");
630 StringRef regexStr = strToProcess.take_front(regexEndIt);
633 std::string regexError;
634 if (!llvm::Regex(regexStr).isValid(regexError))
635 return emitError(os, mgr,
"invalid regex: " + regexError);
637 regexOS <<
'(' << regexStr <<
')';
638 strToProcess = strToProcess.drop_front(regexEndIt + 2);
663 std::optional<MutableArrayRef<ExpectedDiag>>
669 const llvm::MemoryBuffer *buf);
679 llvm::Regex(
"expected-(error|note|remark|warning)(-re)? "
680 "*(@([+-][0-9]+|above|below))? *{{(.*)}}$");
697 llvm_unreachable(
"Unknown DiagnosticSeverity");
700 std::optional<MutableArrayRef<ExpectedDiag>>
701 SourceMgrDiagnosticVerifierHandlerImpl::getExpectedDiags(StringRef bufName) {
702 auto expectedDiags = expectedDiagsPerFile.find(bufName);
703 if (expectedDiags != expectedDiagsPerFile.end())
709 SourceMgrDiagnosticVerifierHandlerImpl::computeExpectedDiags(
710 raw_ostream &os, llvm::SourceMgr &mgr,
const llvm::MemoryBuffer *buf) {
714 auto &expectedDiags = expectedDiagsPerFile[buf->getBufferIdentifier()];
717 unsigned lastNonDesignatorLine = 0;
724 buf->getBuffer().split(lines,
'\n');
725 for (
unsigned lineNo = 0, e = lines.size(); lineNo < e; ++lineNo) {
727 if (!expected.match(lines[lineNo].rtrim(), &matches)) {
729 if (!designatorsForNextLine.empty()) {
730 for (
unsigned diagIndex : designatorsForNextLine)
731 expectedDiags[diagIndex].lineNo = lineNo + 1;
732 designatorsForNextLine.clear();
734 lastNonDesignatorLine = lineNo;
739 SMLoc expectedStart = SMLoc::getFromPointer(matches[0].data());
742 if (matches[1] ==
"error")
744 else if (matches[1] ==
"warning")
746 else if (matches[1] ==
"remark")
749 assert(matches[1] ==
"note");
752 ExpectedDiag record(kind, lineNo + 1, expectedStart, matches[5]);
755 if (!matches[2].empty() && failed(record.
computeRegex(os, mgr))) {
760 StringRef offsetMatch = matches[3];
761 if (!offsetMatch.empty()) {
762 offsetMatch = offsetMatch.drop_front(1);
765 if (offsetMatch[0] ==
'+' || offsetMatch[0] ==
'-') {
767 offsetMatch.drop_front().getAsInteger(0, offset);
769 if (offsetMatch.front() ==
'+')
773 }
else if (offsetMatch.consume_front(
"above")) {
776 record.
lineNo = lastNonDesignatorLine + 1;
780 assert(offsetMatch.consume_front(
"below"));
781 designatorsForNextLine.push_back(expectedDiags.size());
788 expectedDiags.emplace_back(std::move(record));
790 return expectedDiags;
794 llvm::SourceMgr &srcMgr,
MLIRContext *ctx, raw_ostream &out)
799 for (
unsigned i = 0, e =
mgr.getNumBuffers(); i != e; ++i)
800 (void)
impl->computeExpectedDiags(out,
mgr,
mgr.getMemoryBuffer(i + 1));
808 for (
auto ¬e :
diag.getNotes())
827 for (
auto &expectedDiagsPair :
impl->expectedDiagsPerFile) {
828 for (
auto &err : expectedDiagsPair.second) {
832 err.emitError(
os,
mgr,
834 err.substring +
"\" was not produced");
837 impl->expectedDiagsPerFile.clear();
842 void SourceMgrDiagnosticVerifierHandler::process(
Diagnostic &
diag) {
843 auto kind =
diag.getSeverity();
846 if (
auto fileLoc =
diag.getLocation()->findInstanceOf<FileLineColLoc>())
847 return process(fileLoc,
diag.str(), kind);
852 impl->status = failure();
856 void SourceMgrDiagnosticVerifierHandler::process(FileLineColLoc loc,
860 auto diags =
impl->getExpectedDiags(loc.getFilename());
862 diags =
impl->computeExpectedDiags(
os,
mgr,
871 unsigned line = loc.getLine();
872 for (
auto &e : *diags) {
873 if (line == e.lineNo && e.match(msg)) {
874 if (e.kind == kind) {
887 mgr.PrintMessage(
os, nearMiss->
fileLoc, llvm::SourceMgr::DK_Error,
889 "' diagnostic emitted when expecting a '" +
894 impl->status = failure();
920 uint64_t tid = llvm::get_threadid();
921 llvm::sys::SmartScopedLock<true> lock(
mutex);
962 uint64_t tid = llvm::get_threadid();
963 llvm::sys::SmartScopedLock<true> lock(
mutex);
969 uint64_t tid = llvm::get_threadid();
970 llvm::sys::SmartScopedLock<true> lock(
mutex);
975 void print(raw_ostream &os)
const override {
980 os <<
"In-Flight Diagnostics:\n";
986 if (!llvm::isa<UnknownLoc>(
diag.getLocation()))
987 os <<
diag.getLocation() <<
": ";
988 switch (
diag.getSeverity()) {
989 case DiagnosticSeverity::Error:
992 case DiagnosticSeverity::Warning:
995 case DiagnosticSeverity::Note:
998 case DiagnosticSeverity::Remark:
1030 impl->setOrderIDForThread(orderID);
1036 impl->eraseOrderIDForThread();
static OpPrintingFlags adjustPrintingFlags(OpPrintingFlags flags, DiagnosticSeverity severity)
Adjusts operation printing flags used in diagnostics for the given severity level.
static InFlightDiagnostic emitDiag(Location location, DiagnosticSeverity severity, const Twine &message)
Helper function used to emit a diagnostic with an optionally empty twine message.
static StringRef getDiagKindStr(DiagnosticSeverity kind)
Given a diagnostic kind, return a human readable string for it.
static StringRef twineToStrRef(const Twine &val, std::vector< std::unique_ptr< char[]>> &strings)
Convert a Twine to a StringRef.
static std::optional< CallSiteLoc > getCallSiteLoc(Location loc)
Return a processable CallSiteLoc from the given location.
static llvm::SourceMgr::DiagKind getDiagKind(DiagnosticSeverity kind)
Given a diagnostic kind, returns the LLVM DiagKind.
static std::string diag(const llvm::Value &value)
Attributes are known-constant values of operations.
MLIRContext * getContext() const
Return the context this attribute belongs to.
static Attribute getFromOpaquePointer(const void *ptr)
Construct an attribute from the opaque pointer representation.
A variant type that holds a single argument for a diagnostic.
DiagnosticArgumentKind
Enum that represents the different kinds of diagnostic arguments supported.
StringRef getAsString() const
Returns this argument as a string.
double getAsDouble() const
Returns this argument as a double.
DiagnosticArgument(Attribute attr)
Note: The constructors below are only exposed due to problems accessing constructors from type traits...
Type getAsType() const
Returns this argument as a Type.
int64_t getAsInteger() const
Returns this argument as a signed integer.
DiagnosticArgumentKind getKind() const
Returns the kind of this argument.
Attribute getAsAttribute() const
Returns this argument as an Attribute.
void print(raw_ostream &os) const
Outputs this argument to a stream.
uint64_t getAsUnsigned() const
Returns this argument as an unsigned integer.
uint64_t HandlerID
A handle to a specific registered handler object.
InFlightDiagnostic emit(Location loc, DiagnosticSeverity severity)
Create a new inflight diagnostic with the given location and severity.
void eraseHandler(HandlerID id)
Erase the registered diagnostic handler with the given identifier.
llvm::unique_function< LogicalResult(Diagnostic &)> HandlerTy
The handler type for MLIR diagnostics.
HandlerID registerHandler(HandlerTy handler)
Register a new handler for diagnostics to the engine.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
std::string str() const
Converts the diagnostic to a string.
Diagnostic & attachNote(std::optional< Location > noteLoc=std::nullopt)
Attaches a note to this diagnostic.
MutableArrayRef< DiagnosticArgument > getArguments()
Returns the current list of diagnostic arguments.
std::enable_if_t<!std::is_convertible< Arg, StringRef >::value &&std::is_constructible< DiagnosticArgument, Arg >::value, Diagnostic & > operator<<(Arg &&val)
Stream operator for inserting new diagnostic arguments.
void print(raw_ostream &os) const
Outputs this diagnostic to a stream.
Diagnostic & appendOp(Operation &op, const OpPrintingFlags &flags)
Append an operation with the given printing flags.
This class represents a diagnostic that is inflight and set to be reported.
void report()
Reports the diagnostic to the engine.
void abandon()
Abandons this diagnostic so that it will no longer be reported.
T findInstanceOf()
Return an instance of the given location type if one is nested under the current location.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
bool shouldPrintStackTraceOnDiagnostic()
Return true if we should attach the current stacktrace to diagnostics when emitted.
DiagnosticEngine & getDiagEngine()
Returns the diagnostic engine for this context.
Set of flags used to control the behavior of the various IR print methods (e.g.
OpPrintingFlags & elideLargeElementsAttrs(int64_t largeElementLimit=16)
Enables the elision of large elements attributes by printing a lexically valid but otherwise meaningl...
OpPrintingFlags & printGenericOpForm(bool enable=true)
Always print operations in the generic form.
OpPrintingFlags & useLocalScope()
Use local scope when printing the operation.
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
Operation is the basic unit of execution within MLIR.
void print(raw_ostream &os, const OpPrintingFlags &flags=std::nullopt)
void eraseOrderIDForThread()
Remove the order id for the current thread.
~ParallelDiagnosticHandler()
ParallelDiagnosticHandler(MLIRContext *ctx)
void setOrderIDForThread(size_t orderID)
Set the order id for the current thread.
This diagnostic handler is a simple RAII class that registers and erases a diagnostic handler on a gi...
~ScopedDiagnosticHandler()
void setHandler(FuncTy &&handler)
Set the handler to manage via RAII.
This class is a utility diagnostic handler for use with llvm::SourceMgr.
void emitDiagnostic(Location loc, Twine message, DiagnosticSeverity kind, bool displaySourceLine=true)
Emit the given diagnostic information with the held source manager.
raw_ostream & os
The output stream to use when printing diagnostics.
SourceMgrDiagnosticHandler(llvm::SourceMgr &mgr, MLIRContext *ctx, raw_ostream &os, ShouldShowLocFn &&shouldShowLocFn={})
~SourceMgrDiagnosticHandler()
ShouldShowLocFn shouldShowLocFn
A functor used when determining if a location for a diagnostic should be shown.
const llvm::MemoryBuffer * getBufferForFile(StringRef filename)
Get a memory buffer for the given file, or nullptr if no file is available.
llvm::SourceMgr & mgr
The source manager that we are wrapping.
llvm::unique_function< bool(Location)> ShouldShowLocFn
This type represents a functor used to filter out locations when printing a diagnostic.
This class is a utility diagnostic handler for use with llvm::SourceMgr that verifies that emitted di...
~SourceMgrDiagnosticVerifierHandler()
LogicalResult verify()
Returns the status of the handler and verifies that all expected diagnostics were emitted.
SourceMgrDiagnosticVerifierHandler(llvm::SourceMgr &srcMgr, MLIRContext *ctx, raw_ostream &out)
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
static Type getFromOpaquePointer(const void *pointer)
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
void print(raw_ostream &os) const
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Include the generated interface declarations.
InFlightDiagnostic emitWarning(Location loc)
Utility method to emit a warning message using this location.
DiagnosticSeverity
Defines the different supported severity of a diagnostic.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
InFlightDiagnostic emitRemark(Location loc)
Utility method to emit a remark message using this location.
DiagnosticEngine::HandlerID uniqueHandlerId
This is a unique identifier counter for diagnostic handlers in the context.
llvm::sys::SmartMutex< true > mutex
A mutex to ensure that diagnostics emission is thread-safe.
void emit(Diagnostic &&diag)
Emit a diagnostic using the registered issue handle if present, or with the default behavior if not.
llvm::SmallMapVector< DiagnosticEngine::HandlerID, DiagnosticEngine::HandlerTy, 2 > handlers
These are the handlers used to report diagnostics.
This class represents an expected output diagnostic.
LogicalResult emitError(raw_ostream &os, llvm::SourceMgr &mgr, const Twine &msg)
Emit an error at the location referenced by this diagnostic.
unsigned lineNo
The line number the expected diagnostic should be on.
LogicalResult computeRegex(raw_ostream &os, llvm::SourceMgr &mgr)
Compute the regex matcher for this diagnostic, using the provided stream and manager to emit diagnost...
bool match(StringRef str) const
Returns true if this diagnostic matches the given string.
bool matched
A flag indicating if the expected diagnostic has been matched yet.
DiagnosticSeverity kind
The severity of the diagnosic expected.
StringRef substring
The substring that is expected to be within the diagnostic.
std::optional< llvm::Regex > substringRegex
An optional regex matcher, if the expected diagnostic sub-string was a regex string.
SMLoc fileLoc
The location of the expected diagnostic within the input file.
ExpectedDiag(DiagnosticSeverity kind, unsigned lineNo, SMLoc fileLoc, StringRef substring)
size_t id
The id for this diagnostic, this is used for ordering.
bool operator<(const ThreadDiagnostic &rhs) const
ThreadDiagnostic(size_t id, Diagnostic diag)
Diagnostic diag
The diagnostic.
void print(raw_ostream &os) const override
Dump the current diagnostics that were inflight.
MLIRContext * context
The context to emit the diagnostics to.
ParallelDiagnosticHandlerImpl(MLIRContext *ctx)
~ParallelDiagnosticHandlerImpl() override
void emitDiagnostics(llvm::function_ref< void(Diagnostic &)> emitFn) const
Utility method to emit any held diagnostics.
DiagnosticEngine::HandlerID handlerID
The unique id for the parallel handler.
DenseMap< uint64_t, size_t > threadToOrderID
A mapping between the thread id and the current order id.
void setOrderIDForThread(size_t orderID)
Set the order id for the current thread.
std::vector< ThreadDiagnostic > diagnostics
An unordered list of diagnostics that were emitted.
void eraseOrderIDForThread()
Remove the order id for the current thread.
llvm::sys::SmartMutex< true > mutex
A smart mutex to lock access to the internal state.
llvm::StringMap< unsigned > filenameToBufId
Mapping between file name and buffer ID's.
unsigned getSourceMgrBufferIDForFile(llvm::SourceMgr &mgr, StringRef filename)
Return the SrcManager buffer id for the specified file, or zero if none can be found.
llvm::StringMap< SmallVector< ExpectedDiag, 2 > > expectedDiagsPerFile
A list of expected diagnostics for each buffer of the source manager.
LogicalResult status
The current status of the verifier.
MutableArrayRef< ExpectedDiag > computeExpectedDiags(raw_ostream &os, llvm::SourceMgr &mgr, const llvm::MemoryBuffer *buf)
Computes the expected diagnostics for the given source buffer.
SourceMgrDiagnosticVerifierHandlerImpl()
llvm::Regex expected
Regex to match the expected diagnostics format.
std::optional< MutableArrayRef< ExpectedDiag > > getExpectedDiags(StringRef bufName)
Returns the expected diagnostics for the given source file.