Skip to content

Commit c58fe89

Browse files
committed
[NFC] Add InFlightDiagnostic::limitBehavior()
This allows code to reduce the “severity” of a diagnostic at one particular emission site, so that (for instance) an error can be emitted as a warning in some situations. It also changes some code in TypeCheckConcurrency.cpp to use this new feature, allowing us to delete some redundant diagnostic definitions.
1 parent 4a01b56 commit c58fe89

File tree

4 files changed

+78
-77
lines changed

4 files changed

+78
-77
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -344,15 +344,16 @@ namespace swift {
344344
}
345345
};
346346

347-
/// Describes the current behavior to take with a diagnostic
347+
/// Describes the current behavior to take with a diagnostic.
348+
/// Ordered from most severe to least.
348349
enum class DiagnosticBehavior : uint8_t {
349-
Unspecified,
350-
Ignore,
351-
Note,
352-
Remark,
353-
Warning,
354-
Error,
350+
Unspecified = 0,
355351
Fatal,
352+
Error,
353+
Warning,
354+
Remark,
355+
Note,
356+
Ignore,
356357
};
357358

358359
struct DiagnosticFormatOptions {
@@ -405,6 +406,7 @@ namespace swift {
405406
SourceLoc Loc;
406407
bool IsChildNote = false;
407408
const swift::Decl *Decl = nullptr;
409+
DiagnosticBehavior BehaviorLimit = DiagnosticBehavior::Unspecified;
408410

409411
friend DiagnosticEngine;
410412

@@ -432,10 +434,12 @@ namespace swift {
432434
bool isChildNote() const { return IsChildNote; }
433435
SourceLoc getLoc() const { return Loc; }
434436
const class Decl *getDecl() const { return Decl; }
437+
DiagnosticBehavior getBehaviorLimit() const { return BehaviorLimit; }
435438

436439
void setLoc(SourceLoc loc) { Loc = loc; }
437440
void setIsChildNote(bool isChildNote) { IsChildNote = isChildNote; }
438441
void setDecl(const class Decl *decl) { Decl = decl; }
442+
void setBehaviorLimit(DiagnosticBehavior limit){ BehaviorLimit = limit; }
439443

440444
/// Returns true if this object represents a particular diagnostic.
441445
///
@@ -505,6 +509,11 @@ namespace swift {
505509
/// Flush the active diagnostic to the diagnostic output engine.
506510
void flush();
507511

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+
508517
/// Add a token-based range to the currently-active diagnostic.
509518
InFlightDiagnostic &highlight(SourceRange R);
510519

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4364,38 +4364,20 @@ ERROR(non_concurrent_type_member,none,
43644364
"%select{stored property %1|associated value %1}0 of "
43654365
"'ConcurrentValue'-conforming %2 %3 has non-concurrent-value type %4",
43664366
(bool, DeclName, DescriptiveDeclKind, DeclName, Type))
4367-
WARNING(non_concurrent_type_member_warn,none,
4368-
"%select{stored property %1|associated value %1}0 of "
4369-
"'ConcurrentValue'-conforming %2 %3 has non-concurrent-value type %4",
4370-
(bool, DeclName, DescriptiveDeclKind, DeclName, Type))
43714367
ERROR(concurrent_value_class_mutable_property,none,
43724368
"stored property %0 of 'ConcurrentValue'-conforming %1 %2 is mutable",
43734369
(DeclName, DescriptiveDeclKind, DeclName))
4374-
WARNING(concurrent_value_class_mutable_property_warn,none,
4375-
"stored property %0 of 'ConcurrentValue'-conforming %1 %2 is mutable",
4376-
(DeclName, DescriptiveDeclKind, DeclName))
43774370
ERROR(concurrent_value_outside_source_file,none,
43784371
"conformance to 'ConcurrentValue' must occur in the same source file as "
43794372
"%0 %1; use 'UnsafeConcurrentValue' for retroactive conformance",
43804373
(DescriptiveDeclKind, DeclName))
4381-
WARNING(concurrent_value_outside_source_file_warn,none,
4382-
"conformance to 'ConcurrentValue' must occur in the same source file as "
4383-
"%0 %1; use 'UnsafeConcurrentValue' for retroactive conformance",
4384-
(DescriptiveDeclKind, DeclName))
43854374
ERROR(concurrent_value_nonfinal_class,none,
43864375
"non-final class %0 cannot conform to `ConcurrentValue`; "
43874376
"use `UnsafeConcurrentValue`", (DeclName))
4388-
WARNING(concurrent_value_nonfinal_class_warn,none,
4389-
"non-final class %0 cannot conform to `ConcurrentValue`; "
4390-
"use `UnsafeConcurrentValue`", (DeclName))
43914377
ERROR(concurrent_value_inherit,none,
43924378
"`ConcurrentValue` class %1 cannot inherit from another class"
43934379
"%select{| other than 'NSObject'}0",
43944380
(bool, DeclName))
4395-
WARNING(concurrent_value_inherit_warn,none,
4396-
"`ConcurrentValue` class %1 cannot inherit from another class"
4397-
"%select{| other than 'NSObject'}0",
4398-
(bool, DeclName))
43994381

44004382
ERROR(actorindependent_let,none,
44014383
"'@actorIndependent' is meaningless on 'let' declarations because "

lib/AST/DiagnosticEngine.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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;
@@ -828,10 +834,11 @@ DiagnosticBehavior DiagnosticState::determineBehavior(const Diagnostic &diag) {
828834
// 2) If the user ignored this specific diagnostic, follow that
829835
// 3) If the user provided a behavior for this diagnostic's kind, follow
830836
// that
831-
// 4) Otherwise remap the diagnostic kind
837+
// 4) Otherwise remap the diagnostic kind, applying the behaviorLimit
832838

833839
auto diagInfo = storedDiagnosticInfos[(unsigned)diag.getID()];
834-
bool isNote = diagInfo.kind == DiagnosticKind::Note;
840+
bool isNote = diagInfo.kind == DiagnosticKind::Note
841+
|| diag.getBehaviorLimit() == DiagnosticBehavior::Note;
835842

836843
// 1) If current state dictates a certain behavior, follow that
837844

@@ -850,27 +857,34 @@ DiagnosticBehavior DiagnosticState::determineBehavior(const Diagnostic &diag) {
850857

851858
// 3) If the user provided a behavior for this diagnostic's kind, follow
852859
// that
853-
if (diagInfo.kind == DiagnosticKind::Warning) {
860+
if (diagInfo.kind == DiagnosticKind::Warning
861+
|| (diag.getBehaviorLimit() == DiagnosticBehavior::Warning && !isNote)) {
854862
if (suppressWarnings)
855863
return set(DiagnosticBehavior::Ignore);
856864
if (warningsAsErrors)
857865
return set(DiagnosticBehavior::Error);
858866
}
859867

860-
// 4) Otherwise remap the diagnostic kind
868+
// 4) Otherwise remap the diagnostic kind, applying the behaviorLimit
869+
DiagnosticBehavior lvl = DiagnosticBehavior::Unspecified;
861870
switch (diagInfo.kind) {
862871
case DiagnosticKind::Note:
863-
return set(DiagnosticBehavior::Note);
872+
lvl = DiagnosticBehavior::Note;
873+
break;
864874
case DiagnosticKind::Error:
865-
return set(diagInfo.isFatal ? DiagnosticBehavior::Fatal
866-
: DiagnosticBehavior::Error);
875+
lvl = diagInfo.isFatal ? DiagnosticBehavior::Fatal
876+
: DiagnosticBehavior::Error;
877+
break;
867878
case DiagnosticKind::Warning:
868-
return set(DiagnosticBehavior::Warning);
879+
lvl = DiagnosticBehavior::Warning;
880+
break;
869881
case DiagnosticKind::Remark:
870-
return set(DiagnosticBehavior::Remark);
882+
lvl = DiagnosticBehavior::Remark;
883+
break;
871884
}
885+
assert(lvl != DiagnosticBehavior::Unspecified);
872886

873-
llvm_unreachable("Unhandled DiagnosticKind in switch.");
887+
return set(std::max(lvl, diag.getBehaviorLimit()));
874888
}
875889

876890
void DiagnosticEngine::flushActiveDiagnostic() {

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 38 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2371,14 +2371,16 @@ static bool shouldDiagnoseExistingDataRaces(const DeclContext *dc) {
23712371
return false;
23722372
}
23732373

2374-
static bool shouldDiagnoseConcurrentValue(ConcurrentValueCheck check) {
2374+
static DiagnosticBehavior toDiagnosticBehavior(ConcurrentValueCheck check,
2375+
bool diagnoseImplicit = false) {
23752376
switch (check) {
23762377
case ConcurrentValueCheck::ImpliedByStandardProtocol:
2378+
return DiagnosticBehavior::Warning;
23772379
case ConcurrentValueCheck::Explicit:
2378-
return true;
2379-
2380+
return DiagnosticBehavior::Unspecified;
23802381
case ConcurrentValueCheck::Implicit:
2381-
return false;
2382+
return diagnoseImplicit ? DiagnosticBehavior::Unspecified
2383+
: DiagnosticBehavior::Ignore;
23822384
}
23832385
}
23842386

@@ -2388,34 +2390,31 @@ static bool checkConcurrentValueInstanceStorage(
23882390
NominalTypeDecl *nominal, DeclContext *dc, ConcurrentValueCheck check) {
23892391
// Stored properties of structs and classes must have
23902392
// ConcurrentValue-conforming types.
2391-
bool asWarning = (check == ConcurrentValueCheck::ImpliedByStandardProtocol);
2393+
auto behavior = toDiagnosticBehavior(check);
23922394
bool invalid = false;
23932395
if (isa<StructDecl>(nominal) || isa<ClassDecl>(nominal)) {
23942396
auto classDecl = dyn_cast<ClassDecl>(nominal);
23952397
for (auto property : nominal->getStoredProperties()) {
23962398
if (classDecl && property->supportsMutation()) {
2397-
if (!shouldDiagnoseConcurrentValue(check))
2399+
if (behavior == DiagnosticBehavior::Ignore)
23982400
return true;
2399-
2400-
property->diagnose(
2401-
asWarning ? diag::concurrent_value_class_mutable_property_warn
2402-
: diag::concurrent_value_class_mutable_property,
2403-
property->getName(), nominal->getDescriptiveKind(),
2404-
nominal->getName());
2401+
property->diagnose(diag::concurrent_value_class_mutable_property,
2402+
property->getName(), nominal->getDescriptiveKind(),
2403+
nominal->getName())
2404+
.limitBehavior(behavior);
24052405
invalid = true;
24062406
continue;
24072407
}
24082408

24092409
auto propertyType = dc->mapTypeIntoContext(property->getInterfaceType());
24102410
if (!isConcurrentValueType(dc, propertyType)) {
2411-
if (!shouldDiagnoseConcurrentValue(check))
2411+
if (behavior == DiagnosticBehavior::Ignore)
24122412
return true;
2413-
2414-
property->diagnose(
2415-
asWarning ? diag::non_concurrent_type_member_warn
2416-
: diag::non_concurrent_type_member,
2417-
false, property->getName(),
2418-
nominal->getDescriptiveKind(), nominal->getName(), propertyType);
2413+
property->diagnose(diag::non_concurrent_type_member,
2414+
false, property->getName(),
2415+
nominal->getDescriptiveKind(), nominal->getName(),
2416+
propertyType)
2417+
.limitBehavior(behavior);
24192418
invalid = true;
24202419
continue;
24212420
}
@@ -2435,14 +2434,13 @@ static bool checkConcurrentValueInstanceStorage(
24352434
auto elementType = dc->mapTypeIntoContext(
24362435
element->getArgumentInterfaceType());
24372436
if (!isConcurrentValueType(dc, elementType)) {
2438-
if (!shouldDiagnoseConcurrentValue(check))
2437+
if (behavior == DiagnosticBehavior::Ignore)
24392438
return true;
2440-
2441-
element->diagnose(
2442-
asWarning ? diag::non_concurrent_type_member_warn
2443-
: diag::non_concurrent_type_member,
2444-
true, element->getName(),
2445-
nominal->getDescriptiveKind(), nominal->getName(), elementType);
2439+
element->diagnose(diag::non_concurrent_type_member,
2440+
true, element->getName(),
2441+
nominal->getDescriptiveKind(), nominal->getName(),
2442+
elementType)
2443+
.limitBehavior(behavior);
24462444
invalid = true;
24472445
continue;
24482446
}
@@ -2469,28 +2467,26 @@ bool swift::checkConcurrentValueConformance(
24692467

24702468
// ConcurrentValue can only be used in the same source file.
24712469
auto conformanceDecl = conformanceDC->getAsDecl();
2472-
bool asWarning = (check == ConcurrentValueCheck::ImpliedByStandardProtocol);
2470+
auto behavior = toDiagnosticBehavior(check, /*diagnoseImplicit=*/true);
24732471
if (!conformanceDC->getParentSourceFile() ||
24742472
conformanceDC->getParentSourceFile() != nominal->getParentSourceFile()) {
2475-
conformanceDecl->diagnose(
2476-
asWarning
2477-
? diag::concurrent_value_outside_source_file_warn
2478-
: diag::concurrent_value_outside_source_file,
2479-
nominal->getDescriptiveKind(), nominal->getName());
2473+
conformanceDecl->diagnose(diag::concurrent_value_outside_source_file,
2474+
nominal->getDescriptiveKind(),
2475+
nominal->getName())
2476+
.limitBehavior(behavior);
24802477

2481-
if (!asWarning)
2478+
if (behavior != DiagnosticBehavior::Warning)
24822479
return true;
24832480
}
24842481

24852482
if (classDecl) {
24862483
// An non-final class cannot conform to `ConcurrentValue`.
24872484
if (!classDecl->isFinal()) {
2488-
classDecl->diagnose(
2489-
asWarning ? diag::concurrent_value_nonfinal_class_warn
2490-
: diag::concurrent_value_nonfinal_class,
2491-
classDecl->getName());
2485+
classDecl->diagnose(diag::concurrent_value_nonfinal_class,
2486+
classDecl->getName())
2487+
.limitBehavior(behavior);
24922488

2493-
if (!asWarning)
2489+
if (behavior != DiagnosticBehavior::Warning)
24942490
return true;
24952491
}
24962492

@@ -2500,12 +2496,12 @@ bool swift::checkConcurrentValueConformance(
25002496
if (auto superclassDecl = classDecl->getSuperclassDecl()) {
25012497
if (!superclassDecl->isNSObject()) {
25022498
classDecl->diagnose(
2503-
asWarning ? diag::concurrent_value_inherit_warn
2504-
: diag::concurrent_value_inherit,
2499+
diag::concurrent_value_inherit,
25052500
nominal->getASTContext().LangOpts.EnableObjCInterop,
2506-
classDecl->getName());
2501+
classDecl->getName())
2502+
.limitBehavior(behavior);
25072503

2508-
if (!asWarning)
2504+
if (behavior != DiagnosticBehavior::Warning)
25092505
return true;
25102506
}
25112507
}

0 commit comments

Comments
 (0)