Skip to content

Commit a7fef22

Browse files
authored
Merge pull request #4627 from rintaro/nowitness-stub
[Sema] Improve fix-it for missing initializer requirement
2 parents ec69f11 + f585a27 commit a7fef22

File tree

3 files changed

+69
-38
lines changed

3 files changed

+69
-38
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,9 +1317,10 @@ ERROR(protocol_property_must_be_computed,none,
13171317
NOTE(inherited_protocol_does_not_conform,none,
13181318
"type %0 does not conform to inherited protocol %1", (Type, Type))
13191319
NOTE(no_witnesses,none,
1320-
"protocol requires %select{initializer %1|function %1|property %1|"
1321-
"subscript}0 with type %2; do you want to add a stub?",
1322-
(RequirementKind, DeclName, Type))
1320+
"protocol requires "
1321+
"%select{initializer %1|function %1|property %1|subscript}0 with type %2"
1322+
"%select{|; do you want to add a stub?}3",
1323+
(RequirementKind, DeclName, Type, bool))
13231324
NOTE(ambiguous_witnesses,none,
13241325
"multiple matching "
13251326
"%select{initializers named %1|functions named %1|properties named %1|"

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,14 +2009,15 @@ static void diagnoseNoWitness(ValueDecl *Requirement, Type RequirementType,
20092009
TypeChecker &TC) {
20102010
// FIXME: Try an ignore-access lookup?
20112011

2012+
DeclContext *Adopter = Conformance->getDeclContext();
2013+
20122014
SourceLoc FixitLocation;
20132015
SourceLoc TypeLoc;
2014-
if (auto Extension = dyn_cast<ExtensionDecl>(Conformance->getDeclContext())) {
2015-
FixitLocation = Extension->getBraces().Start.getAdvancedLocOrInvalid(1);
2016+
if (auto Extension = dyn_cast<ExtensionDecl>(Adopter)) {
2017+
FixitLocation = Extension->getBraces().Start;
20162018
TypeLoc = Extension->getStartLoc();
2017-
} else if (auto Nominal =
2018-
dyn_cast<NominalTypeDecl>(Conformance->getDeclContext())) {
2019-
FixitLocation = Nominal->getBraces().Start.getAdvancedLocOrInvalid(1);
2019+
} else if (auto Nominal = dyn_cast<NominalTypeDecl>(Adopter)) {
2020+
FixitLocation = Nominal->getBraces().Start;
20202021
TypeLoc = Nominal->getStartLoc();
20212022
} else {
20222023
llvm_unreachable("Unknown adopter kind");
@@ -2036,7 +2037,7 @@ static void diagnoseNoWitness(ValueDecl *Requirement, Type RequirementType,
20362037

20372038
Accessibility Access = std::min(
20382039
/* Access of the context */
2039-
Conformance->getDeclContext()
2040+
Adopter
20402041
->getAsGenericTypeOrGenericTypeExtensionContext()->getFormalAccess(),
20412042
/* Access of the protocol */
20422043
Requirement->getDeclContext()
@@ -2050,38 +2051,49 @@ static void diagnoseNoWitness(ValueDecl *Requirement, Type RequirementType,
20502051

20512052
TC.diagnose(MissingTypeWitness, diag::no_witnesses_type,
20522053
MissingTypeWitness->getName())
2053-
.fixItInsert(FixitLocation, FixitStream.str());
2054+
.fixItInsertAfter(FixitLocation, FixitStream.str());
20542055
} else {
2055-
2056-
PrintOptions Options = PrintOptions::printForDiagnostics();
2057-
Options.AccessibilityFilter = Accessibility::Private;
2058-
Options.PrintAccessibility = false;
2059-
Options.FunctionBody = [](const ValueDecl *VD) { return "<#code#>"; };
2060-
if (isa<ClassDecl>(Conformance->getDeclContext())) {
2061-
Type SelfType = Conformance->getDeclContext()->getSelfTypeInContext();
2062-
DeclContext *Adopter = Conformance->getDeclContext();
2063-
Options.setArchetypeSelfTransform(SelfType, Adopter);
2064-
} else {
2065-
Type SelfType = Conformance->getDeclContext()->getSelfTypeInContext();
2066-
DeclContext *Adopter = Conformance->getDeclContext();
2067-
Options.setArchetypeAndDynamicSelfTransform(SelfType, Adopter);
2068-
}
2069-
Options.CurrentModule = Conformance->getDeclContext()->getParentModule();
2070-
if (isa<NominalTypeDecl>(Conformance->getDeclContext())) {
2071-
// Create a variable declaration instead of a computed property in nominal
2072-
// types
2073-
Options.PrintPropertyAccessors = false;
2056+
bool AddFixit = true;
2057+
if (isa<ConstructorDecl>(Requirement)) {
2058+
if (auto CD = Adopter->getAsClassOrClassExtensionContext()) {
2059+
if (!CD->isFinal() && Adopter->isExtensionContext()) {
2060+
// In this case, user should mark class as 'final' or define
2061+
// 'required' intializer directly in the class definition.
2062+
AddFixit = false;
2063+
} else if (!CD->isFinal()) {
2064+
Printer << "required ";
2065+
} else if (Adopter->isExtensionContext()) {
2066+
Printer << "convenience ";
2067+
}
2068+
}
20742069
}
20752070

2076-
Requirement->print(Printer, Options);
2077-
Printer << "\n";
2078-
20792071
// Point out the requirement that wasn't met.
2080-
TC.diagnose(Requirement, diag::no_witnesses,
2072+
auto diag = TC.diagnose(Requirement, diag::no_witnesses,
20812073
getRequirementKind(Requirement),
20822074
Requirement->getFullName(),
2083-
RequirementType)
2084-
.fixItInsert(FixitLocation, FixitStream.str());
2075+
RequirementType, AddFixit);
2076+
if (AddFixit) {
2077+
PrintOptions Options = PrintOptions::printForDiagnostics();
2078+
Options.AccessibilityFilter = Accessibility::Private;
2079+
Options.PrintAccessibility = false;
2080+
Options.FunctionBody = [](const ValueDecl *VD) { return "<#code#>"; };
2081+
Type SelfType = Adopter->getSelfTypeInContext();
2082+
if (Adopter->getAsClassOrClassExtensionContext())
2083+
Options.setArchetypeSelfTransform(SelfType, Adopter);
2084+
else
2085+
Options.setArchetypeAndDynamicSelfTransform(SelfType, Adopter);
2086+
Options.CurrentModule = Adopter->getParentModule();
2087+
if (!Adopter->isExtensionContext()) {
2088+
// Create a variable declaration instead of a computed property in
2089+
// nominal types
2090+
Options.PrintPropertyAccessors = false;
2091+
}
2092+
Requirement->print(Printer, Options);
2093+
Printer << "\n";
2094+
2095+
diag.fixItInsertAfter(FixitLocation, FixitStream.str());
2096+
}
20852097
}
20862098
}
20872099

@@ -2483,7 +2495,7 @@ ResolveWitnessResult ConformanceChecker::resolveWitnessViaDefault(
24832495
tc.diagnose(requirement, diag::no_witnesses,
24842496
getRequirementKind(requirement),
24852497
requirement->getName(),
2486-
reqType);
2498+
reqType, false);
24872499
});
24882500

24892501
return ResolveWitnessResult::ExplicitFailed;

test/decl/protocol/conforms/fixit_stub.swift

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
protocol Protocol1 {
44
func foo(arg1: Int, arg2: String) -> String // expected-note{{protocol requires function 'foo(arg1:arg2:)' with type '(Int, String) -> String'; do you want to add a stub?}} {{27-27=\n func foo(arg1: Int, arg2: String) -> String {\n <#code#>\n \}\n}}
55
func bar() throws -> String // expected-note{{protocol requires function 'bar()' with type '() throws -> String'; do you want to add a stub?}} {{27-27=\n func bar() throws -> String {\n <#code#>\n \}\n}}
6-
init(arg: Int) // expected-note{{protocol requires initializer 'init(arg:)' with type '(arg: Int)'; do you want to add a stub?}} {{27-27=\n init(arg: Int) {\n <#code#>\n \}\n}}
6+
init(arg: Int) // expected-note{{protocol requires initializer 'init(arg:)' with type '(arg: Int)'; do you want to add a stub?}} {{27-27=\n required init(arg: Int) {\n <#code#>\n \}\n}}
77
var baz: Int { get } // expected-note{{protocol requires property 'baz' with type 'Int'; do you want to add a stub?}} {{27-27=\n var baz: Int\n}}
88
var baz2: Int { get set } // expected-note{{protocol requires property 'baz2' with type 'Int'; do you want to add a stub?}} {{27-27=\n var baz2: Int\n}}
99
subscript(arg: Int) -> String { get } //expected-note{{rotocol requires subscript with type '(Int) -> String'; do you want to add a stub?}} {{27-27=\n subscript(arg: Int) -> String {\n <#code#>\n \}\n}}
@@ -18,7 +18,7 @@ class Adopter: Protocol1 { // expected-error{{type 'Adopter' does not conform to
1818
protocol Protocol2 {
1919
func foo(arg1: Int, arg2: String) -> String // expected-note{{protocol requires function 'foo(arg1:arg2:)' with type '(Int, String) -> String'; do you want to add a stub?}} {{32-32=\n func foo(arg1: Int, arg2: String) -> String {\n <#code#>\n \}\n}}
2020
func bar() throws -> String // expected-note{{protocol requires function 'bar()' with type '() throws -> String'; do you want to add a stub?}} {{32-32=\n func bar() throws -> String {\n <#code#>\n \}\n}}
21-
init(arg: Int) // expected-note{{protocol requires initializer 'init(arg:)' with type '(arg: Int)'; do you want to add a stub?}} {{32-32=\n init(arg: Int) {\n <#code#>\n \}\n}}
21+
init(arg: Int) // expected-note{{protocol requires initializer 'init(arg:)' with type '(arg: Int)'}} {{none}}
2222
var baz: Int { get } // expected-note{{protocol requires property 'baz' with type 'Int'; do you want to add a stub?}} {{32-32=\n var baz: Int {\n <#code#>\n \}\n}}
2323
var baz2: Int { get set } // expected-note{{protocol requires property 'baz2' with type 'Int'; do you want to add a stub?}} {{32-32=\n var baz2: Int {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n}}
2424
subscript(arg: Int) -> String { get } //expected-note{{rotocol requires subscript with type '(Int) -> String'; do you want to add a stub?}} {{32-32=\n subscript(arg: Int) -> String {\n <#code#>\n \}\n}}
@@ -126,3 +126,21 @@ public class Adopter11: ProtocolWithPrivateAccess3, ProtocolWithPrivateAccess4 {
126126
// expected-error@-1{{type 'Adopter11' does not conform to protocol 'ProtocolWithPrivateAccess3'}}
127127
// expected-error@-2{{type 'Adopter11' does not conform to protocol 'ProtocolWithPrivateAccess4'}}
128128
}
129+
130+
protocol ProtocolRequiresInit1 {
131+
init(arg: Int) // expected-note{{protocol requires initializer 'init(arg:)' with type '(arg: Int)'; do you want to add a stub?}} {{48-48=\n init(arg: Int) {\n <#code#>\n \}\n}}
132+
}
133+
final class Adopter12 : ProtocolRequiresInit1 {} //expected-error {{type 'Adopter12' does not conform to protocol 'ProtocolRequiresInit1'}} // expected-note{{candidate}}
134+
135+
protocol ProtocolRequiresInit2 {
136+
init(arg: Int) // expected-note{{protocol requires initializer 'init(arg:)' with type '(arg: Int)'; do you want to add a stub?}} {{46-46=\n convenience init(arg: Int) {\n <#code#>\n \}\n}}
137+
}
138+
final class Adopter13 {} // expected-note{{candidate}}
139+
extension Adopter13 : ProtocolRequiresInit2 {} //expected-error {{type 'Adopter13' does not conform to protocol 'ProtocolRequiresInit2'}}
140+
141+
protocol ProtocolRequiresInit3 {
142+
init(arg: Int) // expected-note{{protocol requires initializer 'init(arg:)' with type '(arg: Int)'; do you want to add a stub?}} {{46-46=\n init(arg: Int) {\n <#code#>\n \}\n}}
143+
}
144+
145+
struct Adopter14 {} // expected-note{{candidate}}
146+
extension Adopter14 : ProtocolRequiresInit3 {} //expected-error {{type 'Adopter14' does not conform to protocol 'ProtocolRequiresInit3'}}

0 commit comments

Comments
 (0)