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 @@ -6309,7 +6309,8 @@ MultiExprArg ArgExprs, SourceLocation RParenLoc, Expr *ExecConfig) { ExprResult Call = - BuildCallExpr(Scope, Fn, LParenLoc, ArgExprs, RParenLoc, ExecConfig); + BuildCallExpr(Scope, Fn, LParenLoc, ArgExprs, RParenLoc, ExecConfig, + /*IsExecConfig=*/false, /*AllowRecovery=*/true); if (Call.isInvalid()) return Call; @@ -6337,7 +6338,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 +6399,7 @@ if (Fn->getType() == Context.BoundMemberTy) { return BuildCallToMemberFunction(Scope, Fn, LParenLoc, ArgExprs, - RParenLoc); + RParenLoc, AllowRecovery); } } @@ -6416,7 +6418,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,17 @@ return MaybeBindToTemporary(call); } + // We only try to build a recovery expr at this level if we can preserve + // the return type, otherwise we return ExprError() and let the caller + // recover. + 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 +14373,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 +14390,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 @@ -121,6 +121,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'