From e5323aa92145a909c0c4d644977a2756bbf4321a Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Fri, 21 Jun 2013 23:54:45 +0000 Subject: [PATCH] Provide suggested no-arg calls for overloaded member functions missing calls Reviewed by Richard Smith. llvm-svn: 184612 --- clang/include/clang/Sema/Sema.h | 4 +- clang/lib/Sema/Sema.cpp | 46 +++++++++++++------ clang/lib/Sema/SemaChecking.cpp | 2 +- .../SemaCXX/addr-of-overloaded-function.cpp | 28 ++++++++++- 4 files changed, 61 insertions(+), 19 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index f0bcc4015528..df46ddec5bdf 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3083,8 +3083,8 @@ public: bool (*IsPlausibleResult)(QualType) = 0); /// \brief Figure out if an expression could be turned into a call. - bool isExprCallable(const Expr &E, QualType &ZeroArgCallReturnTy, - UnresolvedSetImpl &NonTemplateOverloads); + bool tryExprAsCall(Expr &E, QualType &ZeroArgCallReturnTy, + UnresolvedSetImpl &NonTemplateOverloads); /// \brief Conditionally issue a diagnostic based on the current /// evaluation context. diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 954091d61779..3ddce77a883c 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1136,12 +1136,13 @@ void PrettyDeclStackTraceEntry::print(raw_ostream &OS) const { /// call; otherwise, it is set to an empty QualType. /// \param OverloadSet - If the expression is an overloaded function /// name, this parameter is populated with the decls of the various overloads. -bool Sema::isExprCallable(const Expr &E, QualType &ZeroArgCallReturnTy, - UnresolvedSetImpl &OverloadSet) { +bool Sema::tryExprAsCall(Expr &E, QualType &ZeroArgCallReturnTy, + UnresolvedSetImpl &OverloadSet) { ZeroArgCallReturnTy = QualType(); OverloadSet.clear(); const OverloadExpr *Overloads = NULL; + bool IsMemExpr = false; if (E.getType() == Context.OverloadTy) { OverloadExpr::FindResult FR = OverloadExpr::find(const_cast(&E)); @@ -1152,15 +1153,20 @@ bool Sema::isExprCallable(const Expr &E, QualType &ZeroArgCallReturnTy, Overloads = FR.Expression; } else if (E.getType() == Context.BoundMemberTy) { Overloads = dyn_cast(E.IgnoreParens()); + IsMemExpr = true; } + + bool Ambiguous = false; + if (Overloads) { - bool Ambiguous = false; for (OverloadExpr::decls_iterator it = Overloads->decls_begin(), DeclsEnd = Overloads->decls_end(); it != DeclsEnd; ++it) { OverloadSet.addDecl(*it); - // Check whether the function is a non-template which takes no + // Check whether the function is a non-template, non-member which takes no // arguments. + if (IsMemExpr) + continue; if (const FunctionDecl *OverloadDecl = dyn_cast((*it)->getUnderlyingDecl())) { if (OverloadDecl->getMinRequiredArguments() == 0) { @@ -1173,7 +1179,25 @@ bool Sema::isExprCallable(const Expr &E, QualType &ZeroArgCallReturnTy, } } - return !Ambiguous; + // If it's not a member, use better machinery to try to resolve the call + if (!IsMemExpr) + return !ZeroArgCallReturnTy.isNull(); + } + + // Attempt to call the member with no arguments - this will correctly handle + // member templates with defaults/deduction of template arguments, overloads + // with default arguments, etc. + if (IsMemExpr) { + bool Suppress = getDiagnostics().getSuppressAllDiagnostics(); + getDiagnostics().setSuppressAllDiagnostics(true); + ExprResult R = BuildCallToMemberFunction(NULL, &E, SourceLocation(), None, + SourceLocation()); + getDiagnostics().setSuppressAllDiagnostics(Suppress); + if (R.isUsable()) { + ZeroArgCallReturnTy = R.get()->getType(); + return true; + } + return false; } if (const DeclRefExpr *DeclRef = dyn_cast(E.IgnoreParens())) { @@ -1193,14 +1217,6 @@ bool Sema::isExprCallable(const Expr &E, QualType &ZeroArgCallReturnTy, FunTy = PointeeTy->getAs(); if (!FunTy) FunTy = ExprTy->getAs(); - if (!FunTy && ExprTy == Context.BoundMemberTy) { - // Look for the bound-member type. If it's still overloaded, give up, - // although we probably should have fallen into the OverloadExpr case above - // if we actually have an overloaded bound member. - QualType BoundMemberTy = Expr::findBoundMemberType(&E); - if (!BoundMemberTy.isNull()) - FunTy = BoundMemberTy->castAs(); - } if (const FunctionProtoType *FPT = dyn_cast_or_null(FunTy)) { @@ -1213,7 +1229,7 @@ bool Sema::isExprCallable(const Expr &E, QualType &ZeroArgCallReturnTy, /// \brief Give notes for a set of overloads. /// -/// A companion to isExprCallable. In cases when the name that the programmer +/// A companion to tryExprAsCall. In cases when the name that the programmer /// wrote was an overloaded function, we may be able to make some guesses about /// plausible overloads based on their return types; such guesses can be handed /// off to this method to be emitted as notes. @@ -1283,7 +1299,7 @@ bool Sema::tryToRecoverWithCall(ExprResult &E, const PartialDiagnostic &PD, QualType ZeroArgCallTy; UnresolvedSet<4> Overloads; - if (isExprCallable(*E.get(), ZeroArgCallTy, Overloads) && + if (tryExprAsCall(*E.get(), ZeroArgCallTy, Overloads) && !ZeroArgCallTy.isNull() && (!IsPlausibleResult || IsPlausibleResult(ZeroArgCallTy))) { // At this point, we know E is potentially callable with 0 diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 87fe76358914..96d8742b91d6 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4871,7 +4871,7 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T, << FixItHint::CreateInsertion(E->getExprLoc(), "&"); QualType ReturnType; UnresolvedSet<4> NonTemplateOverloads; - S.isExprCallable(*E, ReturnType, NonTemplateOverloads); + S.tryExprAsCall(*E, ReturnType, NonTemplateOverloads); if (!ReturnType.isNull() && ReturnType->isSpecificBuiltinType(BuiltinType::Bool)) S.Diag(E->getExprLoc(), diag::note_function_to_bool_call) diff --git a/clang/test/SemaCXX/addr-of-overloaded-function.cpp b/clang/test/SemaCXX/addr-of-overloaded-function.cpp index 95223f0f5e3b..3d007a9612aa 100644 --- a/clang/test/SemaCXX/addr-of-overloaded-function.cpp +++ b/clang/test/SemaCXX/addr-of-overloaded-function.cpp @@ -57,7 +57,7 @@ struct B struct C { C &getC() { - return makeAC; // expected-error-re{{reference to non-static member function must be called$}} + return makeAC; // expected-error{{reference to non-static member function must be called; did you mean to call it with no arguments?}} } // FIXME: filter by const so we can unambiguously suggest '()' & point to just the one candidate, probably @@ -70,6 +70,32 @@ struct C { void g() { int (&fp)() = f; // expected-error{{address of overloaded function 'f' does not match required type 'int ()'}} } + + template + void q1(int); // expected-note{{possible target for call}} + template + void q2(T t = T()); // expected-note{{possible target for call}} + template + void q3(); // expected-note{{possible target for call}} + template + void q4(); // expected-note{{possible target for call}} + template // expected-warning{{default template arguments for a function template are a C++11 extension}} + void q5(); // expected-note{{possible target for call}} + + void h() { + // Do not suggest '()' since an int argument is required + q1; // expected-error-re{{reference to non-static member function must be called$}} + // Suggest '()' since there's a default value for the only argument & the + // type argument is already provided + q2; // expected-error{{reference to non-static member function must be called; did you mean to call it with no arguments?}} + // Suggest '()' since no arguments are required & the type argument is + // already provided + q3; // expected-error{{reference to non-static member function must be called; did you mean to call it with no arguments?}} + // Do not suggest '()' since another type argument is required + q4; // expected-error-re{{reference to non-static member function must be called$}} + // Suggest '()' since the type parameter has a default value + q5; // expected-error{{reference to non-static member function must be called; did you mean to call it with no arguments?}} + } }; // PR6886