Coverage Report

Created: 2020-06-26 05:44

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