Refactor the */& mismatch fixit generation out of SemaOverload and provide a simple conversion checking function.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@136376 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt
index 65219818..3766439 100644
--- a/lib/Sema/CMakeLists.txt
+++ b/lib/Sema/CMakeLists.txt
@@ -25,6 +25,7 @@
   SemaExprCXX.cpp
   SemaExprMember.cpp
   SemaExprObjC.cpp
+  SemaFixItUtils.cpp
   SemaInit.cpp
   SemaLookup.cpp
   SemaObjCProperty.cpp
diff --git a/lib/Sema/SemaFixItUtils.cpp b/lib/Sema/SemaFixItUtils.cpp
new file mode 100644
index 0000000..8e8a46d
--- /dev/null
+++ b/lib/Sema/SemaFixItUtils.cpp
@@ -0,0 +1,160 @@
+//===--- SemaFixItUtils.cpp - Sema FixIts ---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines helper classes for generation of Sema FixItHints.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaFixItUtils.h"
+
+using namespace clang;
+
+bool ConversionFixItGenerator::compareTypesSimple(CanQualType From,
+                                                  CanQualType To,
+                                                  Sema &S,
+                                                  SourceLocation Loc,
+                                                  ExprValueKind FromVK) {
+  if (!To.isAtLeastAsQualifiedAs(From))
+    return false;
+
+  From = From.getNonReferenceType();
+  To = To.getNonReferenceType();
+
+  // If both are pointer types, work with the pointee types.
+  if (isa<PointerType>(From) && isa<PointerType>(To)) {
+    From = S.Context.getCanonicalType(
+        (cast<PointerType>(From))->getPointeeType());
+    To = S.Context.getCanonicalType(
+        (cast<PointerType>(To))->getPointeeType());
+  }
+
+  const CanQualType FromUnq = From.getUnqualifiedType();
+  const CanQualType ToUnq = To.getUnqualifiedType();
+
+  if ((FromUnq == ToUnq || (S.IsDerivedFrom(FromUnq, ToUnq)) ) &&
+      To.isAtLeastAsQualifiedAs(From))
+    return true;
+  return false;
+}
+
+bool ConversionFixItGenerator::tryToFixConversion(const Expr *FullExpr,
+                                                  const QualType FromTy,
+                                                  const QualType ToTy,
+                                                  Sema &S) {
+  if (!FullExpr)
+    return false;
+
+  const CanQualType FromQTy = S.Context.getCanonicalType(FromTy);
+  const CanQualType ToQTy = S.Context.getCanonicalType(ToTy);
+  const SourceLocation Begin = FullExpr->getSourceRange().getBegin();
+  const SourceLocation End = S.PP.getLocForEndOfToken(FullExpr->getSourceRange()
+                                                      .getEnd());
+
+  // Strip the implicit casts - those are implied by the compiler, not the
+  // original source code.
+  const Expr* Expr = FullExpr->IgnoreImpCasts();
+
+  bool NeedParen = true;
+  if (isa<ArraySubscriptExpr>(Expr) ||
+      isa<CallExpr>(Expr) ||
+      isa<DeclRefExpr>(Expr) ||
+      isa<CastExpr>(Expr) ||
+      isa<CXXNewExpr>(Expr) ||
+      isa<CXXConstructExpr>(Expr) ||
+      isa<CXXDeleteExpr>(Expr) ||
+      isa<CXXNoexceptExpr>(Expr) ||
+      isa<CXXPseudoDestructorExpr>(Expr) ||
+      isa<CXXScalarValueInitExpr>(Expr) ||
+      isa<CXXThisExpr>(Expr) ||
+      isa<CXXTypeidExpr>(Expr) ||
+      isa<CXXUnresolvedConstructExpr>(Expr) ||
+      isa<ObjCMessageExpr>(Expr) ||
+      isa<ObjCPropertyRefExpr>(Expr) ||
+      isa<ObjCProtocolExpr>(Expr) ||
+      isa<MemberExpr>(Expr) ||
+      isa<ParenExpr>(FullExpr) ||
+      isa<ParenListExpr>(Expr) ||
+      isa<SizeOfPackExpr>(Expr) ||
+      isa<UnaryOperator>(Expr))
+    NeedParen = false;
+
+  // Check if the argument needs to be dereferenced:
+  //   (type * -> type) or (type * -> type &).
+  if (const PointerType *FromPtrTy = dyn_cast<PointerType>(FromQTy)) {
+    OverloadFixItKind FixKind = OFIK_Dereference;
+
+    bool CanConvert = CompareTypes(
+      S.Context.getCanonicalType(FromPtrTy->getPointeeType()), ToQTy,
+                                 S, Begin, VK_LValue);
+    if (CanConvert) {
+      // Do not suggest dereferencing a Null pointer.
+      if (Expr->IgnoreParenCasts()->
+          isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNotNull))
+        return false;
+
+      if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(Expr)) {
+        if (UO->getOpcode() == UO_AddrOf) {
+          FixKind = OFIK_RemoveTakeAddress;
+          Hints.push_back(FixItHint::CreateRemoval(
+                            CharSourceRange::getTokenRange(Begin, Begin)));
+        }
+      } else if (NeedParen) {
+        Hints.push_back(FixItHint::CreateInsertion(Begin, "*("));
+        Hints.push_back(FixItHint::CreateInsertion(End, ")"));
+      } else {
+        Hints.push_back(FixItHint::CreateInsertion(Begin, "*"));
+      }
+
+      NumConversionsFixed++;
+      if (NumConversionsFixed == 1)
+        Kind = FixKind;
+      return true;
+    }
+  }
+
+  // Check if the pointer to the argument needs to be passed:
+  //   (type -> type *) or (type & -> type *).
+  if (isa<PointerType>(ToQTy)) {
+    bool CanConvert = false;
+    OverloadFixItKind FixKind = OFIK_TakeAddress;
+
+    // Only suggest taking address of L-values.
+    if (!Expr->isLValue() || Expr->getObjectKind() != OK_Ordinary)
+      return false;
+
+    CanConvert = CompareTypes(S.Context.getPointerType(FromQTy), ToQTy,
+                              S, Begin, VK_RValue);
+    if (CanConvert) {
+
+      if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(Expr)) {
+        if (UO->getOpcode() == UO_Deref) {
+          FixKind = OFIK_RemoveDereference;
+          Hints.push_back(FixItHint::CreateRemoval(
+                            CharSourceRange::getTokenRange(Begin, Begin)));
+        }
+      } else if (NeedParen) {
+        Hints.push_back(FixItHint::CreateInsertion(Begin, "&("));
+        Hints.push_back(FixItHint::CreateInsertion(End, ")"));
+      } else {
+        Hints.push_back(FixItHint::CreateInsertion(Begin, "&"));
+      }
+
+      NumConversionsFixed++;
+      if (NumConversionsFixed == 1)
+        Kind = FixKind;
+      return true;
+    }
+  }
+
+  return false;
+}
diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index 93acf89..79aa305 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -3644,6 +3644,18 @@
                                AllowObjCWritebackConversion);
 }
 
+static bool TryCopyInitialization(const CanQualType FromQTy,
+                                  const CanQualType ToQTy,
+                                  Sema &S,
+                                  SourceLocation Loc,
+                                  ExprValueKind FromVK) {
+  OpaqueValueExpr TmpExpr(Loc, FromQTy, FromVK);
+  ImplicitConversionSequence ICS =
+    TryCopyInitialization(S, &TmpExpr, ToQTy, true, true, false);
+
+  return !ICS.isBad();
+}
+
 /// TryObjectArgumentInitialization - Try to initialize the object
 /// parameter of the given member function (@c Method) from the
 /// expression @p From.
@@ -6729,108 +6741,6 @@
 
 namespace {
 
-/// Try to find a fix for the bad conversion. Populate the ConvFix structure
-/// on success. Produces the hints for the following cases:
-/// - The user forgot to apply * or & operator to one or more arguments.
-static bool TryToFixBadConversion(Sema &S,
-                                  const ImplicitConversionSequence &Conv,
-                                  OverloadCandidate::FixInfo &ConvFix) {
-  assert(Conv.isBad() && "Only try to fix a bad conversion.");
-
-  const Expr *Arg = Conv.Bad.FromExpr;
-  if (!Arg)
-    return false;
-
-  // The conversion is from argument type to parameter type.
-  const CanQualType FromQTy = S.Context.getCanonicalType(Conv.Bad
-                                                         .getFromType());
-  const CanQualType ToQTy = S.Context.getCanonicalType(Conv.Bad.getToType());
-  const SourceLocation Begin = Arg->getSourceRange().getBegin();
-  const SourceLocation End = S.PP.getLocForEndOfToken(Arg->getSourceRange()
-                                                      .getEnd());
-  bool NeedParen = true;
-  if (isa<ParenExpr>(Arg) ||
-      isa<DeclRefExpr>(Arg) ||
-      isa<ArraySubscriptExpr>(Arg) ||
-      isa<CallExpr>(Arg) ||
-      isa<MemberExpr>(Arg))
-    NeedParen = false;
-  if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(Arg))
-    if (UO->isPostfix())
-      NeedParen = false;
-
-  // Check if the argument needs to be dereferenced
-  // (type * -> type) or (type * -> type &).
-  if (const PointerType *FromPtrTy = dyn_cast<PointerType>(FromQTy)) {
-    // Try to construct an implicit conversion from argument type to the
-    // parameter type.
-    OpaqueValueExpr TmpExpr(Arg->getExprLoc(), FromPtrTy->getPointeeType(),
-                            VK_LValue);
-    ImplicitConversionSequence ICS =
-      TryCopyInitialization(S, &TmpExpr, ToQTy, true, true, false);
-
-    OverloadFixItKind FixKind = OFIK_Dereference;
-    if (!ICS.isBad()) {
-      // Do not suggest dereferencing a Null pointer.
-      if (Arg->IgnoreParenCasts()->
-          isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNotNull))
-        return false;
-
-      if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(Arg)) {
-        if (UO->getOpcode() == UO_AddrOf) {
-          ConvFix.Hints.push_back(
-                               FixItHint::CreateRemoval(Arg->getSourceRange()));
-          FixKind = OFIK_RemoveTakeAddress;
-        }
-      } else if (NeedParen) {
-        ConvFix.Hints.push_back(FixItHint::CreateInsertion(Begin, "*("));
-        ConvFix.Hints.push_back(FixItHint::CreateInsertion(End, ")"));
-      } else {
-        ConvFix.Hints.push_back(FixItHint::CreateInsertion(Begin, "*"));
-      }
-      ConvFix.NumConversionsFixed++;
-      if (ConvFix.NumConversionsFixed == 1)
-        ConvFix.Kind = FixKind;
-      return true;
-    }
-  }
-
-  // Check if the pointer to the argument needs to be passed
-  // (type -> type *) or (type & -> type *).
-  if (isa<PointerType>(ToQTy)) {
-    // Only suggest taking address of L-values.
-    if (!Arg->isLValue() || Arg->getObjectKind() != OK_Ordinary)
-      return false;
-
-    OpaqueValueExpr TmpExpr(Arg->getExprLoc(),
-                            S.Context.getPointerType(FromQTy), VK_RValue);
-    ImplicitConversionSequence ICS =
-      TryCopyInitialization(S, &TmpExpr, ToQTy, true, true, false);
-
-    OverloadFixItKind FixKind = OFIK_TakeAddress;
-    if (!ICS.isBad()) {
-
-      if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(Arg)) {
-        if (UO->getOpcode() == UO_Deref) {
-          ConvFix.Hints.push_back(
-                               FixItHint::CreateRemoval(Arg->getSourceRange()));
-          FixKind = OFIK_RemoveDereference;
-        }
-      } else if (NeedParen) {
-        ConvFix.Hints.push_back(FixItHint::CreateInsertion(Begin, "&("));
-        ConvFix.Hints.push_back(FixItHint::CreateInsertion(End, ")"));
-      } else {
-        ConvFix.Hints.push_back(FixItHint::CreateInsertion(Begin, "&"));
-      }
-      ConvFix.NumConversionsFixed++;
-      if (ConvFix.NumConversionsFixed == 1)
-        ConvFix.Kind = FixKind;
-      return true;
-    }
-  }
-  return false;
-}
-
 void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) {
   const ImplicitConversionSequence &Conv = Cand->Conversions[I];
   assert(Conv.isBad());
@@ -7015,7 +6925,6 @@
       }
   }
   
-  // TODO: specialize more based on the kind of mismatch
   // Emit the generic diagnostic and, optionally, add the hints to it.
   PartialDiagnostic FDiag = S.PDiag(diag::note_ovl_candidate_bad_conv);
   FDiag << (unsigned) FnKind << FnDesc
@@ -7446,6 +7355,8 @@
 
   // We only want the FixIts if all the arguments can be corrected.
   bool Unfixable = false;
+  // Use a implicit copy initialization to check conversion fixes.
+  Cand->Fix.setConversionChecker(TryCopyInitialization);
 
   // Skip forward to the first bad conversion.
   unsigned ConvIdx = (Cand->IgnoreObjectArgument ? 1 : 0);
@@ -7454,9 +7365,7 @@
     assert(ConvIdx != ConvCount && "no bad conversion in candidate");
     ConvIdx++;
     if (Cand->Conversions[ConvIdx - 1].isBad()) {
-      if ((Unfixable = !TryToFixBadConversion(S, Cand->Conversions[ConvIdx - 1],
-                                                 Cand->Fix)))
-        Cand->Fix.Hints.clear();
+      Unfixable = !Cand->TryToFixBadConversion(ConvIdx - 1, S);
       break;
     }
   }
@@ -7512,17 +7421,11 @@
                                   S.getLangOptions().ObjCAutoRefCount);
       // Store the FixIt in the candidate if it exists.
       if (!Unfixable && Cand->Conversions[ConvIdx].isBad())
-        Unfixable = !TryToFixBadConversion(S, Cand->Conversions[ConvIdx],
-                                              Cand->Fix);
+        Unfixable = !Cand->TryToFixBadConversion(ConvIdx, S);
     }
     else
       Cand->Conversions[ConvIdx].setEllipsis();
   }
-
-  if (Unfixable) {
-    Cand->Fix.Hints.clear();
-    Cand->Fix.NumConversionsFixed = 0;
-  }
 }
 
 } // end anonymous namespace