From 65b34d7bc314c7d4b448164e1a889311bd30b375 Mon Sep 17 00:00:00 2001 From: Matt Beaumont-Gay Date: Tue, 22 Feb 2011 23:52:53 +0000 Subject: [PATCH] Clean up the error recovery at the bottom of Sema::LookupMemberExpr. This mostly just shuffles various possibilities for recovery into a more straightforward order, but also unifies a couple of diagnostics. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@126266 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 7 +- lib/Sema/SemaExpr.cpp | 207 ++++++++++----------- test/SemaCXX/PR7944.cpp | 2 +- test/SemaCXX/member-expr.cpp | 10 +- 4 files changed, 114 insertions(+), 112 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 0715eba69e..98523ee57f 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2399,13 +2399,10 @@ def err_typecheck_member_reference_type : Error< def err_typecheck_member_reference_unknown : Error< "cannot refer to member %0 in %1 with '%select{.|->}2'">; def err_member_reference_needs_call : Error< - "base of member reference is an overloaded function; perhaps you meant " - "to call %select{it|the 0-argument overload}0?">; + "base of member reference is %select{a function|an overloaded function}0; " + "perhaps you meant to call it%select{| with no arguments}1?">; def note_member_ref_possible_intended_overload : Note< "possibly valid overload here">; -def err_member_reference_needs_call_zero_arg : Error< - "base of member reference has function type %0; perhaps you meant to call " - "this function with '()'?">; def warn_subscript_is_char : Warning<"array subscript is of type 'char'">, InGroup, DefaultIgnore; diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 4ac3538764..d3a8eb6a87 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -3980,131 +3980,130 @@ Sema::LookupMemberExpr(LookupResult &R, Expr *&BaseExpr, // Failure cases. fail: - // There's a possible road to recovery for function types. - const FunctionType *Fun = 0; - SourceLocation ParenInsertionLoc = - PP.getLocForEndOfToken(BaseExpr->getLocEnd()); - + // Recover from dot accesses to pointers, e.g.: + // type *foo; + // foo.bar + // This is actually well-formed in two cases: + // - 'type' is an Objective C type + // - 'bar' is a pseudo-destructor name which happens to refer to + // the appropriate pointer type if (const PointerType *Ptr = BaseType->getAs()) { - if ((Fun = Ptr->getPointeeType()->getAs())) { - // fall out, handled below. - - // Recover from dot accesses to pointers, e.g.: - // type *foo; - // foo.bar - // This is actually well-formed in two cases: - // - 'type' is an Objective C type - // - 'bar' is a pseudo-destructor name which happens to refer to - // the appropriate pointer type - } else if (!IsArrow && Ptr->getPointeeType()->isRecordType() && - MemberName.getNameKind() != DeclarationName::CXXDestructorName) { + if (!IsArrow && Ptr->getPointeeType()->isRecordType() && + MemberName.getNameKind() != DeclarationName::CXXDestructorName) { Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) - << BaseType << int(IsArrow) << BaseExpr->getSourceRange() - << FixItHint::CreateReplacement(OpLoc, "->"); + << BaseType << int(IsArrow) << BaseExpr->getSourceRange() + << FixItHint::CreateReplacement(OpLoc, "->"); // Recurse as an -> access. IsArrow = true; return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, ObjCImpDecl, HasTemplateArgs); } - } else { - Fun = BaseType->getAs(); - } - - // If the user is trying to apply -> or . to a function pointer - // type, it's probably because they forgot parentheses to call that - // function. Suggest the addition of those parentheses, build the - // call, and continue on. - if (Fun || BaseType == Context.OverloadTy) { - bool TryCall; - if (BaseType == Context.OverloadTy) { - // Plunder the overload set for something that would make the member - // expression valid. - const OverloadExpr *Overloads = cast(BaseExpr); - UnresolvedSet<4> CandidateOverloads; - bool HasZeroArgCandidateOverload = false; - for (OverloadExpr::decls_iterator it = Overloads->decls_begin(), - DeclsEnd = Overloads->decls_end(); it != DeclsEnd; ++it) { - const FunctionDecl *OverloadDecl = cast(*it); - QualType ResultTy = OverloadDecl->getResultType(); - if ((!IsArrow && ResultTy->isRecordType()) || - (IsArrow && ResultTy->isPointerType() && - ResultTy->getPointeeType()->isRecordType())) { - CandidateOverloads.addDecl(*it); - if (OverloadDecl->getNumParams() == 0) { - HasZeroArgCandidateOverload = true; - } + } + + // 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)) { + AllOverloads.append(Overloads->decls_begin(), Overloads->decls_end()); + TryCall = true; + Overloaded = true; + } else if (DeclRefExpr *DeclRef = dyn_cast(BaseExpr)) { + 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) { + const FunctionDecl *OverloadDecl = 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 (HasZeroArgCandidateOverload && CandidateOverloads.size() == 1) { - // We have one reasonable overload, and there's only one way to call it, - // so emit a fixit and try to recover - Diag(ParenInsertionLoc, diag::err_member_reference_needs_call) - << 1 - << BaseExpr->getSourceRange() - << FixItHint::CreateInsertion(ParenInsertionLoc, "()"); - TryCall = true; - } else { - Diag(BaseExpr->getExprLoc(), diag::err_member_reference_needs_call) - << 0 - << BaseExpr->getSourceRange(); - int CandidateOverloadCount = CandidateOverloads.size(); - int I; - for (I = 0; I < CandidateOverloadCount; ++I) { - // FIXME: Magic number for max shown overloads stolen from - // OverloadCandidateSet::NoteCandidates. - if (I >= 4 && Diags.getShowOverloads() == Diagnostic::Ovl_Best) { - break; - } - Diag(CandidateOverloads[I].getDecl()->getSourceRange().getBegin(), - diag::note_member_ref_possible_intended_overload); - } - if (I != CandidateOverloadCount) { - Diag(BaseExpr->getExprLoc(), diag::note_ovl_too_many_candidates) - << int(CandidateOverloadCount - I); + } + + if (!HasViableZeroArgOverload || ViableOverloads.size() != 1) { + Diag(BaseExpr->getExprLoc(), diag::err_member_reference_needs_call) + << 1 << 0 + << BaseExpr->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; } - return ExprError(); + Diag(ViableOverloads[I].getDecl()->getSourceRange().getBegin(), + diag::note_member_ref_possible_intended_overload); } - } else { - if (const FunctionProtoType *FPT = dyn_cast(Fun)) { - TryCall = (FPT->getNumArgs() == 0); - } else { - TryCall = true; + if (I != ViableOverloadCount) { + Diag(BaseExpr->getExprLoc(), diag::note_ovl_too_many_candidates) + << int(ViableOverloadCount - I); } - - if (TryCall) { - QualType ResultTy = Fun->getResultType(); - TryCall = (!IsArrow && ResultTy->isRecordType()) || - (IsArrow && ResultTy->isPointerType() && - ResultTy->getAs()->getPointeeType()->isRecordType()); + 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; } - if (TryCall) { - if (Fun) { - Diag(BaseExpr->getExprLoc(), - diag::err_member_reference_needs_call_zero_arg) - << QualType(Fun, 0) - << FixItHint::CreateInsertion(ParenInsertionLoc, "()"); + 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()); + } } - - ExprResult NewBase - = ActOnCallExpr(0, BaseExpr, ParenInsertionLoc, - MultiExprArg(*this, 0, 0), ParenInsertionLoc); - if (NewBase.isInvalid()) - return ExprError(); - BaseExpr = NewBase.takeAs(); - - - DefaultFunctionArrayConversion(BaseExpr); - BaseType = BaseExpr->getType(); - - 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->getLocEnd()); + Diag(BaseExpr->getExprLoc(), diag::err_member_reference_needs_call) + << int(Overloaded) << 1 + << BaseExpr->getSourceRange() + << FixItHint::CreateInsertion(ParenInsertionLoc, "()"); + ExprResult NewBase = ActOnCallExpr(0, BaseExpr, ParenInsertionLoc, + MultiExprArg(*this, 0, 0), + ParenInsertionLoc); + if (NewBase.isInvalid()) + return ExprError(); + BaseExpr = NewBase.takeAs(); + DefaultFunctionArrayConversion(BaseExpr); + return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, + ObjCImpDecl, HasTemplateArgs); + } + Diag(MemberLoc, diag::err_typecheck_member_reference_struct_union) << BaseType << BaseExpr->getSourceRange(); diff --git a/test/SemaCXX/PR7944.cpp b/test/SemaCXX/PR7944.cpp index fc52d101e8..a998a15c3e 100644 --- a/test/SemaCXX/PR7944.cpp +++ b/test/SemaCXX/PR7944.cpp @@ -8,5 +8,5 @@ struct A { B* b() { return new B; } }; void g() { A a; - MACRO(a.b->f()); // expected-error{{base of member reference has function type}} + MACRO(a.b->f()); // expected-error{{base of member reference is a function}} } diff --git a/test/SemaCXX/member-expr.cpp b/test/SemaCXX/member-expr.cpp index a4a39d7801..3c3eb04e08 100644 --- a/test/SemaCXX/member-expr.cpp +++ b/test/SemaCXX/member-expr.cpp @@ -28,7 +28,7 @@ struct B { A *f0(); }; int f0(B *b) { - return b->f0->f0; // expected-error{{perhaps you meant to call this function}} + return b->f0->f0; // expected-error{{perhaps you meant to call it with no arguments}} } int i; @@ -121,7 +121,7 @@ namespace PR9025 { S fun(); int fun(int i); int g() { - return fun.x; // expected-error{{base of member reference is an overloaded function; perhaps you meant to call the 0-argument overload?}} + 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}} @@ -129,4 +129,10 @@ namespace PR9025 { int g2() { return fun2.x; // expected-error{{base of member reference is an overloaded function; perhaps you meant to call it?}} } + + S fun3(int i=0); + int fun3(int i, int j); + int g3() { + return fun3.x; // expected-error{{base of member reference is an overloaded function; perhaps you meant to call it with no arguments?}} + } } -- 2.40.0