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 @@ -12735,8 +12735,6 @@ } /// Attempts to recover from a call where no functions were found. -/// -/// Returns true if new candidates were found. static ExprResult BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE, @@ -12793,7 +12791,7 @@ return ExprError(); } - // Build an implicit member call if appropriate. Just drop the + // Build an implicit member access expression if appropriate. Just drop the // casts and such from the call, we don't really care. ExprResult NewFn = ExprError(); if ((*R.begin())->isCXXClassMember()) @@ -12808,12 +12806,19 @@ if (NewFn.isInvalid()) return ExprError(); - // This shouldn't cause an infinite loop because we're giving it - // an expression with viable lookup results, which should never - // end up here. - return SemaRef.BuildCallExpr(/*Scope*/ nullptr, NewFn.get(), LParenLoc, - MultiExprArg(Args.data(), Args.size()), - RParenLoc); + auto CallE = + SemaRef.BuildCallExpr(/*Scope*/ nullptr, NewFn.get(), LParenLoc, + MultiExprArg(Args.data(), Args.size()), RParenLoc); + if (CallE.isInvalid()) + return ExprError(); + // We now have recovered a callee. However, building a real call may lead to + // incorrect secondary diagnostics if our recovery wasn't correct. + // We keep the recovery behavior but suppress all following diagnostics by + // using RecoveryExpr. We deliberately drop the return type of the recovery + // function, and rely on clang's dependent mechanism to suppress following + // diagnostics. + return SemaRef.CreateRecoveryExpr(CallE.get()->getBeginLoc(), + CallE.get()->getEndLoc(), {CallE.get()}); } /// Constructs and populates an OverloadedCandidateSet from 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 @@ -271,3 +271,14 @@ // CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 2 invalid() ? 1 : 2; } + +void abcc(); +void TypoCorrection() { + // RecoveryExpr is always dependent-type in this case in order to suppress + // following diagnostics. + // CHECK: RecoveryExpr {{.*}} '' + // CHECK-NEXT: `-CallExpr {{.*}} 'void' + // CHECK-NEXT: `-ImplicitCastExpr + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'abcc' + abc(); +} diff --git a/clang/test/SemaCXX/typo-correction-delayed.cpp b/clang/test/SemaCXX/typo-correction-delayed.cpp --- a/clang/test/SemaCXX/typo-correction-delayed.cpp +++ b/clang/test/SemaCXX/typo-correction-delayed.cpp @@ -209,6 +209,15 @@ // expected-error-re@-1 {{use of undeclared identifier 'N'{{$}}}} } +namespace noSecondaryDiags { +void abcc(); // expected-note {{'abcc' declared here}} + +void test() { + // Verify the secondary diagnostic ".. convertible to 'bool'" is suppressed. + if (abc()) {} // expected-error {{use of undeclared identifier 'abc'; did you mean 'abcc'?}} +} +} + // PR 23285. This test must be at the end of the file to avoid additional, // unwanted diagnostics. // expected-error-re@+2 {{use of undeclared identifier 'uintmax_t'{{$}}}}