Skip to content

Commit ec7269d

Browse files
committed
Sema: Clean up diagnoseMissingWitnesses()
1 parent 35b3f9b commit ec7269d

File tree

5 files changed

+136
-123
lines changed

5 files changed

+136
-123
lines changed

lib/AST/ASTContext.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2839,6 +2839,8 @@ void ASTContext::addDelayedConformanceDiag(
28392839
void ASTContext::addDelayedMissingWitness(
28402840
NormalProtocolConformance *conformance,
28412841
ASTContext::MissingWitness missingWitness) {
2842+
conformance->setInvalid();
2843+
28422844
auto &diagnostics = getImpl().DelayedConformanceDiags[conformance];
28432845
maybeEmitFallbackConformanceDiagnostic(*this, conformance, diagnostics);
28442846
diagnostics.MissingWitnesses.push_back(missingWitness);

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 127 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -3681,6 +3681,118 @@ static ArrayRef<ASTContext::MissingWitness> pruneMissingWitnesses(
36813681
return missingWitnesses;
36823682
}
36833683

3684+
static void diagnoseProtocolStubFixit(
3685+
NormalProtocolConformance *Conf,
3686+
llvm::SmallVector<ASTContext::MissingWitness, 4> MissingWitnesses) {
3687+
DeclContext *DC = Conf->getDeclContext();
3688+
3689+
auto &Ctx = DC->getASTContext();
3690+
3691+
SourceLoc ComplainLoc = Conf->getLoc();
3692+
3693+
// The location where to insert stubs.
3694+
SourceLoc FixitLocation;
3695+
3696+
// The location where the type starts.
3697+
SourceLoc TypeLoc;
3698+
if (auto Extension = dyn_cast<ExtensionDecl>(DC)) {
3699+
FixitLocation = Extension->getBraces().Start;
3700+
TypeLoc = Extension->getStartLoc();
3701+
} else if (auto Nominal = dyn_cast<NominalTypeDecl>(DC)) {
3702+
FixitLocation = Nominal->getBraces().Start;
3703+
TypeLoc = Nominal->getStartLoc();
3704+
} else {
3705+
llvm_unreachable("Unknown adopter kind");
3706+
}
3707+
std::string FixIt;
3708+
llvm::SetVector<ValueDecl*> NoStubRequirements;
3709+
3710+
// Print stubs for all known missing witnesses.
3711+
printProtocolStubFixitString(TypeLoc, Conf, MissingWitnesses, FixIt,
3712+
NoStubRequirements);
3713+
auto &Diags = Ctx.Diags;
3714+
3715+
// If we are in editor mode, squash all notes into a single fixit.
3716+
if (Ctx.LangOpts.DiagnosticsEditorMode) {
3717+
if (!FixIt.empty()) {
3718+
Diags.diagnose(ComplainLoc, diag::missing_witnesses_general).
3719+
fixItInsertAfter(FixitLocation, FixIt);
3720+
}
3721+
return;
3722+
}
3723+
auto &SM = Ctx.SourceMgr;
3724+
auto FixitBufferId = SM.findBufferContainingLoc(FixitLocation);
3725+
for (const auto &Missing : MissingWitnesses) {
3726+
auto VD = Missing.requirement;
3727+
3728+
// Don't ever emit a diagnostic for a requirement in the NSObject
3729+
// protocol. They're not implementable.
3730+
if (isNSObjectProtocol(VD->getDeclContext()->getSelfProtocolDecl()))
3731+
continue;
3732+
3733+
// Whether this VD has a stub printed.
3734+
bool AddFixit = !NoStubRequirements.count(VD);
3735+
bool SameFile = VD->getLoc().isValid() ?
3736+
SM.findBufferContainingLoc(VD->getLoc()) == FixitBufferId : false;
3737+
3738+
// Issue diagnostics for witness types.
3739+
if (auto MissingTypeWitness = dyn_cast<AssociatedTypeDecl>(VD)) {
3740+
llvm::Optional<InFlightDiagnostic> diag;
3741+
if (isa<BuiltinTupleDecl>(DC->getSelfNominalTypeDecl())) {
3742+
auto expectedTy = getTupleConformanceTypeWitness(DC, MissingTypeWitness);
3743+
diag.emplace(Diags.diagnose(MissingTypeWitness, diag::no_witnesses_type_tuple,
3744+
MissingTypeWitness, expectedTy));
3745+
} else {
3746+
diag.emplace(Diags.diagnose(MissingTypeWitness, diag::no_witnesses_type,
3747+
MissingTypeWitness));
3748+
}
3749+
if (SameFile) {
3750+
// If the protocol member decl is in the same file of the stub,
3751+
// we can directly associate the fixit with the note issued to the
3752+
// requirement.
3753+
diag->fixItInsertAfter(FixitLocation, FixIt);
3754+
} else {
3755+
diag.value().flush();
3756+
3757+
// Otherwise, we have to issue another note to carry the fixit,
3758+
// because editor may assume the fixit is in the same file with the note.
3759+
if (Ctx.LangOpts.DiagnosticsEditorMode) {
3760+
Diags.diagnose(ComplainLoc, diag::missing_witnesses_general)
3761+
.fixItInsertAfter(FixitLocation, FixIt);
3762+
}
3763+
}
3764+
continue;
3765+
}
3766+
3767+
// Issue diagnostics for witness values.
3768+
Type RequirementType =
3769+
getRequirementTypeForDisplay(DC->getParentModule(), Conf, VD);
3770+
if (AddFixit) {
3771+
if (SameFile) {
3772+
// If the protocol member decl is in the same file of the stub,
3773+
// we can directly associate the fixit with the note issued to the
3774+
// requirement.
3775+
Diags
3776+
.diagnose(VD, diag::no_witnesses, getProtocolRequirementKind(VD),
3777+
VD, RequirementType, true)
3778+
.fixItInsertAfter(FixitLocation, FixIt);
3779+
} else {
3780+
// Otherwise, we have to issue another note to carry the fixit,
3781+
// because editor may assume the fixit is in the same file with the note.
3782+
Diags.diagnose(VD, diag::no_witnesses, getProtocolRequirementKind(VD),
3783+
VD, RequirementType, false);
3784+
if (Ctx.LangOpts.DiagnosticsEditorMode) {
3785+
Diags.diagnose(ComplainLoc, diag::missing_witnesses_general)
3786+
.fixItInsertAfter(FixitLocation, FixIt);
3787+
}
3788+
}
3789+
} else {
3790+
Diags.diagnose(VD, diag::no_witnesses, getProtocolRequirementKind(VD),
3791+
VD, RequirementType, true);
3792+
}
3793+
}
3794+
}
3795+
36843796
bool ConformanceChecker::
36853797
diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind, bool Delayed) {
36863798
auto LocalMissing = getLocalMissingWitness();
@@ -3693,6 +3805,16 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind, bool Delayed) {
36933805
if (LocalMissing.empty())
36943806
return false;
36953807

3808+
if (Delayed) {
3809+
// If the diagnostics are suppressed, we register these missing witnesses
3810+
// for later revisiting.
3811+
for (auto Missing : LocalMissing) {
3812+
getASTContext().addDelayedMissingWitness(Conformance, Missing);
3813+
}
3814+
3815+
return true;
3816+
}
3817+
36963818
// Diagnose the missing witnesses.
36973819
for (auto &Missing : LocalMissing) {
36983820
auto requirement = Missing.requirement;
@@ -3712,132 +3834,14 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind, bool Delayed) {
37123834
});
37133835
}
37143836

3715-
const auto InsertFixit = [](
3716-
NormalProtocolConformance *Conf, SourceLoc ComplainLoc, bool EditorMode,
3717-
llvm::SmallVector<ASTContext::MissingWitness, 4> MissingWitnesses) {
3718-
DeclContext *DC = Conf->getDeclContext();
3719-
// The location where to insert stubs.
3720-
SourceLoc FixitLocation;
3721-
3722-
// The location where the type starts.
3723-
SourceLoc TypeLoc;
3724-
if (auto Extension = dyn_cast<ExtensionDecl>(DC)) {
3725-
FixitLocation = Extension->getBraces().Start;
3726-
TypeLoc = Extension->getStartLoc();
3727-
} else if (auto Nominal = dyn_cast<NominalTypeDecl>(DC)) {
3728-
FixitLocation = Nominal->getBraces().Start;
3729-
TypeLoc = Nominal->getStartLoc();
3730-
} else {
3731-
llvm_unreachable("Unknown adopter kind");
3732-
}
3733-
std::string FixIt;
3734-
llvm::SetVector<ValueDecl*> NoStubRequirements;
3735-
3736-
// Print stubs for all known missing witnesses.
3737-
printProtocolStubFixitString(TypeLoc, Conf, MissingWitnesses, FixIt,
3738-
NoStubRequirements);
3739-
auto &Diags = DC->getASTContext().Diags;
3740-
3741-
// If we are in editor mode, squash all notes into a single fixit.
3742-
if (EditorMode) {
3743-
if (!FixIt.empty()) {
3744-
Diags.diagnose(ComplainLoc, diag::missing_witnesses_general).
3745-
fixItInsertAfter(FixitLocation, FixIt);
3746-
}
3747-
return;
3748-
}
3749-
auto &SM = DC->getASTContext().SourceMgr;
3750-
auto FixitBufferId = SM.findBufferContainingLoc(FixitLocation);
3751-
for (const auto &Missing : MissingWitnesses) {
3752-
auto VD = Missing.requirement;
3753-
3754-
// Don't ever emit a diagnostic for a requirement in the NSObject
3755-
// protocol. They're not implementable.
3756-
if (isNSObjectProtocol(VD->getDeclContext()->getSelfProtocolDecl()))
3757-
continue;
3758-
3759-
// Whether this VD has a stub printed.
3760-
bool AddFixit = !NoStubRequirements.count(VD);
3761-
bool SameFile = VD->getLoc().isValid() ?
3762-
SM.findBufferContainingLoc(VD->getLoc()) == FixitBufferId : false;
3763-
3764-
// Issue diagnostics for witness types.
3765-
if (auto MissingTypeWitness = dyn_cast<AssociatedTypeDecl>(VD)) {
3766-
llvm::Optional<InFlightDiagnostic> diag;
3767-
if (isa<BuiltinTupleDecl>(DC->getSelfNominalTypeDecl())) {
3768-
auto expectedTy = getTupleConformanceTypeWitness(DC, MissingTypeWitness);
3769-
diag.emplace(Diags.diagnose(MissingTypeWitness, diag::no_witnesses_type_tuple,
3770-
MissingTypeWitness, expectedTy));
3771-
} else {
3772-
diag.emplace(Diags.diagnose(MissingTypeWitness, diag::no_witnesses_type,
3773-
MissingTypeWitness));
3774-
}
3775-
if (SameFile) {
3776-
// If the protocol member decl is in the same file of the stub,
3777-
// we can directly associate the fixit with the note issued to the
3778-
// requirement.
3779-
diag->fixItInsertAfter(FixitLocation, FixIt);
3780-
} else {
3781-
diag.value().flush();
3782-
3783-
// Otherwise, we have to issue another note to carry the fixit,
3784-
// because editor may assume the fixit is in the same file with the note.
3785-
if (EditorMode) {
3786-
Diags.diagnose(ComplainLoc, diag::missing_witnesses_general)
3787-
.fixItInsertAfter(FixitLocation, FixIt);
3788-
}
3789-
}
3790-
continue;
3791-
}
3792-
3793-
// Issue diagnostics for witness values.
3794-
Type RequirementType =
3795-
getRequirementTypeForDisplay(DC->getParentModule(), Conf, VD);
3796-
if (AddFixit) {
3797-
if (SameFile) {
3798-
// If the protocol member decl is in the same file of the stub,
3799-
// we can directly associate the fixit with the note issued to the
3800-
// requirement.
3801-
Diags
3802-
.diagnose(VD, diag::no_witnesses, getProtocolRequirementKind(VD),
3803-
VD, RequirementType, true)
3804-
.fixItInsertAfter(FixitLocation, FixIt);
3805-
} else {
3806-
// Otherwise, we have to issue another note to carry the fixit,
3807-
// because editor may assume the fixit is in the same file with the note.
3808-
Diags.diagnose(VD, diag::no_witnesses, getProtocolRequirementKind(VD),
3809-
VD, RequirementType, false);
3810-
if (EditorMode) {
3811-
Diags.diagnose(ComplainLoc, diag::missing_witnesses_general)
3812-
.fixItInsertAfter(FixitLocation, FixIt);
3813-
}
3814-
}
3815-
} else {
3816-
Diags.diagnose(VD, diag::no_witnesses, getProtocolRequirementKind(VD),
3817-
VD, RequirementType, true);
3818-
}
3819-
}
3820-
};
3821-
3822-
const bool IsEditorMode = Context.LangOpts.DiagnosticsEditorMode;
38233837
switch (Kind) {
38243838
case MissingWitnessDiagnosisKind::ErrorFixIt: {
38253839
const auto MissingWitnesses = filterProtocolRequirements(
38263840
GlobalMissingWitnesses.getArrayRef(), Adoptee);
3827-
if (Delayed) {
3828-
// If the diagnostics are suppressed, we register these missing witnesses
3829-
// for later revisiting.
3830-
Conformance->setInvalid();
3831-
for (auto Missing : MissingWitnesses) {
3832-
getASTContext().addDelayedMissingWitness(Conformance, Missing);
3833-
}
3834-
} else {
3835-
auto Loc = this->Loc;
3836-
getASTContext().addDelayedConformanceDiag(Conformance, true,
3837-
[InsertFixit, Loc, IsEditorMode, MissingWitnesses](NormalProtocolConformance *Conf) {
3838-
InsertFixit(Conf, Loc, IsEditorMode, MissingWitnesses);
3839-
});
3840-
}
3841+
getASTContext().addDelayedConformanceDiag(Conformance, true,
3842+
[MissingWitnesses](NormalProtocolConformance *Conf) {
3843+
diagnoseProtocolStubFixit(Conf, MissingWitnesses);
3844+
});
38413845
clearGlobalMissingWitnesses();
38423846
return true;
38433847
}
@@ -3847,7 +3851,7 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind, bool Delayed) {
38473851
return true;
38483852
}
38493853
case MissingWitnessDiagnosisKind::FixItOnly:
3850-
InsertFixit(Conformance, Loc, IsEditorMode,
3854+
diagnoseProtocolStubFixit(Conformance,
38513855
filterProtocolRequirements(GlobalMissingWitnesses.getArrayRef(),
38523856
Adoptee));
38533857
clearGlobalMissingWitnesses();

test/Distributed/distributed_actor_system_missing_type_no_crash.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ distributed actor Fish {
1111
// expected-error@-1{{distributed actor 'Fish' does not declare ActorSystem it can be used with}}
1212
// expected-error@-2{{distributed actor 'Fish' does not declare ActorSystem it can be used with}}
1313
// expected-note@-3{{you can provide a module-wide default actor system by declaring:\ntypealias DefaultDistributedActorSystem = <#ConcreteActorSystem#>}}
14+
// expected-error@-4{{type 'Fish' does not conform to protocol 'Encodable'}}
15+
// expected-error@-5{{type 'Fish' does not conform to protocol 'Decodable'}}
1416

1517
distributed func tell(_ text: String, by: Fish) {
1618
// What would the fish say, if it could talk?

test/Distributed/distributed_incomplete_system_no_crash.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public final class CompletelyHollowActorSystem: DistributedActorSystem {
3434

3535
public struct ResultHandler: DistributedTargetInvocationResultHandler {
3636
// expected-error@-1{{type 'CompletelyHollowActorSystem.ResultHandler' does not conform to protocol 'DistributedTargetInvocationResultHandler'}}
37+
// expected-error@-2{{struct 'ResultHandler' is missing witness for protocol requirement 'onReturn'}}
38+
// expected-note@-3{{add stubs for conformance}}
3739
}
3840

3941
}

test/Distributed/distributed_protocols_distributed_func_serialization_requirements.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ distributed actor ProtocolWithChecksSeqReqDA_MissingSystem: ProtocolWithChecksSe
5151
// expected-note@-2{{you can provide a module-wide default actor system by declaring:}}
5252
//
5353
// expected-error@-4{{distributed actor 'ProtocolWithChecksSeqReqDA_MissingSystem' does not declare ActorSystem it can be used with}}
54+
//
55+
// expected-error@-6{{type 'ProtocolWithChecksSeqReqDA_MissingSystem' does not conform to protocol 'Encodable'}}
56+
// expected-error@-7{{type 'ProtocolWithChecksSeqReqDA_MissingSystem' does not conform to protocol 'Decodable'}}
5457

5558
// Entire conformance is doomed, so we didn't proceed to checking the functions; that's fine
5659
distributed func testAT() async throws -> NotCodable { .init() }

0 commit comments

Comments
 (0)