From: Matt Beaumont-Gay Date: Wed, 4 May 2011 22:10:40 +0000 (+0000) Subject: Implement Sema::isExprCallable. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c9366ba8fff6461a5b7f0fd2626d1bce3e98e629;p=clang Implement Sema::isExprCallable. We can use this to produce nice diagnostics (and try to fixit-and-recover) in various cases where we might see "MyFunction" instead of "MyFunction()". The changes in SemaExpr are an example of how to use isExprCallable. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130878 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index faa0ac487c..b528042345 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2074,7 +2074,14 @@ public: void MarkDeclarationReferenced(SourceLocation Loc, Decl *D); void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T); void MarkDeclarationsReferencedInExpr(Expr *E); - + + /// \brief Figure out if an expression could be turned into a call. + bool isExprCallable(const Expr &E, QualType &ZeroArgCallReturnTy, + UnresolvedSetImpl &NonTemplateOverloads); + /// \brief Give notes for a set of overloads. + void NoteOverloads(const UnresolvedSetImpl &Overloads, + const SourceLocation FinalNoteLoc); + /// \brief Conditionally issue a diagnostic based on the current /// evaluation context. /// diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 7153939151..ec2c739228 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -31,6 +31,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/StmtCXX.h" #include "clang/Lex/Preprocessor.h" #include "clang/Basic/PartialDiagnostic.h" @@ -766,3 +767,102 @@ void PrettyDeclStackTraceEntry::print(llvm::raw_ostream &OS) const { OS << '\n'; } + +/// \brief Figure out if an expression could be turned into a call. +/// +/// Use this when trying to recover from an error where the programmer may have +/// written just the name of a function instead of actually calling it. +/// +/// \param E - The expression to examine. +/// \param ZeroArgCallReturnTy - If the expression can be turned into a call +/// with no arguments, this parameter is set to the type returned by such a +/// call; otherwise, it is set to an empty QualType. +/// \param NonTemplateOverloads - 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 &NonTemplateOverloads) { + ZeroArgCallReturnTy = QualType(); + NonTemplateOverloads.clear(); + if (const OverloadExpr *Overloads = dyn_cast(&E)) { + for (OverloadExpr::decls_iterator it = Overloads->decls_begin(), + DeclsEnd = Overloads->decls_end(); it != DeclsEnd; ++it) { + // Our overload set may include TemplateDecls, which we'll ignore for our + // present purpose. + if (const FunctionDecl *OverloadDecl = dyn_cast(*it)) { + NonTemplateOverloads.addDecl(*it); + if (OverloadDecl->getMinRequiredArguments() == 0) + ZeroArgCallReturnTy = OverloadDecl->getResultType(); + } + } + return true; + } + + if (const DeclRefExpr *DeclRef = dyn_cast(&E)) { + if (const FunctionDecl *Fun = dyn_cast(DeclRef->getDecl())) { + if (Fun->getMinRequiredArguments() == 0) + ZeroArgCallReturnTy = Fun->getResultType(); + return true; + } + } + + // We don't have an expression that's convenient to get a FunctionDecl from, + // but we can at least check if the type is "function of 0 arguments". + QualType ExprTy = E.getType(); + const FunctionType *FunTy = NULL; + if (const PointerType *Ptr = ExprTy->getAs()) + FunTy = Ptr->getPointeeType()->getAs(); + else if (const ReferenceType *Ref = ExprTy->getAs()) + FunTy = Ref->getPointeeType()->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)) { + if (FPT->getNumArgs() == 0) + ZeroArgCallReturnTy = FunTy->getResultType(); + return true; + } + return false; +} + +/// \brief Give notes for a set of overloads. +/// +/// A companion to isExprCallable. 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. +/// +/// \param Overloads - The overloads to note. +/// \param FinalNoteLoc - If we've suppressed printing some overloads due to +/// -fshow-overloads=best, this is the location to attach to the note about too +/// many candidates. Typically this will be the location of the original +/// ill-formed expression. +void Sema::NoteOverloads(const UnresolvedSetImpl &Overloads, + const SourceLocation FinalNoteLoc) { + int ShownOverloads = 0; + int SuppressedOverloads = 0; + for (UnresolvedSetImpl::iterator It = Overloads.begin(), + DeclsEnd = Overloads.end(); It != DeclsEnd; ++It) { + // FIXME: Magic number for max shown overloads stolen from + // OverloadCandidateSet::NoteCandidates. + if (ShownOverloads >= 4 && + Diags.getShowOverloads() == Diagnostic::Ovl_Best) { + ++SuppressedOverloads; + continue; + } + Diag(cast(*It)->getSourceRange().getBegin(), + diag::note_member_ref_possible_intended_overload); + ++ShownOverloads; + } + if (SuppressedOverloads) + Diag(FinalNoteLoc, diag::note_ovl_too_many_candidates) + << SuppressedOverloads; +} diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index afcde44814..56abdfbcd3 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -4377,120 +4377,52 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, // If the user is trying to apply -> or . to a function name, it's probably // because they forgot parentheses to call that function. - bool TryCall = false; - bool Overloaded = false; - UnresolvedSet<8> AllOverloads; - if (const OverloadExpr *Overloads = dyn_cast(BaseExpr.get())) { - AllOverloads.append(Overloads->decls_begin(), Overloads->decls_end()); - TryCall = true; - Overloaded = true; - } else if (DeclRefExpr *DeclRef = dyn_cast(BaseExpr.get())) { - if (FunctionDecl* Fun = dyn_cast(DeclRef->getDecl())) { - AllOverloads.addDecl(Fun); - TryCall = true; - } - } - - if (TryCall) { - // Plunder the overload set for something that would make the member - // expression valid. - UnresolvedSet<4> ViableOverloads; - bool HasViableZeroArgOverload = false; - for (OverloadExpr::decls_iterator it = AllOverloads.begin(), - DeclsEnd = AllOverloads.end(); it != DeclsEnd; ++it) { - // Our overload set may include TemplateDecls, which we'll ignore for the - // purposes of determining whether we can issue a '()' fixit. - if (const FunctionDecl *OverloadDecl = dyn_cast(*it)) { - QualType ResultTy = OverloadDecl->getResultType(); - if ((!IsArrow && ResultTy->isRecordType()) || - (IsArrow && ResultTy->isPointerType() && - ResultTy->getPointeeType()->isRecordType())) { - ViableOverloads.addDecl(*it); - if (OverloadDecl->getMinRequiredArguments() == 0) { - HasViableZeroArgOverload = true; - } - } - } - } - - if (!HasViableZeroArgOverload || ViableOverloads.size() != 1) { + QualType ZeroArgCallTy; + UnresolvedSet<4> Overloads; + if (isExprCallable(*BaseExpr.get(), ZeroArgCallTy, Overloads)) { + if (ZeroArgCallTy.isNull()) { Diag(BaseExpr.get()->getExprLoc(), diag::err_member_reference_needs_call) - << (AllOverloads.size() > 1) << 0 - << BaseExpr.get()->getSourceRange(); - int ViableOverloadCount = ViableOverloads.size(); - int I; - for (I = 0; I < ViableOverloadCount; ++I) { - // FIXME: Magic number for max shown overloads stolen from - // OverloadCandidateSet::NoteCandidates. - if (I >= 4 && Diags.getShowOverloads() == Diagnostic::Ovl_Best) { - break; - } - Diag(ViableOverloads[I].getDecl()->getSourceRange().getBegin(), - diag::note_member_ref_possible_intended_overload); - } - if (I != ViableOverloadCount) { - Diag(BaseExpr.get()->getExprLoc(), diag::note_ovl_too_many_candidates) - << int(ViableOverloadCount - I); + << (Overloads.size() > 1) << 0 << BaseExpr.get()->getSourceRange(); + UnresolvedSet<2> PlausibleOverloads; + for (OverloadExpr::decls_iterator It = Overloads.begin(), + DeclsEnd = Overloads.end(); It != DeclsEnd; ++It) { + const FunctionDecl *OverloadDecl = cast(*It); + QualType OverloadResultTy = OverloadDecl->getResultType(); + if ((!IsArrow && OverloadResultTy->isRecordType()) || + (IsArrow && OverloadResultTy->isPointerType() && + OverloadResultTy->getPointeeType()->isRecordType())) + PlausibleOverloads.addDecl(It.getDecl()); } + NoteOverloads(PlausibleOverloads, BaseExpr.get()->getExprLoc()); return ExprError(); } - } else { - // We don't have an expression that's convenient to get a Decl from, but we - // can at least check if the type is "function of 0 arguments which returns - // an acceptable type". - const FunctionType *Fun = NULL; - if (const PointerType *Ptr = BaseType->getAs()) { - if ((Fun = Ptr->getPointeeType()->getAs())) { - TryCall = true; - } - } else if ((Fun = BaseType->getAs())) { - TryCall = true; - } else if (BaseType == 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 fnType = Expr::findBoundMemberType(BaseExpr.get()); - if (!fnType.isNull()) { - TryCall = true; - Fun = fnType->castAs(); - } - } - - if (TryCall) { - if (const FunctionProtoType *FPT = dyn_cast(Fun)) { - if (FPT->getNumArgs() == 0) { - QualType ResultTy = Fun->getResultType(); - TryCall = (!IsArrow && ResultTy->isRecordType()) || - (IsArrow && ResultTy->isPointerType() && - ResultTy->getPointeeType()->isRecordType()); - } - } + if ((!IsArrow && ZeroArgCallTy->isRecordType()) || + (IsArrow && ZeroArgCallTy->isPointerType() && + ZeroArgCallTy->getPointeeType()->isRecordType())) { + // At this point, we know BaseExpr looks like it's potentially callable + // with 0 arguments, and that it returns something of a reasonable type, + // so we can emit a fixit and carry on pretending that BaseExpr was + // actually a CallExpr. + SourceLocation ParenInsertionLoc = + PP.getLocForEndOfToken(BaseExpr.get()->getLocEnd()); + Diag(BaseExpr.get()->getExprLoc(), diag::err_member_reference_needs_call) + << (Overloads.size() > 1) << 1 << BaseExpr.get()->getSourceRange() + << FixItHint::CreateInsertion(ParenInsertionLoc, "()"); + // FIXME: Try this before emitting the fixit, and suppress diagnostics + // while doing so. + ExprResult NewBase = + ActOnCallExpr(0, BaseExpr.take(), ParenInsertionLoc, + MultiExprArg(*this, 0, 0), + ParenInsertionLoc.getFileLocWithOffset(1)); + if (NewBase.isInvalid()) + return ExprError(); + BaseExpr = NewBase; + BaseExpr = DefaultFunctionArrayConversion(BaseExpr.take()); + return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, + ObjCImpDecl, HasTemplateArgs); } } - if (TryCall) { - // At this point, we know BaseExpr looks like it's potentially callable with - // 0 arguments, and that it returns something of a reasonable type, so we - // can emit a fixit and carry on pretending that BaseExpr was actually a - // CallExpr. - SourceLocation ParenInsertionLoc = - PP.getLocForEndOfToken(BaseExpr.get()->getLocEnd()); - Diag(BaseExpr.get()->getExprLoc(), diag::err_member_reference_needs_call) - << int(Overloaded) << 1 - << BaseExpr.get()->getSourceRange() - << FixItHint::CreateInsertion(ParenInsertionLoc, "()"); - ExprResult NewBase = ActOnCallExpr(0, BaseExpr.take(), ParenInsertionLoc, - MultiExprArg(*this, 0, 0), - ParenInsertionLoc); - if (NewBase.isInvalid()) - return ExprError(); - BaseExpr = NewBase; - BaseExpr = DefaultFunctionArrayConversion(BaseExpr.take()); - return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); - } - Diag(MemberLoc, diag::err_typecheck_member_reference_struct_union) << BaseType << BaseExpr.get()->getSourceRange(); diff --git a/test/SemaCXX/member-expr.cpp b/test/SemaCXX/member-expr.cpp index 68af4152ef..981bae7c77 100644 --- a/test/SemaCXX/member-expr.cpp +++ b/test/SemaCXX/member-expr.cpp @@ -124,10 +124,10 @@ namespace PR9025 { return fun.x; // expected-error{{base of member reference is an overloaded function; perhaps you meant to call it with no arguments?}} } - S fun2(); // expected-note{{possibly valid overload here}} - S fun2(int i); // expected-note{{possibly valid overload here}} + S fun2(); + S fun2(int i); int g2() { - return fun2.x; // expected-error{{base of member reference is an overloaded function; perhaps you meant to call it?}} + return fun2.x; // expected-error{{base of member reference is an overloaded function; perhaps you meant to call it with no arguments?}} } S fun3(int i=0); @@ -140,4 +140,10 @@ namespace PR9025 { int g4() { return fun4.x; // expected-error{{base of member reference is a function; perhaps you meant to call it?}} } + + S fun5(int i); // expected-note{{possibly valid overload here}} + S fun5(float f); // expected-note{{possibly valid overload here}} + int g5() { + return fun5.x; // expected-error{{base of member reference is an overloaded function; perhaps you meant to call it?}} + } }