diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3723,11 +3723,11 @@ SourceLocation RLoc, Expr *Base,Expr *Idx); - ExprResult - BuildCallToMemberFunction(Scope *S, Expr *MemExpr, - SourceLocation LParenLoc, - MultiExprArg Args, - SourceLocation RParenLoc); + ExprResult BuildCallToMemberFunction(Scope *S, Expr *MemExpr, + SourceLocation LParenLoc, + MultiExprArg Args, + SourceLocation RParenLoc, + bool allowRecovery = false); ExprResult BuildCallToObjectOfClassType(Scope *S, Expr *Object, SourceLocation LParenLoc, MultiExprArg Args, @@ -5238,7 +5238,8 @@ ExprResult BuildCallExpr(Scope *S, Expr *Fn, SourceLocation LParenLoc, MultiExprArg ArgExprs, SourceLocation RParenLoc, Expr *ExecConfig = nullptr, - bool IsExecConfig = false); + bool IsExecConfig = false, + bool allowRecovery = false); enum class AtomicArgumentOrder { API, AST }; ExprResult BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6308,8 +6308,8 @@ ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, MultiExprArg ArgExprs, SourceLocation RParenLoc, Expr *ExecConfig) { - ExprResult Call = - BuildCallExpr(Scope, Fn, LParenLoc, ArgExprs, RParenLoc, ExecConfig); + ExprResult Call = BuildCallExpr(Scope, Fn, LParenLoc, ArgExprs, RParenLoc, + ExecConfig, false, true); if (Call.isInvalid()) return Call; @@ -6337,7 +6337,8 @@ /// locations. ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, MultiExprArg ArgExprs, SourceLocation RParenLoc, - Expr *ExecConfig, bool IsExecConfig) { + Expr *ExecConfig, bool IsExecConfig, + bool allowRecovery) { // Since this might be a postfix expression, get rid of ParenListExprs. ExprResult Result = MaybeConvertParenListExprToParenExpr(Scope, Fn); if (Result.isInvalid()) return ExprError(); @@ -6397,7 +6398,7 @@ if (Fn->getType() == Context.BoundMemberTy) { return BuildCallToMemberFunction(Scope, Fn, LParenLoc, ArgExprs, - RParenLoc); + RParenLoc, allowRecovery); } } @@ -6416,7 +6417,7 @@ Scope, Fn, ULE, LParenLoc, ArgExprs, RParenLoc, ExecConfig, /*AllowTypoCorrection=*/true, find.IsAddressOfOperand); return BuildCallToMemberFunction(Scope, Fn, LParenLoc, ArgExprs, - RParenLoc); + RParenLoc, allowRecovery); } } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -14154,11 +14154,11 @@ /// parameter). The caller needs to validate that the member /// expression refers to a non-static member function or an overloaded /// member function. -ExprResult -Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, - SourceLocation LParenLoc, - MultiExprArg Args, - SourceLocation RParenLoc) { +ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, + SourceLocation LParenLoc, + MultiExprArg Args, + SourceLocation RParenLoc, + bool allowRecovery) { assert(MemExprE->getType() == Context.BoundMemberTy || MemExprE->getType() == Context.OverloadTy); @@ -14215,6 +14215,14 @@ return MaybeBindToTemporary(call); } + auto BuildRecoveryExpr = [&](QualType Type) { + if (!allowRecovery) + return ExprError(); + std::vector SubExprs = {MemExprE}; + llvm::for_each(Args, [&SubExprs](Expr *E) { SubExprs.push_back(E); }); + return CreateRecoveryExpr(MemExprE->getBeginLoc(), RParenLoc, SubExprs, + Type); + }; if (isa(NakedMemExpr)) return CallExpr::Create(Context, MemExprE, Args, Context.VoidTy, VK_RValue, RParenLoc, CurFPFeatureOverrides()); @@ -14362,7 +14370,7 @@ // Check for a valid return type. if (CheckCallReturnType(Method->getReturnType(), MemExpr->getMemberLoc(), TheCall, Method)) - return ExprError(); + return BuildRecoveryExpr(ResultType); // Convert the object argument (for a non-static member function call). // We only need to do this if there was actually an overload; otherwise @@ -14379,7 +14387,7 @@ // Convert the rest of the arguments if (ConvertArgumentsForCall(TheCall, MemExpr, Method, Proto, Args, RParenLoc)) - return ExprError(); + return BuildRecoveryExpr(ResultType); DiagnoseSentinelCalls(Method, LParenLoc, Args); diff --git a/clang/test/AST/ast-dump-recovery.cpp b/clang/test/AST/ast-dump-recovery.cpp --- a/clang/test/AST/ast-dump-recovery.cpp +++ b/clang/test/AST/ast-dump-recovery.cpp @@ -42,6 +42,9 @@ // DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors int ambig_call = ambig_func(123); +class ForwardClass; +ForwardClass createFwd(); +auto s = createFwd(); // CHECK: VarDecl {{.*}} unresolved_call1 // CHECK-NEXT:`-RecoveryExpr {{.*}} '' contains-errors // CHECK-NEXT: `-UnresolvedLookupExpr {{.*}} 'bar' @@ -121,6 +124,23 @@ foo->func(x); } +struct Foo2 { + double func(); + class ForwardClass; + ForwardClass createFwd(); +}; +void test2(Foo2 f) { + // CHECK: RecoveryExpr {{.*}} 'double' + // CHECK-NEXT: |-MemberExpr {{.*}} '' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'f' + // CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1 + f.func(1); + // CHECK: RecoveryExpr {{.*}} 'Foo2::ForwardClass' + // CHECK-NEXT: `-MemberExpr {{.*}} '' .createFwd + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'f' + f.createFwd(); +} + // CHECK: |-AlignedAttr {{.*}} alignas // CHECK-NEXT:| `-RecoveryExpr {{.*}} contains-errors // CHECK-NEXT:| `-UnresolvedLookupExpr {{.*}} 'invalid'