1 //===- Diagnostic.h - PDLL AST Diagnostics ----------------------*- C++ -*-===// 2 // 3 // Part of the LLVM 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 #ifndef MLIR_TOOLS_PDLL_AST_DIAGNOSTIC_H 10 #define MLIR_TOOLS_PDLL_AST_DIAGNOSTIC_H 11 12 #include <string> 13 #include <optional> 14 15 #include "mlir/Support/LLVM.h" 16 #include "llvm/ADT/FunctionExtras.h" 17 #include "llvm/Support/SourceMgr.h" 18 19 namespace mlir { 20 namespace pdll { 21 namespace ast { 22 class DiagnosticEngine; 23 24 //===----------------------------------------------------------------------===// 25 // Diagnostic 26 //===----------------------------------------------------------------------===// 27 28 /// This class provides a simple implementation of a PDLL diagnostic. 29 class Diagnostic { 30 public: 31 using Severity = llvm::SourceMgr::DiagKind; 32 33 /// Return the severity of this diagnostic. getSeverity()34 Severity getSeverity() const { return severity; } 35 36 /// Return the message of this diagnostic. getMessage()37 StringRef getMessage() const { return message; } 38 39 /// Return the location of this diagnostic. getLocation()40 SMRange getLocation() const { return location; } 41 42 /// Return the notes of this diagnostic. getNotes()43 auto getNotes() const { return llvm::make_pointee_range(notes); } 44 45 /// Attach a note to this diagnostic. 46 Diagnostic &attachNote(const Twine &msg, 47 std::optional<SMRange> noteLoc = std::nullopt) { 48 assert(getSeverity() != Severity::DK_Note && 49 "cannot attach a Note to a Note"); 50 notes.emplace_back( 51 new Diagnostic(Severity::DK_Note, noteLoc.value_or(location), msg)); 52 return *notes.back(); 53 } 54 55 /// Allow an inflight diagnostic to be converted to 'failure', otherwise 56 /// 'success' if this is an empty diagnostic. LogicalResult()57 operator LogicalResult() const { return failure(); } 58 59 private: Diagnostic(Severity severity,SMRange loc,const Twine & msg)60 Diagnostic(Severity severity, SMRange loc, const Twine &msg) 61 : severity(severity), message(msg.str()), location(loc) {} 62 63 // Allow access to the constructor. 64 friend DiagnosticEngine; 65 66 /// The severity of this diagnostic. 67 Severity severity; 68 /// The message held by this diagnostic. 69 std::string message; 70 /// The raw location of this diagnostic. 71 SMRange location; 72 /// Any additional note diagnostics attached to this diagnostic. 73 std::vector<std::unique_ptr<Diagnostic>> notes; 74 }; 75 76 //===----------------------------------------------------------------------===// 77 // InFlightDiagnostic 78 //===----------------------------------------------------------------------===// 79 80 /// This class represents a diagnostic that is inflight and set to be reported. 81 /// This allows for last minute modifications of the diagnostic before it is 82 /// emitted by a DiagnosticEngine. 83 class InFlightDiagnostic { 84 public: 85 InFlightDiagnostic() = default; InFlightDiagnostic(InFlightDiagnostic && rhs)86 InFlightDiagnostic(InFlightDiagnostic &&rhs) 87 : owner(rhs.owner), impl(std::move(rhs.impl)) { 88 // Reset the rhs diagnostic. 89 rhs.impl.reset(); 90 rhs.abandon(); 91 } ~InFlightDiagnostic()92 ~InFlightDiagnostic() { 93 if (isInFlight()) 94 report(); 95 } 96 97 /// Access the internal diagnostic. 98 Diagnostic &operator*() { return *impl; } 99 Diagnostic *operator->() { return &*impl; } 100 101 /// Reports the diagnostic to the engine. 102 void report(); 103 104 /// Abandons this diagnostic so that it will no longer be reported. abandon()105 void abandon() { owner = nullptr; } 106 107 /// Allow an inflight diagnostic to be converted to 'failure', otherwise 108 /// 'success' if this is an empty diagnostic. LogicalResult()109 operator LogicalResult() const { return failure(isActive()); } 110 111 private: 112 InFlightDiagnostic &operator=(const InFlightDiagnostic &) = delete; 113 InFlightDiagnostic &operator=(InFlightDiagnostic &&) = delete; InFlightDiagnostic(DiagnosticEngine * owner,Diagnostic && rhs)114 InFlightDiagnostic(DiagnosticEngine *owner, Diagnostic &&rhs) 115 : owner(owner), impl(std::move(rhs)) {} 116 117 /// Returns true if the diagnostic is still active, i.e. it has a live 118 /// diagnostic. isActive()119 bool isActive() const { return impl.has_value(); } 120 121 /// Returns true if the diagnostic is still in flight to be reported. isInFlight()122 bool isInFlight() const { return owner; } 123 124 // Allow access to the constructor. 125 friend DiagnosticEngine; 126 127 /// The engine that this diagnostic is to report to. 128 DiagnosticEngine *owner = nullptr; 129 130 /// The raw diagnostic that is inflight to be reported. 131 std::optional<Diagnostic> impl; 132 }; 133 134 //===----------------------------------------------------------------------===// 135 // DiagnosticEngine 136 //===----------------------------------------------------------------------===// 137 138 /// This class manages the construction and emission of PDLL diagnostics. 139 class DiagnosticEngine { 140 public: 141 /// A function used to handle diagnostics emitted by the engine. 142 using HandlerFn = llvm::unique_function<void(Diagnostic &)>; 143 144 /// Emit an error to the diagnostic engine. emitError(SMRange loc,const Twine & msg)145 InFlightDiagnostic emitError(SMRange loc, const Twine &msg) { 146 return InFlightDiagnostic( 147 this, Diagnostic(Diagnostic::Severity::DK_Error, loc, msg)); 148 } emitWarning(SMRange loc,const Twine & msg)149 InFlightDiagnostic emitWarning(SMRange loc, const Twine &msg) { 150 return InFlightDiagnostic( 151 this, Diagnostic(Diagnostic::Severity::DK_Warning, loc, msg)); 152 } 153 154 /// Report the given diagnostic. report(Diagnostic && diagnostic)155 void report(Diagnostic &&diagnostic) { 156 if (handler) 157 handler(diagnostic); 158 } 159 160 /// Get the current handler function of this diagnostic engine. getHandlerFn()161 const HandlerFn &getHandlerFn() const { return handler; } 162 163 /// Take the current handler function, resetting the current handler to null. takeHandlerFn()164 HandlerFn takeHandlerFn() { 165 HandlerFn oldHandler = std::move(handler); 166 handler = {}; 167 return oldHandler; 168 } 169 170 /// Set the handler function for this diagnostic engine. setHandlerFn(HandlerFn && newHandler)171 void setHandlerFn(HandlerFn &&newHandler) { handler = std::move(newHandler); } 172 173 private: 174 /// The registered diagnostic handler function. 175 HandlerFn handler; 176 }; 177 178 } // namespace ast 179 } // namespace pdll 180 } // namespace mlir 181 182 #endif // MLIR_TOOLS_PDLL_AST_DIAGNOSTIC_H 183