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'}}