Skip to content

Commit 45065f3

Browse files
committed
[Diags] Allow multiple in-flight diagnostics
Lift the limitation of a single active diagnostic, which was a pretty easy-to-hit footgun. We now maintain an array of active in-flight diagnostics, which gets flushed once all them have ended.
1 parent 92e6a00 commit 45065f3

18 files changed

+133
-93
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,19 @@ namespace swift {
279279
}
280280
};
281281

282+
namespace detail {
283+
/// Stores information for an active diagnostic that hasn't been emitted yet.
284+
/// This includes both "in-flight" diagnostics as well as diagnostics queued
285+
/// for a transaction.
286+
struct ActiveDiagnostic {
287+
Diagnostic Diag;
288+
SmallVector<DiagnosticInfo, 2> WrappedDiagnostics;
289+
SmallVector<std::vector<DiagnosticArgument>, 4> WrappedDiagnosticArgs;
290+
291+
ActiveDiagnostic(Diagnostic diag) : Diag(std::move(diag)) {}
292+
};
293+
} // namespace detail
294+
282295
/// A diagnostic that has no input arguments, so it is trivially-destructable.
283296
using ZeroArgDiagnostic = Diag<>;
284297

@@ -293,33 +306,40 @@ namespace swift {
293306
friend class DiagnosticEngine;
294307

295308
DiagnosticEngine *Engine;
309+
unsigned Idx;
296310
bool IsActive;
297311

298312
/// Create a new in-flight diagnostic.
299313
///
300314
/// This constructor is only available to the DiagnosticEngine.
301-
InFlightDiagnostic(DiagnosticEngine &Engine)
302-
: Engine(&Engine), IsActive(true) { }
303-
315+
InFlightDiagnostic(DiagnosticEngine &Engine, unsigned idx)
316+
: Engine(&Engine), Idx(idx), IsActive(true) {}
317+
304318
InFlightDiagnostic(const InFlightDiagnostic &) = delete;
305319
InFlightDiagnostic &operator=(const InFlightDiagnostic &) = delete;
306320
InFlightDiagnostic &operator=(InFlightDiagnostic &&) = delete;
307321

322+
/// Retrieve the underlying active diagnostic information.
323+
detail::ActiveDiagnostic &getActiveDiag() const;
324+
325+
/// Retrieve the underlying diagnostic.
326+
Diagnostic &getDiag() const { return getActiveDiag().Diag; }
327+
308328
public:
309329
/// Create an active but unattached in-flight diagnostic.
310330
///
311331
/// The resulting diagnostic can be used as a dummy, accepting the
312332
/// syntax to add additional information to a diagnostic without
313333
/// actually emitting a diagnostic.
314-
InFlightDiagnostic() : Engine(0), IsActive(true) { }
315-
334+
InFlightDiagnostic() : Engine(0), Idx(0), IsActive(true) {}
335+
316336
/// Transfer an in-flight diagnostic to a new object, which is
317337
/// typically used when returning in-flight diagnostics.
318338
InFlightDiagnostic(InFlightDiagnostic &&Other)
319-
: Engine(Other.Engine), IsActive(Other.IsActive) {
339+
: Engine(Other.Engine), Idx(Other.Idx), IsActive(Other.IsActive) {
320340
Other.IsActive = false;
321341
}
322-
342+
323343
~InFlightDiagnostic() {
324344
if (IsActive)
325345
flush();
@@ -784,16 +804,12 @@ namespace swift {
784804
/// Tracks diagnostic behaviors and state
785805
DiagnosticState state;
786806

787-
/// The currently active diagnostic, if there is one.
788-
std::optional<Diagnostic> ActiveDiagnostic;
789-
790-
/// Diagnostics wrapped by ActiveDiagnostic, if any.
791-
SmallVector<DiagnosticInfo, 2> WrappedDiagnostics;
792-
SmallVector<std::vector<DiagnosticArgument>, 4> WrappedDiagnosticArgs;
807+
/// The currently active diagnostics.
808+
SmallVector<detail::ActiveDiagnostic, 4> ActiveDiagnostics;
793809

794810
/// All diagnostics that have are no longer active but have not yet
795811
/// been emitted due to an open transaction.
796-
SmallVector<Diagnostic, 4> TentativeDiagnostics;
812+
SmallVector<detail::ActiveDiagnostic, 4> TentativeDiagnostics;
797813

798814
llvm::BumpPtrAllocator TransactionAllocator;
799815
/// A set of all strings involved in current transactional chain.
@@ -816,6 +832,9 @@ namespace swift {
816832
/// emitted once all transactions have closed.
817833
unsigned TransactionCount = 0;
818834

835+
/// The number of currently in-flight diagnostics.
836+
unsigned NumActiveDiags = 0;
837+
819838
/// For batch mode, use this to know where to output a diagnostic from a
820839
/// non-primary file. It's any location in the buffer of the current primary
821840
/// input being compiled.
@@ -851,7 +870,7 @@ namespace swift {
851870

852871
public:
853872
explicit DiagnosticEngine(SourceManager &SourceMgr)
854-
: SourceMgr(SourceMgr), ActiveDiagnostic(),
873+
: SourceMgr(SourceMgr), ActiveDiagnostics(),
855874
TransactionStrings(TransactionAllocator),
856875
DiagnosticStringsSaver(DiagnosticStringsAllocator) {}
857876

@@ -870,6 +889,7 @@ namespace swift {
870889
}
871890

872891
void flushConsumers() {
892+
ASSERT(NumActiveDiags == 0 && "Expected in-flight diags to be flushed");
873893
for (auto consumer : Consumers)
874894
consumer->flush();
875895
}
@@ -1001,10 +1021,9 @@ namespace swift {
10011021
/// \returns An in-flight diagnostic, to which additional information can
10021022
/// be attached.
10031023
InFlightDiagnostic diagnose(SourceLoc Loc, const Diagnostic &D) {
1004-
assert(!ActiveDiagnostic && "Already have an active diagnostic");
1005-
ActiveDiagnostic = D;
1006-
ActiveDiagnostic->setLoc(Loc);
1007-
return InFlightDiagnostic(*this);
1024+
auto IFD = beginDiagnostic(D);
1025+
getActiveDiagnostic(IFD).Diag.setLoc(Loc);
1026+
return IFD;
10081027
}
10091028

10101029
/// Emit a diagnostic with the given set of diagnostic arguments.
@@ -1080,10 +1099,9 @@ namespace swift {
10801099
/// \returns An in-flight diagnostic, to which additional information can
10811100
/// be attached.
10821101
InFlightDiagnostic diagnose(const Decl *decl, const Diagnostic &diag) {
1083-
assert(!ActiveDiagnostic && "Already have an active diagnostic");
1084-
ActiveDiagnostic = diag;
1085-
ActiveDiagnostic->setDecl(decl);
1086-
return InFlightDiagnostic(*this);
1102+
auto IFD = beginDiagnostic(diag);
1103+
getActiveDiagnostic(IFD).Diag.setDecl(decl);
1104+
return IFD;
10871105
}
10881106

10891107
/// Emit a diagnostic with the given set of diagnostic arguments.
@@ -1137,16 +1155,21 @@ namespace swift {
11371155
DiagnosticFormatOptions FormatOpts = DiagnosticFormatOptions());
11381156

11391157
private:
1158+
/// Begins a new in-flight diagnostic.
1159+
InFlightDiagnostic beginDiagnostic(const Diagnostic &D);
1160+
1161+
/// Ends an in-flight diagnostic. Once all in-flight diagnostics have ended,
1162+
/// they will either be emitted, or captured by an open transaction.
1163+
void endDiagnostic(const InFlightDiagnostic &D);
1164+
11401165
/// Called when tentative diagnostic is about to be flushed,
11411166
/// to apply any required transformations e.g. copy string arguments
11421167
/// to extend their lifetime.
11431168
void onTentativeDiagnosticFlush(Diagnostic &diagnostic);
11441169

1145-
/// Flush the active diagnostic.
1146-
void flushActiveDiagnostic();
1147-
1148-
/// Retrieve the active diagnostic.
1149-
Diagnostic &getActiveDiagnostic() { return *ActiveDiagnostic; }
1170+
/// Retrieve the stored active diagnostic for a given InFlightDiagnostic.
1171+
detail::ActiveDiagnostic &
1172+
getActiveDiagnostic(const InFlightDiagnostic &diag);
11501173

11511174
/// Generate DiagnosticInfo for a Diagnostic to be passed to consumers.
11521175
std::optional<DiagnosticInfo>
@@ -1162,7 +1185,7 @@ namespace swift {
11621185

11631186
/// Handle a new diagnostic, which will either be emitted, or added to an
11641187
/// active transaction.
1165-
void handleDiagnostic(Diagnostic &&diag);
1188+
void handleDiagnostic(detail::ActiveDiagnostic &&diag);
11661189

11671190
/// Clear any tentative diagnostics.
11681191
void clearTentativeDiagnostics();
@@ -1291,12 +1314,12 @@ namespace swift {
12911314
}
12921315

12931316
bool hasErrors() const {
1294-
ArrayRef<Diagnostic> diagnostics(Engine.TentativeDiagnostics.begin() +
1295-
PrevDiagnostics,
1296-
Engine.TentativeDiagnostics.end());
1317+
ArrayRef<detail::ActiveDiagnostic> diagnostics(
1318+
Engine.TentativeDiagnostics.begin() + PrevDiagnostics,
1319+
Engine.TentativeDiagnostics.end());
12971320

12981321
for (auto &diagnostic : diagnostics) {
1299-
auto behavior = Engine.state.determineBehavior(diagnostic);
1322+
auto behavior = Engine.state.determineBehavior(diagnostic.Diag);
13001323
if (behavior == DiagnosticBehavior::Fatal ||
13011324
behavior == DiagnosticBehavior::Error)
13021325
return true;
@@ -1361,14 +1384,14 @@ namespace swift {
13611384

13621385
// The first diagnostic is assumed to be the parent. If this is not an
13631386
// error or warning, we'll assert later when trying to add children.
1364-
Diagnostic &parent = Engine.TentativeDiagnostics[PrevDiagnostics];
1387+
Diagnostic &parent = Engine.TentativeDiagnostics[PrevDiagnostics].Diag;
13651388

13661389
// Associate the children with the parent.
13671390
for (auto diag =
13681391
Engine.TentativeDiagnostics.begin() + PrevDiagnostics + 1;
13691392
diag != Engine.TentativeDiagnostics.end(); ++diag) {
1370-
diag->setIsChildNote(true);
1371-
parent.addChildNote(std::move(*diag));
1393+
diag->Diag.setIsChildNote(true);
1394+
parent.addChildNote(std::move(diag->Diag));
13721395
}
13731396

13741397
// Erase the children, they'll be emitted alongside their parent.

0 commit comments

Comments
 (0)