Skip to content

Commit d6cc431

Browse files
authored
[FixCode] Enhance existing fill-protocol-stub fixit so that all missing witnesses can fixed at once.
1 parent 9c2b471 commit d6cc431

File tree

2 files changed

+133
-103
lines changed

2 files changed

+133
-103
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 113 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1706,6 +1706,10 @@ namespace {
17061706
/// Whether we've already complained about problems with this conformance.
17071707
bool AlreadyComplained = false;
17081708

1709+
/// Keep track of missing witnesses, either type or value, for later
1710+
/// diagnosis emits.
1711+
llvm::SetVector<ValueDecl*> MissingWitnesses;
1712+
17091713
/// Retrieve the associated types that are referenced by the given
17101714
/// requirement with a base of 'Self'.
17111715
ArrayRef<AssociatedTypeDecl *> getReferencedAssociatedTypes(ValueDecl *req);
@@ -1779,6 +1783,8 @@ namespace {
17791783
llvm::SmallPtrSetImpl<ProtocolConformance *> &visited);
17801784
void addUsedConformances(ProtocolConformance *conformance);
17811785

1786+
/// Call this to diagnose currently known missing witnesses.
1787+
void diagnoseMissingWitnesses();
17821788
public:
17831789
/// Emit any diagnostics that have been delayed.
17841790
void emitDelayedDiags();
@@ -2397,50 +2403,77 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
23972403
return true;
23982404
}
23992405

2400-
2401-
/// Generates a note for a protocol requirement for which no witness was found
2402-
/// and provides a fixit to add a stub to the adopter
2403-
static void diagnoseNoWitness(ValueDecl *Requirement, Type RequirementType,
2404-
NormalProtocolConformance *Conformance) {
2405-
// FIXME: Try an ignore-access lookup?
2406-
2407-
DeclContext *Adopter = Conformance->getDeclContext();
2408-
2409-
SourceLoc FixitLocation;
2410-
SourceLoc TypeLoc;
2411-
if (auto Extension = dyn_cast<ExtensionDecl>(Adopter)) {
2412-
FixitLocation = Extension->getBraces().Start;
2413-
TypeLoc = Extension->getStartLoc();
2414-
} else if (auto Nominal = dyn_cast<NominalTypeDecl>(Adopter)) {
2415-
FixitLocation = Nominal->getBraces().Start;
2416-
TypeLoc = Nominal->getStartLoc();
2417-
} else {
2418-
llvm_unreachable("Unknown adopter kind");
2419-
}
2420-
2421-
ASTContext &Ctx = Requirement->getASTContext();
2422-
auto &Diags = Ctx.Diags;
2423-
std::string FixitString;
2406+
/// Print the stubs for an array of witnesses, either type or value, to
2407+
/// FixitString. If for a witness we cannot have stub printed, insert it to
2408+
/// NoStubRequirements.
2409+
static void
2410+
printProtocolStubFixitString(SourceLoc TypeLoc, ProtocolConformance *Conf,
2411+
ArrayRef<ValueDecl*> MissingWitnesses,
2412+
std::string &FixitString,
2413+
llvm::SetVector<ValueDecl*> &NoStubRequirements) {
24242414
llvm::raw_string_ostream FixitStream(FixitString);
2415+
std::for_each(MissingWitnesses.begin(), MissingWitnesses.end(),
2416+
[&](ValueDecl* VD) {
2417+
if (!printRequirementStub(VD, Conf->getDeclContext(), Conf->getType(),
2418+
TypeLoc, FixitStream)) {
2419+
NoStubRequirements.insert(VD);
2420+
}
2421+
});
2422+
}
24252423

2426-
bool AddFixit = printRequirementStub(Requirement, Adopter,
2427-
Conformance->getType(), TypeLoc,
2428-
FixitStream);
2424+
void ConformanceChecker::diagnoseMissingWitnesses() {
2425+
if (MissingWitnesses.empty())
2426+
return;
24292427

2430-
if (auto MissingTypeWitness = dyn_cast<AssociatedTypeDecl>(Requirement)) {
2431-
Diags.diagnose(MissingTypeWitness, diag::no_witnesses_type,
2432-
MissingTypeWitness->getName())
2433-
.fixItInsertAfter(FixitLocation, FixitStream.str());
2434-
} else {
2435-
// Point out the requirement that wasn't met.
2436-
auto diag = Diags.diagnose(Requirement, diag::no_witnesses,
2437-
getRequirementKind(Requirement),
2438-
Requirement->getFullName(),
2439-
RequirementType, AddFixit);
2440-
if (AddFixit) {
2441-
diag.fixItInsertAfter(FixitLocation, FixitStream.str());
2442-
}
2443-
}
2428+
// Make sure we clear the missing witness bucket when exiting.
2429+
SWIFT_DEFER { MissingWitnesses.clear(); };
2430+
llvm::SetVector<ValueDecl*> MissingWitnesses = this->MissingWitnesses;
2431+
diagnoseOrDefer(MissingWitnesses[0], true,
2432+
[MissingWitnesses](NormalProtocolConformance *Conf) {
2433+
DeclContext *DC = Conf->getDeclContext();
2434+
// The location where to insert stubs.
2435+
SourceLoc FixitLocation;
2436+
2437+
// The location where the type starts.
2438+
SourceLoc TypeLoc;
2439+
if (auto Extension = dyn_cast<ExtensionDecl>(DC)) {
2440+
FixitLocation = Extension->getBraces().Start;
2441+
TypeLoc = Extension->getStartLoc();
2442+
} else if (auto Nominal = dyn_cast<NominalTypeDecl>(DC)) {
2443+
FixitLocation = Nominal->getBraces().Start;
2444+
TypeLoc = Nominal->getStartLoc();
2445+
} else {
2446+
llvm_unreachable("Unknown adopter kind");
2447+
}
2448+
std::string FixIt;
2449+
llvm::SetVector<ValueDecl*> NoStubRequirements;
2450+
2451+
// Print stubs for all known missing witnesses.
2452+
printProtocolStubFixitString(TypeLoc, Conf,
2453+
MissingWitnesses.getArrayRef(),
2454+
FixIt, NoStubRequirements);
2455+
auto &Diags = DC->getASTContext().Diags;
2456+
for (auto VD : MissingWitnesses) {
2457+
// Whether this VD has a stub printed.
2458+
bool AddFixit = !NoStubRequirements.count(VD);
2459+
2460+
// Issue diagnostics for witness types.
2461+
if (auto MissingTypeWitness = dyn_cast<AssociatedTypeDecl>(VD)) {
2462+
Diags.diagnose(MissingTypeWitness, diag::no_witnesses_type,
2463+
MissingTypeWitness->getName()).
2464+
fixItInsertAfter(FixitLocation, FixIt);
2465+
continue;
2466+
}
2467+
// Issue diagnostics for witness values.
2468+
Type RequirementType =
2469+
getRequirementTypeForDisplay(DC->getParentModule(), Conf, VD);
2470+
auto Diag = Diags.diagnose(VD, diag::no_witnesses,
2471+
getRequirementKind(VD), VD->getFullName(),
2472+
RequirementType, AddFixit);
2473+
if (AddFixit)
2474+
Diag.fixItInsertAfter(FixitLocation, FixIt);
2475+
}
2476+
});
24442477
}
24452478

24462479
ResolveWitnessResult
@@ -2754,30 +2787,36 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
27542787
return ResolveWitnessResult::ExplicitFailed;
27552788
}
27562789

2790+
2791+
if (!numViable) {
2792+
// Save the missing requirement for later diagnosis.
2793+
MissingWitnesses.insert(requirement);
2794+
diagnoseOrDefer(requirement, true,
2795+
[requirement, matches](NormalProtocolConformance *conformance) {
2796+
auto dc = conformance->getDeclContext();
2797+
// Diagnose each of the matches.
2798+
for (const auto &match : matches)
2799+
diagnoseMatch(dc->getParentModule(), conformance, requirement, match);
2800+
});
2801+
return ResolveWitnessResult::ExplicitFailed;
2802+
}
2803+
27572804
diagnoseOrDefer(requirement, true,
2758-
[requirement, matches, ignoringNames, numViable](
2805+
[requirement, matches, ignoringNames](
27592806
NormalProtocolConformance *conformance) {
27602807
auto dc = conformance->getDeclContext();
2761-
auto &diags = dc->getASTContext().Diags;
2762-
27632808
// Determine the type that the requirement is expected to have.
27642809
Type reqType = getRequirementTypeForDisplay(dc->getParentModule(),
27652810
conformance, requirement);
2766-
2767-
if (numViable > 0) {
2768-
auto diagnosticMessage = diag::ambiguous_witnesses;
2769-
if (ignoringNames) {
2770-
diagnosticMessage = diag::ambiguous_witnesses_wrong_name;
2771-
}
2772-
diags.diagnose(requirement, diagnosticMessage,
2773-
getRequirementKind(requirement),
2774-
requirement->getFullName(),
2775-
reqType);
2776-
} else {
2777-
// Generate diagnostics for the missing requirement and a fixit to add
2778-
// it
2779-
diagnoseNoWitness(requirement, reqType, conformance);
2811+
auto &diags = dc->getASTContext().Diags;
2812+
auto diagnosticMessage = diag::ambiguous_witnesses;
2813+
if (ignoringNames) {
2814+
diagnosticMessage = diag::ambiguous_witnesses_wrong_name;
27802815
}
2816+
diags.diagnose(requirement, diagnosticMessage,
2817+
getRequirementKind(requirement),
2818+
requirement->getFullName(),
2819+
reqType);
27812820

27822821
// Diagnose each of the matches.
27832822
for (const auto &match : matches)
@@ -2841,21 +2880,8 @@ ResolveWitnessResult ConformanceChecker::resolveWitnessViaDefault(
28412880
recordOptionalWitness(requirement);
28422881
return ResolveWitnessResult::Success;
28432882
}
2844-
2845-
diagnoseOrDefer(requirement, true,
2846-
[requirement](NormalProtocolConformance *conformance) {
2847-
auto dc = conformance->getDeclContext();
2848-
auto &diags = dc->getASTContext().Diags;
2849-
// Determine the type that the requirement is expected to have.
2850-
Type reqType = getRequirementTypeForDisplay(dc->getParentModule(),
2851-
conformance, requirement);
2852-
2853-
// Point out the requirement that wasn't met.
2854-
diags.diagnose(requirement, diag::no_witnesses,
2855-
getRequirementKind(requirement), requirement->getName(),
2856-
reqType, false);
2857-
});
2858-
2883+
// Save the missing requirement for later diagnosis.
2884+
MissingWitnesses.insert(requirement);
28592885
return ResolveWitnessResult::ExplicitFailed;
28602886
}
28612887

@@ -2949,13 +2975,13 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
29492975

29502976
return ResolveWitnessResult::ExplicitFailed;
29512977
}
2978+
// Save the missing type witness for later diagnosis.
2979+
MissingWitnesses.insert(assocType);
29522980

29532981
// None of the candidates were viable.
29542982
diagnoseOrDefer(assocType, true,
2955-
[assocType, nonViable](NormalProtocolConformance *conformance) {
2956-
auto &diags = assocType->getASTContext().Diags;
2957-
diags.diagnose(assocType, diag::no_witnesses_type, assocType->getName());
2958-
2983+
[nonViable](NormalProtocolConformance *conformance) {
2984+
auto &diags = conformance->getDeclContext()->getASTContext().Diags;
29592985
for (auto candidate : nonViable) {
29602986
if (candidate.first->getDeclaredInterfaceType()->hasError())
29612987
continue;
@@ -4224,12 +4250,9 @@ void ConformanceChecker::resolveTypeWitnesses() {
42244250
return;
42254251
}
42264252

4227-
for (auto missingTypeWitness : unresolvedAssocTypes) {
4228-
diagnoseOrDefer(missingTypeWitness, true,
4229-
[missingTypeWitness](NormalProtocolConformance *conformance) {
4230-
diagnoseNoWitness(missingTypeWitness, Type(), conformance);
4231-
});
4232-
}
4253+
// Save the missing type witnesses for later diagnosis.
4254+
MissingWitnesses.insert(unresolvedAssocTypes.begin(),
4255+
unresolvedAssocTypes.end());
42334256

42344257
return;
42354258
}
@@ -4303,6 +4326,8 @@ void ConformanceChecker::resolveTypeWitnesses() {
43034326

43044327
void ConformanceChecker::resolveSingleTypeWitness(
43054328
AssociatedTypeDecl *assocType) {
4329+
// Ensure we diagnose if the witness is missing.
4330+
SWIFT_DEFER { diagnoseMissingWitnesses(); };
43064331
switch (resolveTypeWitnessViaLookup(assocType)) {
43074332
case ResolveWitnessResult::Success:
43084333
case ResolveWitnessResult::ExplicitFailed:
@@ -4510,6 +4535,11 @@ void ConformanceChecker::checkConformance() {
45104535
// Resolve all of the type witnesses.
45114536
resolveTypeWitnesses();
45124537

4538+
// Diagnose missing type witnesses for now.
4539+
diagnoseMissingWitnesses();
4540+
// Diagnose missing value witnesses later.
4541+
SWIFT_DEFER { diagnoseMissingWitnesses(); };
4542+
45134543
// Resolution attempts to have the witnesses be correct by construction, but
45144544
// this isn't guaranteed, so let's double check.
45154545
ensureRequirementsAreSatisfied();

0 commit comments

Comments
 (0)