Merge pull request #17124 from nkcsgexi/cherry-pick-06-11

[4.2] cherry-pick migrator changes
diff --git a/lib/Migrator/APIDiffMigratorPass.cpp b/lib/Migrator/APIDiffMigratorPass.cpp
index 559322c..4189e66 100644
--- a/lib/Migrator/APIDiffMigratorPass.cpp
+++ b/lib/Migrator/APIDiffMigratorPass.cpp
@@ -250,6 +250,19 @@
   }
 }
 
+struct ConversionFunctionInfo {
+  Expr *ExpressionToWrap;
+  SmallString<256> Buffer;
+  unsigned FuncNameStart;
+  unsigned FuncNameEnd;
+  ConversionFunctionInfo(Expr *ExpressionToWrap):
+    ExpressionToWrap(ExpressionToWrap) {}
+  StringRef getFuncDef() const { return Buffer.str(); }
+  StringRef getFuncName() const {
+    return Buffer.substr(FuncNameStart, FuncNameEnd - FuncNameStart);
+  }
+};
+
 struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker {
 
   APIDiffItemStore DiffStore;
@@ -328,20 +341,53 @@
     return false;
   }
 
-  std::set<std::string> InsertedFunctions;
+  std::vector<ConversionFunctionInfo> HelperFuncInfo;
   SourceLoc FileEndLoc;
+
   APIDiffMigratorPass(EditorAdapter &Editor, SourceFile *SF,
                       const MigratorOptions &Opts):
     ASTMigratorPass(Editor, SF, Opts), DiffStore(Diags),
-    FileEndLoc(SM.getRangeForBuffer(BufferID).getEnd()) {
-      SmallVector<Decl*, 16> TopDecls;
-      SF->getTopLevelDecls(TopDecls);
-      for (auto *D: TopDecls) {
-        if (auto *FD = dyn_cast<FuncDecl>(D)) {
-          InsertedFunctions.insert(FD->getBaseName().getIdentifier().str());
-        }
+    FileEndLoc(SM.getRangeForBuffer(BufferID).getEnd()) {}
+
+  ~APIDiffMigratorPass() {
+    Editor.disableCache();
+    SWIFT_DEFER { Editor.enableCache(); };
+
+    // Collect inserted functions to avoid re-insertion.
+    std::set<std::string> InsertedFunctions;
+    SmallVector<Decl*, 16> TopDecls;
+    SF->getTopLevelDecls(TopDecls);
+    for (auto *D: TopDecls) {
+      if (auto *FD = dyn_cast<FuncDecl>(D)) {
+        InsertedFunctions.insert(FD->getBaseName().getIdentifier().str());
       }
     }
+    for (auto &Cur: HelperFuncInfo) {
+      // Avoid wrapping nil expression.
+      if (isNilExpr(Cur.ExpressionToWrap))
+        continue;
+
+      // Avoid wrapping a single expression with multiple conversion functions.
+      auto count = std::count_if(HelperFuncInfo.begin(), HelperFuncInfo.end(),
+        [&] (ConversionFunctionInfo &Info) {
+          return Info.ExpressionToWrap->getSourceRange() ==
+            Cur.ExpressionToWrap->getSourceRange();
+        });
+      if (count > 1)
+        continue;
+      assert(count == 1);
+      auto FuncName = Cur.getFuncName();
+
+      // Avoid inserting the helper function if it's already present.
+      if (!InsertedFunctions.count(FuncName)) {
+        Editor.insert(FileEndLoc, Cur.getFuncDef());
+        InsertedFunctions.insert(FuncName);
+      }
+      Editor.insertBefore(Cur.ExpressionToWrap->getStartLoc(),
+        (Twine(FuncName) + "(").str());
+      Editor.insertAfterToken(Cur.ExpressionToWrap->getEndLoc(), ")");
+    }
+  }
 
   void run() {
     if (Opts.APIDigesterDataStorePaths.empty())
@@ -737,12 +783,9 @@
     }
     if (!Kind.hasValue())
       return false;
-    if (Kind && !isNilExpr(WrapperTarget)) {
-      SmallString<256> Buffer;
-      auto Func = insertHelperFunction(*Kind, LeftComment, RightComment, Buffer,
-        FromString);
-      Editor.insert(WrapperTarget->getStartLoc(), (Twine(Func) + "(").str());
-      Editor.insertAfterToken(WrapperTarget->getEndLoc(), ")");
+    if (Kind) {
+      insertHelperFunction(*Kind, LeftComment, RightComment, FromString,
+        WrapperTarget);
     }
     if (!Rename.empty()) {
       replaceExpr(Reference, Rename);
@@ -751,8 +794,6 @@
   }
 
   bool handleAssignDestMigration(Expr *E) {
-    Editor.disableCache();
-    SWIFT_DEFER { Editor.enableCache(); };
     auto *ASE = dyn_cast<AssignExpr>(E);
     if (!ASE || !ASE->getDest() || !ASE->getSrc())
       return false;
@@ -771,14 +812,16 @@
     return wrapAttributeReference(E, E, false);
   }
 
-  StringRef insertHelperFunction(NodeAnnotation Anno, StringRef RawType,
-                                 StringRef NewType,
-                                 SmallString<256> &Buffer, bool FromString) {
-    llvm::raw_svector_ostream OS(Buffer);
+  void insertHelperFunction(NodeAnnotation Anno, StringRef RawType,
+                            StringRef NewType, bool FromString,
+                            Expr *Wrappee) {
+    HelperFuncInfo.emplace_back(Wrappee);
+    ConversionFunctionInfo &Info = HelperFuncInfo.back();
+    llvm::raw_svector_ostream OS(Info.Buffer);
     OS << "\n";
     OS << "// Helper function inserted by Swift 4.2 migrator.\n";
     OS << "fileprivate func ";
-    unsigned FuncNameStart = Buffer.size();
+    Info.FuncNameStart = Info.Buffer.size();
     OS << (FromString ? "convertTo" : "convertFrom");
     SmallVector<std::string, 8> Segs;
     StringRef guard = "\tguard let input = input else { return nil }\n";
@@ -835,24 +878,17 @@
     for (auto P: Parts)
       OS << P;
     OS << Segs[1];
-    auto FuncName = Buffer.str().substr(FuncNameStart);
-    if (!InsertedFunctions.count(FuncName)) {
-      if (FromString) {
-        OS << "(_ input: " << Segs[2] << ") -> " << Segs[3] << " {\n";
-        OS << Segs[4] << "\n}\n";
-      } else {
-        OS << "(_ input: " << Segs[3] << ") -> " << Segs[2] << " {\n";
-        OS << Segs[5] << "\n}\n";
-      }
-      Editor.insert(FileEndLoc, OS.str());
-      InsertedFunctions.insert(FuncName);
+    Info.FuncNameEnd = Info.Buffer.size();
+    if (FromString) {
+      OS << "(_ input: " << Segs[2] << ") -> " << Segs[3] << " {\n";
+      OS << Segs[4] << "\n}\n";
+    } else {
+      OS << "(_ input: " << Segs[3] << ") -> " << Segs[2] << " {\n";
+      OS << Segs[5] << "\n}\n";
     }
-    return FuncName;
   }
 
   void handleStringRepresentableArg(ValueDecl *FD, Expr *Arg, Expr *Call) {
-    Editor.disableCache();
-    SWIFT_DEFER { Editor.enableCache(); };
     NodeAnnotation Kind;
     StringRef RawType;
     StringRef NewAttributeType;
@@ -880,19 +916,14 @@
       if (AllArgs.size() <= ArgIdx)
         return;
       WrapTarget = AllArgs[ArgIdx].ArgExp;
-      // Avoid wrapping nil literal.
-      if (isNilExpr(WrapTarget))
-        return;
     }
     assert(WrapTarget);
-    SmallString<256> Buffer;
-    auto FuncName = insertHelperFunction(Kind, RawType, NewAttributeType, Buffer,
-                                         FromString);
-    Editor.insert(WrapTarget->getStartLoc(), (Twine(FuncName) + "(").str());
-    Editor.insertAfterToken(WrapTarget->getEndLoc(), ")");
+    insertHelperFunction(Kind, RawType, NewAttributeType, FromString, WrapTarget);
   }
 
   bool walkToExprPre(Expr *E) override {
+    if (E->getSourceRange().isInvalid())
+      return false;
     if (handleQualifiedReplacement(E))
       return false;
     if (handleAssignDestMigration(E))
diff --git a/test/Migrator/Inputs/string-representable.json b/test/Migrator/Inputs/string-representable.json
index 3fd7c91..e35ed0f 100644
--- a/test/Migrator/Inputs/string-representable.json
+++ b/test/Migrator/Inputs/string-representable.json
@@ -216,4 +216,15 @@
     "NewPrintedName": "NewAttribute",
     "NewTypeName": "AttributeWrapper"
   },
+  {
+    "DiffItemKind": "CommonDiffItem",
+    "NodeKind": "Constructor",
+    "NodeAnnotation": "OptionalDictionaryKeyUpdate",
+    "ChildIndex": "1",
+    "LeftUsr": "c:objc(cs)BarForwardDeclaredClass(im)initWithOldLabel0:",
+    "LeftComment": "Int",
+    "RightUsr": "",
+    "RightComment": "AwesomeIntWrapper",
+    "ModuleName": "bar"
+  },
 ]
diff --git a/test/Migrator/string-representable.swift b/test/Migrator/string-representable.swift
index 8de3333..33394ee 100644
--- a/test/Migrator/string-representable.swift
+++ b/test/Migrator/string-representable.swift
@@ -1,10 +1,11 @@
 // REQUIRES: objc_interop
 // RUN: %empty-directory(%t.mod)
 // RUN: %target-swift-frontend -emit-module -o %t.mod/Cities.swiftmodule %S/Inputs/Cities.swift -module-name Cities -parse-as-library
-// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s  -I %t.mod -api-diff-data-file %S/Inputs/string-representable.json -emit-migrated-file-path %t/string-representable.swift.result -disable-migrator-fixits -o /dev/null
+// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s  -I %t.mod -api-diff-data-file %S/Inputs/string-representable.json -emit-migrated-file-path %t/string-representable.swift.result -disable-migrator-fixits -o /dev/null -F %S/mock-sdk
 // RUN: diff -u %S/string-representable.swift.expected %t/string-representable.swift.result
 
 import Cities
+import Bar
 
 func foo(_ c: Container) -> String {
   c.Value = ""
@@ -42,3 +43,5 @@
   c.optionalAttrDict = nil
   return c.Value
 }
+
+class C: BarForwardDeclaredClass {}
diff --git a/test/Migrator/string-representable.swift.expected b/test/Migrator/string-representable.swift.expected
index 81427ab..1a60198 100644
--- a/test/Migrator/string-representable.swift.expected
+++ b/test/Migrator/string-representable.swift.expected
@@ -1,10 +1,11 @@
 // REQUIRES: objc_interop
 // RUN: %empty-directory(%t.mod)
 // RUN: %target-swift-frontend -emit-module -o %t.mod/Cities.swiftmodule %S/Inputs/Cities.swift -module-name Cities -parse-as-library
-// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s  -I %t.mod -api-diff-data-file %S/Inputs/string-representable.json -emit-migrated-file-path %t/string-representable.swift.result -disable-migrator-fixits -o /dev/null
+// RUN: %empty-directory(%t) && %target-swift-frontend -c -update-code -primary-file %s  -I %t.mod -api-diff-data-file %S/Inputs/string-representable.json -emit-migrated-file-path %t/string-representable.swift.result -disable-migrator-fixits -o /dev/null -F %S/mock-sdk
 // RUN: diff -u %S/string-representable.swift.expected %t/string-representable.swift.result
 
 import Cities
+import Bar
 
 func foo(_ c: Container) -> String {
   c.Value = convertToNewAttribute("")
@@ -27,22 +28,24 @@
   _ = convertFromSimpleAttributeArray(c.getAttrArray())
   _ = convertFromOptionalSimpleAttributeArray(c.getOptionalAttrArray())
 
-  c.addingAttributes(convertToCitiesContainerAttributeDictionary(convertFromSimpleAttributeDictionary(c.getAttrDictionary())))
-  c.adding(optionalAttributes: convertToOptionalSimpleAttributeDictionary(convertFromSimpleAttributeDictionary(c.getAttrDictionary())))
+  c.addingAttributes(c.getAttrDictionary())
+  c.adding(optionalAttributes: c.getAttrDictionary())
 
   c.attrDict = convertToSimpleAttributeDictionary(["a": "b", "a": "b", "a": "b"])
   c.attrArr = convertToSimpleAttributeArray(["key1", "key2"])
   _ = convertFromSimpleAttributeArray(c.attrArr)
   _ = convertFromSimpleAttributeDictionary(c.attrDict)
-  c.adding(attributes: convertToSimpleAttributeDictionary(convertFromSimpleAttributeDictionary(c.attrDict)))
-  _ = Container(optionalAttrArray: convertToOptionalSimpleAttributeArray(convertFromSimpleAttributeArray(c.attrArr)))
-  c.adding(optionalAttributes: convertToOptionalSimpleAttributeDictionary(convertFromOptionalSimpleAttributeDictionary(c.optionalAttrDict)))
+  c.adding(attributes: c.attrDict)
+  _ = Container(optionalAttrArray: c.attrArr)
+  c.adding(optionalAttributes: c.optionalAttrDict)
   _ = convertFromNewAttribute(AttributeWrapper.NewAttribute)
-  c.Value = convertToNewAttribute(convertFromNewAttribute(AttributeWrapper.NewAttribute))
+  c.Value = AttributeWrapper.NewAttribute
   c.optionalAttrDict = nil
   return convertFromNewAttribute(c.Value)
 }
 
+class C: BarForwardDeclaredClass {}
+
 // Helper function inserted by Swift 4.2 migrator.
 fileprivate func convertToNewAttribute(_ input: String) -> NewAttribute {
 	return NewAttribute(rawValue: input)