Merge pull request #4627 from rintaro/nowitness-stub
[Sema] Improve fix-it for missing initializer requirement
diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def
index 817f596..4697a20 100644
--- a/include/swift/AST/DiagnosticsSema.def
+++ b/include/swift/AST/DiagnosticsSema.def
@@ -1317,9 +1317,10 @@
NOTE(inherited_protocol_does_not_conform,none,
"type %0 does not conform to inherited protocol %1", (Type, Type))
NOTE(no_witnesses,none,
- "protocol requires %select{initializer %1|function %1|property %1|"
- "subscript}0 with type %2; do you want to add a stub?",
- (RequirementKind, DeclName, Type))
+ "protocol requires "
+ "%select{initializer %1|function %1|property %1|subscript}0 with type %2"
+ "%select{|; do you want to add a stub?}3",
+ (RequirementKind, DeclName, Type, bool))
NOTE(ambiguous_witnesses,none,
"multiple matching "
"%select{initializers named %1|functions named %1|properties named %1|"
diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp
index 044e35b..23daf48 100644
--- a/lib/Sema/TypeCheckProtocol.cpp
+++ b/lib/Sema/TypeCheckProtocol.cpp
@@ -2009,14 +2009,15 @@
TypeChecker &TC) {
// FIXME: Try an ignore-access lookup?
+ DeclContext *Adopter = Conformance->getDeclContext();
+
SourceLoc FixitLocation;
SourceLoc TypeLoc;
- if (auto Extension = dyn_cast<ExtensionDecl>(Conformance->getDeclContext())) {
- FixitLocation = Extension->getBraces().Start.getAdvancedLocOrInvalid(1);
+ if (auto Extension = dyn_cast<ExtensionDecl>(Adopter)) {
+ FixitLocation = Extension->getBraces().Start;
TypeLoc = Extension->getStartLoc();
- } else if (auto Nominal =
- dyn_cast<NominalTypeDecl>(Conformance->getDeclContext())) {
- FixitLocation = Nominal->getBraces().Start.getAdvancedLocOrInvalid(1);
+ } else if (auto Nominal = dyn_cast<NominalTypeDecl>(Adopter)) {
+ FixitLocation = Nominal->getBraces().Start;
TypeLoc = Nominal->getStartLoc();
} else {
llvm_unreachable("Unknown adopter kind");
@@ -2036,7 +2037,7 @@
Accessibility Access = std::min(
/* Access of the context */
- Conformance->getDeclContext()
+ Adopter
->getAsGenericTypeOrGenericTypeExtensionContext()->getFormalAccess(),
/* Access of the protocol */
Requirement->getDeclContext()
@@ -2050,38 +2051,49 @@
TC.diagnose(MissingTypeWitness, diag::no_witnesses_type,
MissingTypeWitness->getName())
- .fixItInsert(FixitLocation, FixitStream.str());
+ .fixItInsertAfter(FixitLocation, FixitStream.str());
} else {
-
- PrintOptions Options = PrintOptions::printForDiagnostics();
- Options.AccessibilityFilter = Accessibility::Private;
- Options.PrintAccessibility = false;
- Options.FunctionBody = [](const ValueDecl *VD) { return "<#code#>"; };
- if (isa<ClassDecl>(Conformance->getDeclContext())) {
- Type SelfType = Conformance->getDeclContext()->getSelfTypeInContext();
- DeclContext *Adopter = Conformance->getDeclContext();
- Options.setArchetypeSelfTransform(SelfType, Adopter);
- } else {
- Type SelfType = Conformance->getDeclContext()->getSelfTypeInContext();
- DeclContext *Adopter = Conformance->getDeclContext();
- Options.setArchetypeAndDynamicSelfTransform(SelfType, Adopter);
+ bool AddFixit = true;
+ if (isa<ConstructorDecl>(Requirement)) {
+ if (auto CD = Adopter->getAsClassOrClassExtensionContext()) {
+ if (!CD->isFinal() && Adopter->isExtensionContext()) {
+ // In this case, user should mark class as 'final' or define
+ // 'required' intializer directly in the class definition.
+ AddFixit = false;
+ } else if (!CD->isFinal()) {
+ Printer << "required ";
+ } else if (Adopter->isExtensionContext()) {
+ Printer << "convenience ";
+ }
+ }
}
- Options.CurrentModule = Conformance->getDeclContext()->getParentModule();
- if (isa<NominalTypeDecl>(Conformance->getDeclContext())) {
- // Create a variable declaration instead of a computed property in nominal
- // types
- Options.PrintPropertyAccessors = false;
- }
-
- Requirement->print(Printer, Options);
- Printer << "\n";
// Point out the requirement that wasn't met.
- TC.diagnose(Requirement, diag::no_witnesses,
+ auto diag = TC.diagnose(Requirement, diag::no_witnesses,
getRequirementKind(Requirement),
Requirement->getFullName(),
- RequirementType)
- .fixItInsert(FixitLocation, FixitStream.str());
+ RequirementType, AddFixit);
+ if (AddFixit) {
+ PrintOptions Options = PrintOptions::printForDiagnostics();
+ Options.AccessibilityFilter = Accessibility::Private;
+ Options.PrintAccessibility = false;
+ Options.FunctionBody = [](const ValueDecl *VD) { return "<#code#>"; };
+ Type SelfType = Adopter->getSelfTypeInContext();
+ if (Adopter->getAsClassOrClassExtensionContext())
+ Options.setArchetypeSelfTransform(SelfType, Adopter);
+ else
+ Options.setArchetypeAndDynamicSelfTransform(SelfType, Adopter);
+ Options.CurrentModule = Adopter->getParentModule();
+ if (!Adopter->isExtensionContext()) {
+ // Create a variable declaration instead of a computed property in
+ // nominal types
+ Options.PrintPropertyAccessors = false;
+ }
+ Requirement->print(Printer, Options);
+ Printer << "\n";
+
+ diag.fixItInsertAfter(FixitLocation, FixitStream.str());
+ }
}
}
@@ -2483,7 +2495,7 @@
tc.diagnose(requirement, diag::no_witnesses,
getRequirementKind(requirement),
requirement->getName(),
- reqType);
+ reqType, false);
});
return ResolveWitnessResult::ExplicitFailed;
diff --git a/test/decl/protocol/conforms/fixit_stub.swift b/test/decl/protocol/conforms/fixit_stub.swift
index 7cef32f..2c6d3aa 100644
--- a/test/decl/protocol/conforms/fixit_stub.swift
+++ b/test/decl/protocol/conforms/fixit_stub.swift
@@ -3,7 +3,7 @@
protocol Protocol1 {
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}}
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}}
- 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}}
+ 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}}
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}}
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}}
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 @@
protocol Protocol2 {
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}}
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}}
- 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}}
+ init(arg: Int) // expected-note{{protocol requires initializer 'init(arg:)' with type '(arg: Int)'}} {{none}}
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}}
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}}
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 @@
// expected-error@-1{{type 'Adopter11' does not conform to protocol 'ProtocolWithPrivateAccess3'}}
// expected-error@-2{{type 'Adopter11' does not conform to protocol 'ProtocolWithPrivateAccess4'}}
}
+
+protocol ProtocolRequiresInit1 {
+ 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}}
+}
+final class Adopter12 : ProtocolRequiresInit1 {} //expected-error {{type 'Adopter12' does not conform to protocol 'ProtocolRequiresInit1'}} // expected-note{{candidate}}
+
+protocol ProtocolRequiresInit2 {
+ 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}}
+}
+final class Adopter13 {} // expected-note{{candidate}}
+extension Adopter13 : ProtocolRequiresInit2 {} //expected-error {{type 'Adopter13' does not conform to protocol 'ProtocolRequiresInit2'}}
+
+protocol ProtocolRequiresInit3 {
+ 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}}
+}
+
+struct Adopter14 {} // expected-note{{candidate}}
+extension Adopter14 : ProtocolRequiresInit3 {} //expected-error {{type 'Adopter14' does not conform to protocol 'ProtocolRequiresInit3'}}