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"
48 reinterpret_cast<const void *
>(
opaqueVal));
92 auto strRef = val.toStringRef(data);
96 strings.push_back(std::unique_ptr<
char[]>(
new char[strRef.size()]));
97 memcpy(&strings.back()[0], strRef.data(), strRef.size());
99 return StringRef(&strings.back()[0], strRef.size());
148 llvm::raw_string_ostream os(
str);
152 if (
str.find(
'\n') != std::string::npos)
160 llvm::raw_string_ostream os(
str);
167 std::optional<int64_t> messagePartIndex)
const {
168 if (!messagePartIndex.has_value()) {
174 assert(0 <= *messagePartIndex &&
175 *messagePartIndex <=
static_cast<int64_t>(messagePartEnds.size()));
176 size_t argumentStart =
177 *messagePartIndex == 0 ? 0 : messagePartEnds[*messagePartIndex - 1];
179 *messagePartIndex ==
static_cast<int64_t>(messagePartEnds.size())
181 : messagePartEnds[*messagePartIndex];
183 getArguments().slice(argumentStart, argumentEnd - argumentStart))
190 llvm::raw_string_ostream os(
str);
198 size_t numMessageParts = messagePartEnds.size();
202 if (messagePartEnds.empty() || messagePartEnds.back() != arguments.size())
204 for (
size_t i = 0; i < numMessageParts; ++i) {
206 llvm::raw_string_ostream os(
str);
219 "cannot attach a note to a note");
228 return *notes.back();
232Diagnostic::operator LogicalResult()
const {
return failure(); }
236 if (arguments.empty())
238 if (!messagePartEnds.empty() && messagePartEnds.back() == arguments.size())
240 messagePartEnds.push_back(arguments.size());
249InFlightDiagnostic::operator LogicalResult()
const {
250 return failure(isActive());
258 owner->emit(std::move(*
impl));
296 llvm::sys::SmartScopedLock<true> lock(
mutex);
301 for (
auto &handlerIt : llvm::reverse(
handlers))
302 if (succeeded(handlerIt.second(
diag)))
309 auto &os = llvm::errs();
310 if (!llvm::isa<UnknownLoc>(
diag.getLocation()))
311 os <<
diag.getLocation() <<
": ";
330 llvm::sys::SmartScopedLock<true> lock(
impl->mutex);
331 auto uniqueID =
impl->uniqueHandlerId++;
332 impl->handlers.insert({uniqueID, std::move(handler)});
338 llvm::sys::SmartScopedLock<true> lock(
impl->mutex);
339 impl->handlers.erase(handlerID);
346 "notes should not be emitted directly");
357 auto diag = diagEngine.emit(location, severity);
358 if (!message.isTriviallyEmpty())
365 llvm::raw_string_ostream stream(bt);
366 llvm::sys::PrintStackTrace(stream);
369 diag.attachNote() <<
"diagnostic emitted with trace:\n" << bt;
403 ctx->getDiagEngine().eraseHandler(handlerID);
415 StringRef filename) {
419 return bufferIt->second;
422 for (
unsigned i = 1, e = mgr.getNumBuffers() + 1; i != e; ++i) {
423 auto *buf = mgr.getMemoryBuffer(i);
424 if (buf->getBufferIdentifier() == filename)
430 unsigned id = mgr.AddIncludeFile(std::string(filename), SMLoc(), ignored);
443 if (isa<NameLoc>(loc))
445 if (
auto callLoc = dyn_cast<CallSiteLoc>(loc))
447 if (isa<FusedLoc>(loc)) {
448 for (
auto subLoc : cast<FusedLoc>(loc).getLocations()) {
462 return llvm::SourceMgr::DK_Note;
464 return llvm::SourceMgr::DK_Warning;
466 return llvm::SourceMgr::DK_Error;
468 return llvm::SourceMgr::DK_Remark;
470 llvm_unreachable(
"Unknown DiagnosticSeverity");
491 bool displaySourceLine) {
498 llvm::raw_string_ostream strOS(str);
499 if (!llvm::isa<UnknownLoc>(loc))
500 strOS << loc <<
": ";
507 if (displaySourceLine) {
508 auto smloc = convertLocToSMLoc(fileLoc);
517 llvm::raw_string_ostream locOS(locStr);
518 locOS << fileLoc.getFilename().getValue() <<
":" << fileLoc.getLine() <<
":"
519 << fileLoc.getColumn();
527 auto addLocToStack = [&](
Location loc, StringRef locContext) {
528 if (std::optional<Location> showableLoc = findLocToShow(loc))
529 locationStack.emplace_back(*showableLoc, locContext);
534 addLocToStack(loc, {});
540 loc = callLoc->getCaller();
541 for (
unsigned curDepth = 0; curDepth < callStackLimit; ++curDepth) {
542 addLocToStack(loc,
"called from");
544 loc = callLoc->getCaller();
551 if (locationStack.empty()) {
552 for (
const std::string &str :
diag.strs())
557 for (
const std::string &str :
diag.strs())
559 for (
auto &it : llvm::drop_begin(locationStack))
565 for (
auto ¬e :
diag.getNotes()) {
566 emitDiagnostic(note.getLocation(), note.str(), note.getSeverity(),
567 loc != note.getLocation());
568 loc = note.getLocation();
573 callStackLimit = limit;
577const llvm::MemoryBuffer *
579 if (
unsigned id =
impl->getSourceMgrBufferIDForFile(
mgr, filename))
580 return mgr.getMemoryBuffer(
id);
584std::optional<Location>
585SourceMgrDiagnosticHandler::findLocToShow(
Location loc) {
593 .Case([&](CallSiteLoc callLoc) -> std::optional<Location> {
596 return findLocToShow(callLoc.getCallee());
598 .Case([&](
FileLineColLoc) -> std::optional<Location> {
return loc; })
599 .Case([&](FusedLoc fusedLoc) -> std::optional<Location> {
602 for (Location childLoc : fusedLoc.getLocations())
603 if (std::optional<Location> showableLoc = findLocToShow(childLoc))
607 .Case([&](NameLoc nameLoc) -> std::optional<Location> {
608 return findLocToShow(nameLoc.getChildLoc());
610 .Case([&](OpaqueLoc opaqueLoc) -> std::optional<Location> {
612 return findLocToShow(opaqueLoc.getFallbackLocation());
614 .Case([](UnknownLoc) -> std::optional<Location> {
622SMLoc SourceMgrDiagnosticHandler::convertLocToSMLoc(
FileLineColLoc loc) {
628 unsigned bufferId = impl->getSourceMgrBufferIDForFile(
mgr, loc.
getFilename());
654 SMRange range(
fileLoc, SMLoc::getFromPointer(
fileLoc.getPointer() +
656 mgr.PrintMessage(os,
fileLoc, llvm::SourceMgr::DK_Error, msg, range);
658 mgr.PrintMessage(os,
fileLoc, llvm::SourceMgr::DK_Error, msg);
675 std::string regexStr;
676 llvm::raw_string_ostream regexOS(regexStr);
678 while (!strToProcess.empty()) {
680 size_t regexIt = strToProcess.find(
"{{");
681 if (regexIt == StringRef::npos) {
682 regexOS << llvm::Regex::escape(strToProcess);
685 regexOS << llvm::Regex::escape(strToProcess.take_front(regexIt));
686 strToProcess = strToProcess.drop_front(regexIt + 2);
689 size_t regexEndIt = strToProcess.find(
"}}");
690 if (regexEndIt == StringRef::npos)
691 return emitError(os, mgr,
"found start of regex with no end '}}'");
692 StringRef regexStr = strToProcess.take_front(regexEndIt);
695 std::string regexError;
696 if (!llvm::Regex(regexStr).isValid(regexError))
697 return emitError(os, mgr,
"invalid regex: " + regexError);
699 regexOS <<
'(' << regexStr <<
')';
700 strToProcess = strToProcess.drop_front(regexEndIt + 2);
727 std::optional<MutableArrayRef<ExpectedDiag>>
733 const llvm::MemoryBuffer *buf);
750 llvm::Regex(
"expected-(error|note|remark|warning)(-re)? "
751 "*(@([+-][0-9]+|above|below|unknown))? *{{(.*)}}$");
772 llvm_unreachable(
"Unknown DiagnosticSeverity");
775std::optional<MutableArrayRef<ExpectedDiag>>
785 raw_ostream &os, llvm::SourceMgr &mgr,
const llvm::MemoryBuffer *buf) {
792 unsigned lastNonDesignatorLine = 0;
799 buf->getBuffer().split(lines,
'\n');
800 for (
unsigned lineNo = 0, e = lines.size(); lineNo < e; ++lineNo) {
802 if (!
expected.match(lines[lineNo].rtrim(), &matches)) {
804 if (!designatorsForNextLine.empty()) {
805 for (
unsigned diagIndex : designatorsForNextLine)
806 expectedDiags[diagIndex].lineNo = lineNo + 1;
807 designatorsForNextLine.clear();
809 lastNonDesignatorLine = lineNo;
814 SMLoc expectedStart = SMLoc::getFromPointer(matches[0].data());
817 if (matches[1] ==
"error")
819 else if (matches[1] ==
"warning")
821 else if (matches[1] ==
"remark")
824 assert(matches[1] ==
"note");
827 ExpectedDiag record(kind, lineNo + 1, expectedStart, matches[5]);
830 if (!matches[2].empty() && failed(record.
computeRegex(os, mgr))) {
835 StringRef offsetMatch = matches[3];
836 if (!offsetMatch.empty()) {
837 offsetMatch = offsetMatch.drop_front(1);
840 if (offsetMatch[0] ==
'+' || offsetMatch[0] ==
'-') {
842 offsetMatch.drop_front().getAsInteger(0, offset);
844 if (offsetMatch.front() ==
'+')
848 }
else if (offsetMatch.consume_front(
"unknown")) {
853 }
else if (offsetMatch.consume_front(
"above")) {
856 record.
lineNo = lastNonDesignatorLine + 1;
860 assert(offsetMatch.consume_front(
"below"));
861 designatorsForNextLine.push_back(expectedDiags.size());
868 expectedDiags.emplace_back(std::move(record));
870 return expectedDiags;
879 for (
unsigned i = 0, e =
mgr.getNumBuffers(); i != e; ++i)
880 (
void)
impl->computeExpectedDiags(out,
mgr,
mgr.getMemoryBuffer(i + 1));
905 err.emitError(
os,
mgr,
907 err.substring +
"\" was not produced");
909 for (
auto &expectedDiagsPair :
impl->expectedDiagsPerFile)
910 for (
auto &err : expectedDiagsPair.second)
911 checkExpectedDiags(err);
912 for (
auto &err :
impl->expectedUnknownLocDiags)
913 checkExpectedDiags(err);
914 impl->expectedDiagsPerFile.clear();
918std::unique_ptr<ScopedDiagnosticHandler>
920 return std::make_unique<ScopedDiagnosticHandler>(
925void SourceMgrDiagnosticVerifierHandler::process(
Diagnostic &
diag) {
926 for (
const std::string &str :
diag.strs())
927 process(
diag.getLocation(), str,
diag.getSeverity());
928 for (
auto ¬e :
diag.getNotes())
933void SourceMgrDiagnosticVerifierHandler::process(
LocationAttr loc,
944 diags = impl->computeExpectedDiags(
949 diags = impl->expectedUnknownLocDiags;
954 ExpectedDiag *nearMiss =
nullptr;
957 for (
auto &e : diags) {
959 if (fileLoc && fileLoc.
getLine() != e.lineNo)
962 if (e.kind == kind) {
978 mgr.PrintMessage(
os, nearMiss->
fileLoc, llvm::SourceMgr::DK_Error,
980 "' diagnostic emitted when expecting a '" +
985 impl->status = failure();
1011 uint64_t tid = llvm::get_threadid();
1012 llvm::sys::SmartScopedLock<true> lock(
mutex);
1035 return context->getDiagEngine().emit(std::move(
diag));
1053 uint64_t tid = llvm::get_threadid();
1054 llvm::sys::SmartScopedLock<true> lock(
mutex);
1060 uint64_t tid = llvm::get_threadid();
1061 llvm::sys::SmartScopedLock<true> lock(
mutex);
1071 os <<
"In-Flight Diagnostics:\n";
1077 if (!llvm::isa<UnknownLoc>(
diag.getLocation()))
1078 os <<
diag.getLocation() <<
": ";
1079 switch (
diag.getSeverity()) {
1121 impl->setOrderIDForThread(orderID);
1127 impl->eraseOrderIDForThread();
static OpPrintingFlags adjustPrintingFlags(OpPrintingFlags flags, DiagnosticSeverity severity)
Adjusts operation printing flags used in diagnostics for the given severity level.
static StringRef twineToStrRef(const Twine &val, SmallVectorImpl< std::unique_ptr< char[]> > &strings)
Convert a Twine to a StringRef.
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 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.
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, concatenating all the parts if there are multiples.
void startNewMessagePart()
Starts a new message part.
SmallVector< std::string > strs() const
Converts each message part to a separate 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.
Diagnostic(Location loc, DiagnosticSeverity severity)
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, std::optional< int64_t > messagePartIndex=std::nullopt) const
Outputs this diagnostic to a stream.
Diagnostic & appendOp(Operation &op, const OpPrintingFlags &flags)
Append an operation with the given printing flags.
An instance of this location represents a tuple of file, line number, and column number.
StringAttr getFilename() const
unsigned getColumn() const
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.
Location objects represent source locations information in MLIR.
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(bool enable=true)
Use local scope when printing the operation.
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
Operation * getOperation() const
OpPrintingFlags & flags()
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={})
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.
~ScopedDiagnosticHandler()
ScopedDiagnosticHandler(MLIRContext *ctx)
void setHandler(FuncTy &&handler)
Set the handler to manage via RAII.
void setCallStackLimit(unsigned limit)
Set the maximum depth that a call stack will be printed. Defaults to 10.
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.
~SourceMgrDiagnosticVerifierHandler()
SourceMgrDiagnosticVerifierHandler(llvm::SourceMgr &srcMgr, MLIRContext *ctx, raw_ostream &out, Level level=Level::All)
std::unique_ptr< ScopedDiagnosticHandler > registerInContext(MLIRContext *ctx)
Register this handler with ctx and return a scoped registration.
LogicalResult verify()
Returns the status of the handler and verifies that all expected diagnostics were emitted.
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.
llvm::TypeSwitch< T, ResultT > TypeSwitch
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
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.
SourceMgrDiagnosticVerifierHandler::Level getVerifyLevel() const
llvm::Regex expected
Regex to match the expected diagnostics format.
SourceMgrDiagnosticVerifierHandler::Level level
Verification level.
SmallVector< ExpectedDiag, 2 > expectedUnknownLocDiags
A list of expected diagnostics with unknown locations.
SourceMgrDiagnosticVerifierHandlerImpl(SourceMgrDiagnosticVerifierHandler::Level level)
std::optional< MutableArrayRef< ExpectedDiag > > getExpectedDiags(StringRef bufName)
Returns the expected diagnostics for the given source file.