Skip to content

Commit 0990ffe

Browse files
Merge pull request swiftlang#32562 from AnthonyLatsis/stub-filter
Sema: Filter out conflicting requirements when printing stubs
2 parents fe82a70 + 057dbbd commit 0990ffe

File tree

2 files changed

+101
-16
lines changed

2 files changed

+101
-16
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2963,6 +2963,59 @@ printProtocolStubFixitString(SourceLoc TypeLoc, ProtocolConformance *Conf,
29632963
});
29642964
}
29652965

2966+
/// Filter the given array of protocol requirements and produce a new vector
2967+
/// containing the non-conflicting requirements to be implemented by the given
2968+
/// \c Adoptee type.
2969+
static llvm::SmallVector<ValueDecl *, 4>
2970+
filterProtocolRequirements(ArrayRef<ValueDecl *> Reqs, Type Adoptee) {
2971+
llvm::SmallVector<ValueDecl *, 4> Filtered;
2972+
if (Reqs.empty()) {
2973+
return Filtered;
2974+
}
2975+
2976+
const auto getProtocolSubstitutionMap = [&](ValueDecl *Req) {
2977+
auto *const PD = cast<ProtocolDecl>(Req->getDeclContext());
2978+
return SubstitutionMap::getProtocolSubstitutions(
2979+
PD, Adoptee, ProtocolConformanceRef(PD));
2980+
};
2981+
2982+
llvm::SmallDenseMap<DeclName, llvm::SmallVector<ValueDecl *, 2>, 4>
2983+
DeclsByName;
2984+
for (auto *const Req : Reqs) {
2985+
if (DeclsByName.find(Req->getName()) == DeclsByName.end()) {
2986+
DeclsByName[Req->getName()] = {Req};
2987+
Filtered.push_back(Req);
2988+
continue;
2989+
}
2990+
2991+
auto OverloadTy = Req->getOverloadSignatureType();
2992+
if (OverloadTy) {
2993+
OverloadTy =
2994+
OverloadTy.subst(getProtocolSubstitutionMap(Req))->getCanonicalType();
2995+
}
2996+
if (llvm::any_of(DeclsByName[Req->getName()], [&](ValueDecl *OtherReq) {
2997+
auto OtherOverloadTy = OtherReq->getOverloadSignatureType();
2998+
if (OtherOverloadTy) {
2999+
OtherOverloadTy =
3000+
OtherOverloadTy.subst(getProtocolSubstitutionMap(OtherReq))
3001+
->getCanonicalType();
3002+
}
3003+
return conflicting(Req->getASTContext(), Req->getOverloadSignature(),
3004+
OverloadTy, OtherReq->getOverloadSignature(),
3005+
OtherOverloadTy,
3006+
/*wouldConflictInSwift5*/ nullptr,
3007+
/*skipProtocolExtensionCheck*/ true);
3008+
})) {
3009+
continue;
3010+
}
3011+
3012+
DeclsByName[Req->getName()].push_back(Req);
3013+
Filtered.push_back(Req);
3014+
}
3015+
3016+
return Filtered;
3017+
}
3018+
29663019
void ConformanceChecker::
29673020
diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
29683021
auto LocalMissing = getLocalMissingWitness();
@@ -2971,12 +3024,10 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
29713024
if (LocalMissing.empty())
29723025
return;
29733026

2974-
SourceLoc ComplainLoc = Loc;
2975-
bool EditorMode = getASTContext().LangOpts.DiagnosticsEditorMode;
2976-
llvm::SetVector<ValueDecl*> MissingWitnesses(GlobalMissingWitnesses.begin(),
2977-
GlobalMissingWitnesses.end());
2978-
auto InsertFixitCallback = [ComplainLoc, EditorMode, MissingWitnesses]
2979-
(NormalProtocolConformance *Conf) {
3027+
const auto InsertFixit = [](NormalProtocolConformance *Conf,
3028+
SourceLoc ComplainLoc, bool EditorMode,
3029+
llvm::SmallVector<ValueDecl *, 4>
3030+
MissingWitnesses) {
29803031
DeclContext *DC = Conf->getDeclContext();
29813032
// The location where to insert stubs.
29823033
SourceLoc FixitLocation;
@@ -2996,9 +3047,8 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
29963047
llvm::SetVector<ValueDecl*> NoStubRequirements;
29973048

29983049
// Print stubs for all known missing witnesses.
2999-
printProtocolStubFixitString(TypeLoc, Conf,
3000-
MissingWitnesses.getArrayRef(),
3001-
FixIt, NoStubRequirements);
3050+
printProtocolStubFixitString(TypeLoc, Conf, MissingWitnesses, FixIt,
3051+
NoStubRequirements);
30023052
auto &Diags = DC->getASTContext().Diags;
30033053

30043054
// If we are in editor mode, squash all notes into a single fixit.
@@ -3072,27 +3122,33 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
30723122
}
30733123
};
30743124

3125+
const bool IsEditorMode = Context.LangOpts.DiagnosticsEditorMode;
30753126
switch (Kind) {
30763127
case MissingWitnessDiagnosisKind::ErrorFixIt: {
3128+
const auto MissingWitnesses = filterProtocolRequirements(
3129+
GlobalMissingWitnesses.getArrayRef(), Adoptee);
30773130
if (SuppressDiagnostics) {
30783131
// If the diagnostics are suppressed, we register these missing witnesses
30793132
// for later revisiting.
30803133
Conformance->setInvalid();
3081-
getASTContext().addDelayedMissingWitnesses(
3082-
Conformance, MissingWitnesses.getArrayRef());
3134+
getASTContext().addDelayedMissingWitnesses(Conformance, MissingWitnesses);
30833135
} else {
3084-
diagnoseOrDefer(LocalMissing[0], true, InsertFixitCallback);
3136+
diagnoseOrDefer(
3137+
LocalMissing[0], true, [&](NormalProtocolConformance *Conf) {
3138+
InsertFixit(Conf, Loc, IsEditorMode, std::move(MissingWitnesses));
3139+
});
30853140
}
30863141
clearGlobalMissingWitnesses();
30873142
return;
30883143
}
30893144
case MissingWitnessDiagnosisKind::ErrorOnly: {
3090-
diagnoseOrDefer(LocalMissing[0], true,
3091-
[](NormalProtocolConformance *Conf) {});
3145+
diagnoseOrDefer(LocalMissing[0], true, [](NormalProtocolConformance *) {});
30923146
return;
30933147
}
30943148
case MissingWitnessDiagnosisKind::FixItOnly:
3095-
InsertFixitCallback(Conformance);
3149+
InsertFixit(Conformance, Loc, IsEditorMode,
3150+
filterProtocolRequirements(GlobalMissingWitnesses.getArrayRef(),
3151+
Adoptee));
30963152
clearGlobalMissingWitnesses();
30973153
return;
30983154
}

test/decl/protocol/conforms/fixit_stub_editor.swift

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,31 @@
33

44
// RUN: %target-swift-frontend -typecheck %s -I %t -verify -diagnostics-editor-mode
55

6+
protocol P0_A { associatedtype T }
7+
protocol P0_B { associatedtype T }
8+
9+
class C0: P0_A, P0_B {} // expected-error{{type 'C0' does not conform to protocol 'P0_A'}} expected-error{{type 'C0' does not conform to protocol 'P0_B'}} expected-note{{do you want to add protocol stubs?}}{{23-23=\n typealias T = <#type#>\n}}
10+
611
protocol P1 {
712
@available(*, deprecated)
813
func foo1()
914
func foo2()
15+
func foo3(arg: Int, arg2: String)
16+
func foo4<T: P1>(_: T)
1017
}
1118

1219
protocol P2 {
1320
func bar1()
1421
func bar2()
22+
23+
func foo2()
24+
func foo3(arg: Int, arg2: String)
25+
func foo3(arg: Int, arg2: Int)
26+
func foo4<T: P1>(_: T)
27+
func foo4<T: P2>(_: T)
1528
}
1629

17-
class C1 : P1, P2 {} // expected-error{{type 'C1' does not conform to protocol 'P1'}} expected-error{{type 'C1' does not conform to protocol 'P2'}} expected-note{{do you want to add protocol stubs?}}{{20-20=\n func foo1() {\n <#code#>\n \}\n\n func foo2() {\n <#code#>\n \}\n\n func bar1() {\n <#code#>\n \}\n\n func bar2() {\n <#code#>\n \}\n}}
30+
class C1 : P1, P2 {} // expected-error{{type 'C1' does not conform to protocol 'P1'}} expected-error{{type 'C1' does not conform to protocol 'P2'}} expected-note{{do you want to add protocol stubs?}}{{20-20=\n func foo1() {\n <#code#>\n \}\n\n func foo2() {\n <#code#>\n \}\n\n func foo3(arg: Int, arg2: String) {\n <#code#>\n \}\n\n func foo4<T>(_: T) where T : P1 {\n <#code#>\n \}\n\n func bar1() {\n <#code#>\n \}\n\n func bar2() {\n <#code#>\n \}\n\n func foo3(arg: Int, arg2: Int) {\n <#code#>\n \}\n}}
1831

1932
protocol P3 {
2033
associatedtype T1
@@ -23,13 +36,29 @@ protocol P3 {
2336
}
2437

2538
protocol P4 : P3 {
39+
associatedtype T1
2640
associatedtype T4 = T1
2741
associatedtype T5 = T2
2842
associatedtype T6 = T3
2943
}
3044

3145
class C2 : P4 {} // expected-error{{type 'C2' does not conform to protocol 'P4'}} expected-error{{type 'C2' does not conform to protocol 'P3'}} expected-note{{do you want to add protocol stubs?}}{{16-16=\n typealias T1 = <#type#>\n\n typealias T2 = <#type#>\n\n typealias T3 = <#type#>\n}}
3246

47+
protocol P5 {
48+
func foo1()
49+
func foo2(arg: Int, arg2: String)
50+
func foo3<T: P3>(_: T)
51+
}
52+
53+
protocol P6: P5 {
54+
func foo1()
55+
func foo2(arg: Int, arg2: String)
56+
func foo2(arg: Int, arg2: Int)
57+
func foo3<T: P3>(_: T)
58+
func foo3<T: P4>(_: T)
59+
}
60+
61+
class C3 : P6 {} // expected-error{{type 'C3' does not conform to protocol 'P5'}} expected-error{{type 'C3' does not conform to protocol 'P6'}} expected-note{{do you want to add protocol stubs?}}{{16-16=\n func foo1() {\n <#code#>\n \}\n\n func foo2(arg: Int, arg2: String) {\n <#code#>\n \}\n\n func foo2(arg: Int, arg2: Int) {\n <#code#>\n \}\n\n func foo3<T>(_: T) where T : P3 {\n <#code#>\n \}\n}}
3362

3463
// =============================================================================
3564
// Test how we print stubs for mutating and non-mutating requirements.

0 commit comments

Comments
 (0)