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))
+ 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);
+
+ if (!ICS.isBad()) {
+ // Do not suggest dereferencing a Null pointer.
+ if (Arg->IgnoreParenCasts()->
+ isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNotNull))
+ return false;
+
+ 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 = OFIK_Dereference;
+ 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())
+ return false;
+
+ OpaqueValueExpr TmpExpr(Arg->getExprLoc(),
+ S.Context.getPointerType(FromQTy), VK_RValue);
+ ImplicitConversionSequence ICS =
+ TryCopyInitialization(S, &TmpExpr, ToQTy, true, true, false);
+ if (!ICS.isBad()) {
+ 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 = OFIK_TakeAddress;
+ return true;
+ }
+ }
+ return false;
+}
+
void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) {
const ImplicitConversionSequence &Conv = Cand->Conversions[I];
assert(Conv.isBad());
}
// TODO: specialize more based on the kind of mismatch
- S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_conv)
- << (unsigned) FnKind << FnDesc
+
+ // 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
<< (FromExpr ? FromExpr->getSourceRange() : SourceRange())
- << FromTy << ToTy << (unsigned) isObjectArgument << I+1;
+ << FromTy << ToTy << (unsigned) isObjectArgument << I + 1
+ << (unsigned) (Cand->Fix.Kind);
+
+ // If we can fix the conversion, suggest the FixIts.
+ for (llvm::SmallVector<FixItHint, 4>::iterator
+ HI = Cand->Fix.Hints.begin(), HE = Cand->Fix.Hints.end();
+ HI != HE; ++HI)
+ FDiag << *HI;
+ S.Diag(Fn->getLocation(), FDiag);
+
MaybeEmitInheritedConstructorNote(S, Fn);
}
if (R->FailureKind != ovl_fail_bad_conversion)
return true;
+ // The conversion that can be fixed with a smaller number of changes,
+ // comes first.
+ unsigned numLFixes = L->Fix.NumConversionsFixed;
+ unsigned numRFixes = R->Fix.NumConversionsFixed;
+ numLFixes = (numLFixes == 0) ? UINT_MAX : numLFixes;
+ numRFixes = (numRFixes == 0) ? UINT_MAX : numRFixes;
+ if (numLFixes != numRFixes)
+ if (numLFixes < numRFixes)
+ return true;
+ else
+ return false;
+
// If there's any ordering between the defined conversions...
// FIXME: this might not be transitive.
assert(L->Conversions.size() == R->Conversions.size());
};
/// CompleteNonViableCandidate - Normally, overload resolution only
-/// computes up to the first
+/// computes up to the first. Produces the FixIt set if possible.
void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
Expr **Args, unsigned NumArgs) {
assert(!Cand->Viable);
// Don't do anything on failures other than bad conversion.
if (Cand->FailureKind != ovl_fail_bad_conversion) return;
+ // We only want the FixIts if all the arguments can be corrected.
+ bool Unfixable = false;
+
// Skip forward to the first bad conversion.
unsigned ConvIdx = (Cand->IgnoreObjectArgument ? 1 : 0);
unsigned ConvCount = Cand->Conversions.size();
while (true) {
assert(ConvIdx != ConvCount && "no bad conversion in candidate");
ConvIdx++;
- if (Cand->Conversions[ConvIdx - 1].isBad())
+ if (Cand->Conversions[ConvIdx - 1].isBad()) {
+ if ((Unfixable = !TryToFixBadConversion(S, Cand->Conversions[ConvIdx - 1],
+ Cand->Fix)))
+ Cand->Fix.Hints.clear();
break;
+ }
}
if (ConvIdx == ConvCount)
// Fill in the rest of the conversions.
unsigned NumArgsInProto = Proto->getNumArgs();
for (; ConvIdx != ConvCount; ++ConvIdx, ++ArgIdx) {
- if (ArgIdx < NumArgsInProto)
+ if (ArgIdx < NumArgsInProto) {
Cand->Conversions[ConvIdx]
= TryCopyInitialization(S, Args[ArgIdx], Proto->getArgType(ArgIdx),
SuppressUserConversions,
/*InOverloadResolution=*/true,
/*AllowObjCWritebackConversion=*/
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);
+ }
else
Cand->Conversions[ConvIdx].setEllipsis();
}
+
+ if (Unfixable) {
+ Cand->Fix.Hints.clear();
+ Cand->Fix.NumConversionsFixed = 0;
+ }
}
} // end anonymous namespace
--- /dev/null
+// RUN: %clang_cc1 -fdiagnostics-parseable-fixits -x c++ %s 2> %t || true
+// RUN: FileCheck %s < %t
+// PR5941
+// END.
+
+/* Test fixits for * and & mismatch in function arguments.
+ * Since fixits are on the notes, they cannot be applied automatically. */
+
+typedef int intTy;
+typedef int intTy2;
+
+void f0(int *a);
+void f1(double *a);
+void f1(intTy &a);
+
+void f2(intTy2 *a) {
+// CHECK: error: no matching function for call to 'f1
+// CHECK: dereference the argument with *
+// CHECK: void f1(intTy &a);
+// CHECK: fix-it{{.*}}*(
+// CHECK-NEXT: fix-it{{.*}})
+// CHECK: void f1(double *a);
+ f1(a + 1);
+
+// This call cannot be fixed since without resulting in null pointer dereference.
+// CHECK: error: no matching function for call to 'f1
+// CHECK-NOT: take the address of the argument with &
+// CHECK-NOT: fix-it
+ f1((int *)0);
+}
+
+void f3(int &a) {
+// CHECK: error: no matching function for call to 'f0
+// CHECK: fix-it{{.*}}&
+ f0(a);
+}
+
+
+void m(int *a, const int *b); // match 2
+void m(double *a, int *b); // no match
+void m(int *a, double *b); // no match
+void m(intTy &a, int *b); // match 1
+
+void mcaller(intTy2 a, int b) {
+// CHECK: error: no matching function for call to 'm
+// CHECK: take the address of the argument with &
+// CHECK: fix-it{{.*}}&
+// CHECK: take the address of the argument with &
+// CHECK: fix-it{{.*}}&
+// CHECK: fix-it{{.*}}&
+ m(a, b);
+
+// This call cannot be fixed because (a + 1) is not an l-value.
+// CHECK: error: no matching function for call to 'm
+// CHECK-NOT: fix-it
+ m(a + 1, b);
+}
+
+// Test derived to base conversions.
+struct A {
+ int xx;
+};
+
+struct B : public A {
+ double y;
+};
+
+bool br(A &a);
+bool bp(A *a);
+bool dv(B b);
+
+void dbcaller(A *ptra, B *ptrb) {
+ B b;
+
+// CHECK: error: no matching function for call to 'br
+// CHECK: fix-it{{.*}}*
+ br(ptrb); // good
+// CHECK: error: no matching function for call to 'bp
+// CHECK: fix-it{{.*}}&
+ bp(b); // good
+
+// CHECK: error: no matching function for call to 'dv
+// CHECK-NOT: fix-it
+ dv(ptra); // bad: base to derived
+}
+
+// CHECK: errors generated