Skip to content

Commit 07b3c76

Browse files
committed
[DiagnosticEngine] Introduce DiagnosticQueue
This allows us to hold tentative diagnostics independent of other diagnostic transactions.
1 parent 2019212 commit 07b3c76

File tree

2 files changed

+106
-7
lines changed

2 files changed

+106
-7
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,7 @@ namespace swift {
848848
friend class DiagnosticTransaction;
849849
friend class CompoundDiagnosticTransaction;
850850
friend class DiagnosticStateRAII;
851+
friend class DiagnosticQueue;
851852

852853
public:
853854
explicit DiagnosticEngine(SourceManager &SourceMgr)
@@ -1137,10 +1138,20 @@ namespace swift {
11371138
/// Send \c diag to all diagnostic consumers.
11381139
void emitDiagnostic(const Diagnostic &diag);
11391140

1141+
/// Handle a new diagnostic, which will either be emitted, or added to an
1142+
/// active transaction.
1143+
void handleDiagnostic(Diagnostic &&diag);
1144+
1145+
/// Clear any tentative diagnostics.
1146+
void clearTentativeDiagnostics();
1147+
11401148
/// Send all tentative diagnostics to all diagnostic consumers and
11411149
/// delete them.
11421150
void emitTentativeDiagnostics();
11431151

1152+
/// Forward all tentative diagnostics to a different diagnostic engine.
1153+
void forwardTentativeDiagnosticsTo(DiagnosticEngine &targetEngine);
1154+
11441155
public:
11451156
DiagnosticKind declaredDiagnosticKindFor(const DiagID id);
11461157

@@ -1333,6 +1344,78 @@ namespace swift {
13331344
}
13341345
};
13351346

1347+
/// Represents a queue of diagnostics that have their emission delayed until
1348+
/// the queue is destroyed. This is similar to DiagnosticTransaction, but
1349+
/// with a few key differences:
1350+
///
1351+
/// - The queue maintains its own diagnostic engine (which may be accessed
1352+
/// through `getDiags()`), and diagnostics must be specifically emitted
1353+
/// using that engine to be enqueued.
1354+
/// - It allows for non-LIFO transactions, as each queue operates
1355+
/// independently.
1356+
/// - A queue can be drained multiple times without having to be recreated
1357+
/// (unlike DiagnosticTransaction, it has no concept of "closing").
1358+
///
1359+
/// Note you may add DiagnosticTransactions to the queue's diagnostic engine,
1360+
/// but they must be closed before attempting to clear or emit the diagnostics
1361+
/// in the queue.
1362+
///
1363+
class DiagnosticQueue final {
1364+
/// The underlying diagnostic engine that the diagnostics will be emitted
1365+
/// by.
1366+
DiagnosticEngine &UnderlyingEngine;
1367+
1368+
/// A temporary engine used to queue diagnostics.
1369+
DiagnosticEngine QueueEngine;
1370+
1371+
/// Whether the queued diagnostics should be emitted on the destruction of
1372+
/// the queue, or whether they should be cleared.
1373+
bool EmitOnDestruction;
1374+
1375+
public:
1376+
DiagnosticQueue(const DiagnosticQueue &) = delete;
1377+
DiagnosticQueue &operator=(const DiagnosticQueue &) = delete;
1378+
1379+
/// Create a new diagnostic queue with a given engine to forward the
1380+
/// diagnostics to.
1381+
explicit DiagnosticQueue(DiagnosticEngine &engine, bool emitOnDestruction)
1382+
: UnderlyingEngine(engine), QueueEngine(engine.SourceMgr),
1383+
EmitOnDestruction(emitOnDestruction) {
1384+
// Open a transaction to avoid emitting any diagnostics for the temporary
1385+
// engine.
1386+
QueueEngine.TransactionCount++;
1387+
}
1388+
1389+
/// Retrieve the engine which may be used to enqueue diagnostics.
1390+
DiagnosticEngine &getDiags() { return QueueEngine; }
1391+
1392+
/// Retrieve the underlying engine which will receive the diagnostics.
1393+
DiagnosticEngine &getUnderlyingDiags() { return UnderlyingEngine; }
1394+
1395+
/// Clear this queue and erase all diagnostics recorded.
1396+
void clear() {
1397+
assert(QueueEngine.TransactionCount == 1 &&
1398+
"Must close outstanding DiagnosticTransactions before draining");
1399+
QueueEngine.clearTentativeDiagnostics();
1400+
}
1401+
1402+
/// Emit all the diagnostics recorded by this queue.
1403+
void emit() {
1404+
assert(QueueEngine.TransactionCount == 1 &&
1405+
"Must close outstanding DiagnosticTransactions before draining");
1406+
QueueEngine.forwardTentativeDiagnosticsTo(UnderlyingEngine);
1407+
}
1408+
1409+
~DiagnosticQueue() {
1410+
if (EmitOnDestruction) {
1411+
emit();
1412+
} else {
1413+
clear();
1414+
}
1415+
QueueEngine.TransactionCount--;
1416+
}
1417+
};
1418+
13361419
inline void
13371420
DiagnosticEngine::diagnoseWithNotes(InFlightDiagnostic parentDiag,
13381421
llvm::function_ref<void(void)> builder) {

lib/AST/DiagnosticEngine.cpp

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,24 +1040,40 @@ DiagnosticBehavior DiagnosticState::determineBehavior(const Diagnostic &diag) {
10401040

10411041
void DiagnosticEngine::flushActiveDiagnostic() {
10421042
assert(ActiveDiagnostic && "No active diagnostic to flush");
1043+
handleDiagnostic(std::move(*ActiveDiagnostic));
1044+
ActiveDiagnostic.reset();
1045+
}
1046+
1047+
void DiagnosticEngine::handleDiagnostic(Diagnostic &&diag) {
10431048
if (TransactionCount == 0) {
1044-
emitDiagnostic(*ActiveDiagnostic);
1049+
emitDiagnostic(diag);
10451050
WrappedDiagnostics.clear();
10461051
WrappedDiagnosticArgs.clear();
10471052
} else {
1048-
onTentativeDiagnosticFlush(*ActiveDiagnostic);
1049-
TentativeDiagnostics.emplace_back(std::move(*ActiveDiagnostic));
1053+
onTentativeDiagnosticFlush(diag);
1054+
TentativeDiagnostics.emplace_back(std::move(diag));
10501055
}
1051-
ActiveDiagnostic.reset();
1056+
}
1057+
1058+
void DiagnosticEngine::clearTentativeDiagnostics() {
1059+
TentativeDiagnostics.clear();
1060+
WrappedDiagnostics.clear();
1061+
WrappedDiagnosticArgs.clear();
10521062
}
10531063

10541064
void DiagnosticEngine::emitTentativeDiagnostics() {
10551065
for (auto &diag : TentativeDiagnostics) {
10561066
emitDiagnostic(diag);
10571067
}
1058-
TentativeDiagnostics.clear();
1059-
WrappedDiagnostics.clear();
1060-
WrappedDiagnosticArgs.clear();
1068+
clearTentativeDiagnostics();
1069+
}
1070+
1071+
void DiagnosticEngine::forwardTentativeDiagnosticsTo(
1072+
DiagnosticEngine &targetEngine) {
1073+
for (auto &diag : TentativeDiagnostics) {
1074+
targetEngine.handleDiagnostic(std::move(diag));
1075+
}
1076+
clearTentativeDiagnostics();
10611077
}
10621078

10631079
/// Returns the access level of the least accessible PrettyPrintedDeclarations

0 commit comments

Comments
 (0)