Skip to content

Commit 93ffb88

Browse files
authored
Merge pull request swiftlang#36125 from beccadax/behave-yourself
[NFC] Allow errors to be conditionally emitted as warnings
2 parents 362954b + 313235a commit 93ffb88

File tree

4 files changed

+148
-150
lines changed

4 files changed

+148
-150
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "swift/AST/DiagnosticConsumer.h"
2424
#include "swift/AST/TypeLoc.h"
2525
#include "swift/Localization/LocalizationFormat.h"
26+
#include "llvm/ADT/BitVector.h"
2627
#include "llvm/ADT/StringRef.h"
2728
#include "llvm/ADT/StringSet.h"
2829
#include "llvm/Support/Allocator.h"
@@ -342,7 +343,19 @@ namespace swift {
342343
return ActorIsolationVal;
343344
}
344345
};
345-
346+
347+
/// Describes the current behavior to take with a diagnostic.
348+
/// Ordered from most severe to least.
349+
enum class DiagnosticBehavior : uint8_t {
350+
Unspecified = 0,
351+
Fatal,
352+
Error,
353+
Warning,
354+
Remark,
355+
Note,
356+
Ignore,
357+
};
358+
346359
struct DiagnosticFormatOptions {
347360
const std::string OpeningQuotationMark;
348361
const std::string ClosingQuotationMark;
@@ -393,6 +406,7 @@ namespace swift {
393406
SourceLoc Loc;
394407
bool IsChildNote = false;
395408
const swift::Decl *Decl = nullptr;
409+
DiagnosticBehavior BehaviorLimit = DiagnosticBehavior::Unspecified;
396410

397411
friend DiagnosticEngine;
398412

@@ -420,10 +434,12 @@ namespace swift {
420434
bool isChildNote() const { return IsChildNote; }
421435
SourceLoc getLoc() const { return Loc; }
422436
const class Decl *getDecl() const { return Decl; }
437+
DiagnosticBehavior getBehaviorLimit() const { return BehaviorLimit; }
423438

424439
void setLoc(SourceLoc loc) { Loc = loc; }
425440
void setIsChildNote(bool isChildNote) { IsChildNote = isChildNote; }
426441
void setDecl(const class Decl *decl) { Decl = decl; }
442+
void setBehaviorLimit(DiagnosticBehavior limit){ BehaviorLimit = limit; }
427443

428444
/// Returns true if this object represents a particular diagnostic.
429445
///
@@ -493,6 +509,11 @@ namespace swift {
493509
/// Flush the active diagnostic to the diagnostic output engine.
494510
void flush();
495511

512+
/// Prevent the diagnostic from behaving more severely than \p limit. For
513+
/// instance, if \c DiagnosticBehavior::Warning is passed, an error will be
514+
/// emitted as a warning, but a note will still be emitted as a note.
515+
InFlightDiagnostic &limitBehavior(DiagnosticBehavior limit);
516+
496517
/// Add a token-based range to the currently-active diagnostic.
497518
InFlightDiagnostic &highlight(SourceRange R);
498519

@@ -597,19 +618,6 @@ namespace swift {
597618
/// Class to track, map, and remap diagnostic severity and fatality
598619
///
599620
class DiagnosticState {
600-
public:
601-
/// Describes the current behavior to take with a diagnostic
602-
enum class Behavior : uint8_t {
603-
Unspecified,
604-
Ignore,
605-
Note,
606-
Remark,
607-
Warning,
608-
Error,
609-
Fatal,
610-
};
611-
612-
private:
613621
/// Whether we should continue to emit diagnostics, even after a
614622
/// fatal error
615623
bool showDiagnosticsAfterFatalError = false;
@@ -627,17 +635,17 @@ namespace swift {
627635
bool anyErrorOccurred = false;
628636

629637
/// Track the previous emitted Behavior, useful for notes
630-
Behavior previousBehavior = Behavior::Unspecified;
638+
DiagnosticBehavior previousBehavior = DiagnosticBehavior::Unspecified;
631639

632-
/// Track settable, per-diagnostic state that we store
633-
std::vector<Behavior> perDiagnosticBehavior;
640+
/// Track which diagnostics should be ignored.
641+
llvm::BitVector ignoredDiagnostics;
634642

635643
public:
636644
DiagnosticState();
637645

638646
/// Figure out the Behavior for the given diagnostic, taking current
639647
/// state such as fatality into account.
640-
Behavior determineBehavior(DiagID id);
648+
DiagnosticBehavior determineBehavior(const Diagnostic &diag);
641649

642650
bool hadAnyError() const { return anyErrorOccurred; }
643651
bool hasFatalErrorOccurred() const { return fatalErrorOccurred; }
@@ -662,9 +670,9 @@ namespace swift {
662670
fatalErrorOccurred = false;
663671
}
664672

665-
/// Set per-diagnostic behavior
666-
void setDiagnosticBehavior(DiagID id, Behavior behavior) {
667-
perDiagnosticBehavior[(unsigned)id] = behavior;
673+
/// Set whether a diagnostic should be ignored.
674+
void setIgnoredDiagnostic(DiagID id, bool ignored) {
675+
ignoredDiagnostics[(unsigned)id] = ignored;
668676
}
669677

670678
private:
@@ -810,7 +818,7 @@ namespace swift {
810818
}
811819

812820
void ignoreDiagnostic(DiagID id) {
813-
state.setDiagnosticBehavior(id, DiagnosticState::Behavior::Ignore);
821+
state.setIgnoredDiagnostic(id, true);
814822
}
815823

816824
void resetHadAnyError() {
@@ -1106,9 +1114,9 @@ namespace swift {
11061114
Engine.TentativeDiagnostics.end());
11071115

11081116
for (auto &diagnostic : diagnostics) {
1109-
auto behavior = Engine.state.determineBehavior(diagnostic.getID());
1110-
if (behavior == DiagnosticState::Behavior::Fatal ||
1111-
behavior == DiagnosticState::Behavior::Error)
1117+
auto behavior = Engine.state.determineBehavior(diagnostic);
1118+
if (behavior == DiagnosticBehavior::Fatal ||
1119+
behavior == DiagnosticBehavior::Error)
11121120
return true;
11131121
}
11141122

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4375,38 +4375,20 @@ ERROR(non_concurrent_type_member,none,
43754375
"%select{stored property %1|associated value %1}0 of "
43764376
"'ConcurrentValue'-conforming %2 %3 has non-concurrent-value type %4",
43774377
(bool, DeclName, DescriptiveDeclKind, DeclName, Type))
4378-
WARNING(non_concurrent_type_member_warn,none,
4379-
"%select{stored property %1|associated value %1}0 of "
4380-
"'ConcurrentValue'-conforming %2 %3 has non-concurrent-value type %4",
4381-
(bool, DeclName, DescriptiveDeclKind, DeclName, Type))
43824378
ERROR(concurrent_value_class_mutable_property,none,
43834379
"stored property %0 of 'ConcurrentValue'-conforming %1 %2 is mutable",
43844380
(DeclName, DescriptiveDeclKind, DeclName))
4385-
WARNING(concurrent_value_class_mutable_property_warn,none,
4386-
"stored property %0 of 'ConcurrentValue'-conforming %1 %2 is mutable",
4387-
(DeclName, DescriptiveDeclKind, DeclName))
43884381
ERROR(concurrent_value_outside_source_file,none,
43894382
"conformance to 'ConcurrentValue' must occur in the same source file as "
43904383
"%0 %1; use 'UnsafeConcurrentValue' for retroactive conformance",
43914384
(DescriptiveDeclKind, DeclName))
4392-
WARNING(concurrent_value_outside_source_file_warn,none,
4393-
"conformance to 'ConcurrentValue' must occur in the same source file as "
4394-
"%0 %1; use 'UnsafeConcurrentValue' for retroactive conformance",
4395-
(DescriptiveDeclKind, DeclName))
43964385
ERROR(concurrent_value_nonfinal_class,none,
43974386
"non-final class %0 cannot conform to `ConcurrentValue`; "
43984387
"use `UnsafeConcurrentValue`", (DeclName))
4399-
WARNING(concurrent_value_nonfinal_class_warn,none,
4400-
"non-final class %0 cannot conform to `ConcurrentValue`; "
4401-
"use `UnsafeConcurrentValue`", (DeclName))
44024388
ERROR(concurrent_value_inherit,none,
44034389
"`ConcurrentValue` class %1 cannot inherit from another class"
44044390
"%select{| other than 'NSObject'}0",
44054391
(bool, DeclName))
4406-
WARNING(concurrent_value_inherit_warn,none,
4407-
"`ConcurrentValue` class %1 cannot inherit from another class"
4408-
"%select{| other than 'NSObject'}0",
4409-
(bool, DeclName))
44104392

44114393
ERROR(actorindependent_let,none,
44124394
"'@actorIndependent' is meaningless on 'let' declarations because "

lib/AST/DiagnosticEngine.cpp

Lines changed: 77 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ static constexpr EducationalNotes<LocalDiagID::NumDiags> _EducationalNotes = Edu
136136
static constexpr auto educationalNotes = _EducationalNotes.value;
137137

138138
DiagnosticState::DiagnosticState() {
139-
// Initialize our per-diagnostic state to default
140-
perDiagnosticBehavior.resize(LocalDiagID::NumDiags, Behavior::Unspecified);
139+
// Initialize our ignored diagnostics to default
140+
ignoredDiagnostics.resize(LocalDiagID::NumDiags);
141141
}
142142

143143
static CharSourceRange toCharSourceRange(SourceManager &SM, SourceRange SR) {
@@ -289,6 +289,12 @@ InFlightDiagnostic &InFlightDiagnostic::fixItExchange(SourceRange R1,
289289
return *this;
290290
}
291291

292+
InFlightDiagnostic &
293+
InFlightDiagnostic::limitBehavior(DiagnosticBehavior limit) {
294+
Engine->getActiveDiagnostic().setBehaviorLimit(limit);
295+
return *this;
296+
}
297+
292298
void InFlightDiagnostic::flush() {
293299
if (!IsActive)
294300
return;
@@ -779,26 +785,41 @@ void DiagnosticEngine::formatDiagnosticText(
779785
}
780786
}
781787

782-
static DiagnosticKind toDiagnosticKind(DiagnosticState::Behavior behavior) {
788+
static DiagnosticKind toDiagnosticKind(DiagnosticBehavior behavior) {
783789
switch (behavior) {
784-
case DiagnosticState::Behavior::Unspecified:
790+
case DiagnosticBehavior::Unspecified:
785791
llvm_unreachable("unspecified behavior");
786-
case DiagnosticState::Behavior::Ignore:
792+
case DiagnosticBehavior::Ignore:
787793
llvm_unreachable("trying to map an ignored diagnostic");
788-
case DiagnosticState::Behavior::Error:
789-
case DiagnosticState::Behavior::Fatal:
794+
case DiagnosticBehavior::Error:
795+
case DiagnosticBehavior::Fatal:
790796
return DiagnosticKind::Error;
791-
case DiagnosticState::Behavior::Note:
797+
case DiagnosticBehavior::Note:
792798
return DiagnosticKind::Note;
793-
case DiagnosticState::Behavior::Warning:
799+
case DiagnosticBehavior::Warning:
794800
return DiagnosticKind::Warning;
795-
case DiagnosticState::Behavior::Remark:
801+
case DiagnosticBehavior::Remark:
796802
return DiagnosticKind::Remark;
797803
}
798804

799805
llvm_unreachable("Unhandled DiagnosticKind in switch.");
800806
}
801807

808+
static
809+
DiagnosticBehavior toDiagnosticBehavior(DiagnosticKind kind, bool isFatal) {
810+
switch (kind) {
811+
case DiagnosticKind::Note:
812+
return DiagnosticBehavior::Note;
813+
case DiagnosticKind::Error:
814+
return isFatal ? DiagnosticBehavior::Fatal : DiagnosticBehavior::Error;
815+
case DiagnosticKind::Warning:
816+
return DiagnosticBehavior::Warning;
817+
case DiagnosticKind::Remark:
818+
return DiagnosticBehavior::Remark;
819+
}
820+
llvm_unreachable("Unhandled DiagnosticKind in switch.");
821+
}
822+
802823
// A special option only for compiler writers that causes Diagnostics to assert
803824
// when a failure diagnostic is emitted. Intended for use in the debugger.
804825
llvm::cl::opt<bool> AssertOnError("swift-diagnostics-assert-on-error",
@@ -808,72 +829,63 @@ llvm::cl::opt<bool> AssertOnError("swift-diagnostics-assert-on-error",
808829
llvm::cl::opt<bool> AssertOnWarning("swift-diagnostics-assert-on-warning",
809830
llvm::cl::init(false));
810831

811-
DiagnosticState::Behavior DiagnosticState::determineBehavior(DiagID id) {
812-
auto set = [this](DiagnosticState::Behavior lvl) {
813-
if (lvl == Behavior::Fatal) {
814-
fatalErrorOccurred = true;
815-
anyErrorOccurred = true;
816-
} else if (lvl == Behavior::Error) {
817-
anyErrorOccurred = true;
818-
}
819-
820-
assert((!AssertOnError || !anyErrorOccurred) && "We emitted an error?!");
821-
assert((!AssertOnWarning || (lvl != Behavior::Warning)) &&
822-
"We emitted a warning?!");
823-
previousBehavior = lvl;
824-
return lvl;
825-
};
826-
832+
DiagnosticBehavior DiagnosticState::determineBehavior(const Diagnostic &diag) {
827833
// We determine how to handle a diagnostic based on the following rules
828-
// 1) If current state dictates a certain behavior, follow that
829-
// 2) If the user provided a behavior for this specific diagnostic, follow
830-
// that
831-
// 3) If the user provided a behavior for this diagnostic's kind, follow
832-
// that
833-
// 4) Otherwise remap the diagnostic kind
834-
835-
auto diagInfo = storedDiagnosticInfos[(unsigned)id];
836-
bool isNote = diagInfo.kind == DiagnosticKind::Note;
837-
838-
// 1) If current state dictates a certain behavior, follow that
834+
// 1) Map the diagnostic to its "intended" behavior, applying the behavior
835+
// limit for this particular emission
836+
// 2) If current state dictates a certain behavior, follow that
837+
// 3) If the user ignored this specific diagnostic, follow that
838+
// 4) If the user substituted a different behavior for this behavior, apply
839+
// that change
840+
// 5) Update current state for use during the next diagnostic
841+
842+
// 1) Map the diagnostic to its "intended" behavior, applying the behavior
843+
// limit for this particular emission
844+
auto diagInfo = storedDiagnosticInfos[(unsigned)diag.getID()];
845+
DiagnosticBehavior lvl =
846+
std::max(toDiagnosticBehavior(diagInfo.kind, diagInfo.isFatal),
847+
diag.getBehaviorLimit());
848+
assert(lvl != DiagnosticBehavior::Unspecified);
849+
850+
// 2) If current state dictates a certain behavior, follow that
839851

840852
// Notes relating to ignored diagnostics should also be ignored
841-
if (previousBehavior == Behavior::Ignore && isNote)
842-
return set(Behavior::Ignore);
853+
if (previousBehavior == DiagnosticBehavior::Ignore
854+
&& lvl == DiagnosticBehavior::Note)
855+
lvl = DiagnosticBehavior::Ignore;
843856

844857
// Suppress diagnostics when in a fatal state, except for follow-on notes
845858
if (fatalErrorOccurred)
846-
if (!showDiagnosticsAfterFatalError && !isNote)
847-
return set(Behavior::Ignore);
859+
if (!showDiagnosticsAfterFatalError && lvl != DiagnosticBehavior::Note)
860+
lvl = DiagnosticBehavior::Ignore;
848861

849-
// 2) If the user provided a behavior for this specific diagnostic, follow
850-
// that
862+
// 3) If the user ignored this specific diagnostic, follow that
863+
if (ignoredDiagnostics[(unsigned)diag.getID()])
864+
lvl = DiagnosticBehavior::Ignore;
851865

852-
if (perDiagnosticBehavior[(unsigned)id] != Behavior::Unspecified)
853-
return set(perDiagnosticBehavior[(unsigned)id]);
854-
855-
// 3) If the user provided a behavior for this diagnostic's kind, follow
856-
// that
857-
if (diagInfo.kind == DiagnosticKind::Warning) {
858-
if (suppressWarnings)
859-
return set(Behavior::Ignore);
866+
// 4) If the user substituted a different behavior for this behavior, apply
867+
// that change
868+
if (lvl == DiagnosticBehavior::Warning) {
860869
if (warningsAsErrors)
861-
return set(Behavior::Error);
870+
lvl = DiagnosticBehavior::Error;
871+
if (suppressWarnings)
872+
lvl = DiagnosticBehavior::Ignore;
862873
}
863874

864-
// 4) Otherwise remap the diagnostic kind
865-
switch (diagInfo.kind) {
866-
case DiagnosticKind::Note:
867-
return set(Behavior::Note);
868-
case DiagnosticKind::Error:
869-
return set(diagInfo.isFatal ? Behavior::Fatal : Behavior::Error);
870-
case DiagnosticKind::Warning:
871-
return set(Behavior::Warning);
872-
case DiagnosticKind::Remark:
873-
return set(Behavior::Remark);
875+
// 5) Update current state for use during the next diagnostic
876+
if (lvl == DiagnosticBehavior::Fatal) {
877+
fatalErrorOccurred = true;
878+
anyErrorOccurred = true;
879+
} else if (lvl == DiagnosticBehavior::Error) {
880+
anyErrorOccurred = true;
874881
}
875882

876-
llvm_unreachable("Unhandled DiagnosticKind in switch.");
883+
assert((!AssertOnError || !anyErrorOccurred) && "We emitted an error?!");
884+
assert((!AssertOnWarning || (lvl != DiagnosticBehavior::Warning)) &&
885+
"We emitted a warning?!");
886+
887+
previousBehavior = lvl;
888+
return lvl;
877889
}
878890

879891
void DiagnosticEngine::flushActiveDiagnostic() {
@@ -910,8 +922,8 @@ static AccessLevel getBufferAccessLevel(const Decl *decl) {
910922

911923
Optional<DiagnosticInfo>
912924
DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic) {
913-
auto behavior = state.determineBehavior(diagnostic.getID());
914-
if (behavior == DiagnosticState::Behavior::Ignore)
925+
auto behavior = state.determineBehavior(diagnostic);
926+
if (behavior == DiagnosticBehavior::Ignore)
915927
return None;
916928

917929
// Figure out the source location.

0 commit comments

Comments
 (0)