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 @@ -3846,32 +3846,36 @@ /// \param InitDecl A VarDecl to avoid because the Expr being corrected is its /// initializer. /// + /// \param RecoverUncorrectedTypos If true, when typo correction fails, it + /// will rebuild the given Expr with all TypoExprs degraded to RecoveryExprs. + /// /// \param Filter A function applied to a newly rebuilt Expr to determine if /// it is an acceptable/usable result from a single combination of typo /// corrections. As long as the filter returns ExprError, different /// combinations of corrections will be tried until all are exhausted. - ExprResult - CorrectDelayedTyposInExpr(Expr *E, VarDecl *InitDecl = nullptr, - llvm::function_ref Filter = - [](Expr *E) -> ExprResult { return E; }); - - ExprResult - CorrectDelayedTyposInExpr(Expr *E, - llvm::function_ref Filter) { - return CorrectDelayedTyposInExpr(E, nullptr, Filter); - } - - ExprResult - CorrectDelayedTyposInExpr(ExprResult ER, VarDecl *InitDecl = nullptr, - llvm::function_ref Filter = - [](Expr *E) -> ExprResult { return E; }) { - return ER.isInvalid() ? ER : CorrectDelayedTyposInExpr(ER.get(), Filter); + ExprResult CorrectDelayedTyposInExpr( + Expr *E, VarDecl *InitDecl = nullptr, + bool RecoverUncorrectedTypos = false, + llvm::function_ref Filter = + [](Expr *E) -> ExprResult { return E; }); + + ExprResult CorrectDelayedTyposInExpr( + ExprResult ER, VarDecl *InitDecl = nullptr, + bool RecoverUncorrectedTypos = false, + llvm::function_ref Filter = + [](Expr *E) -> ExprResult { return E; }) { + return ER.isInvalid() + ? ER + : CorrectDelayedTyposInExpr(ER.get(), InitDecl, + RecoverUncorrectedTypos, Filter); } ExprResult CorrectDelayedTyposInExpr(ExprResult ER, - llvm::function_ref Filter) { - return CorrectDelayedTyposInExpr(ER, nullptr, Filter); + llvm::function_ref Filter, + bool RecoverUncorrectedTypos = false) { + return CorrectDelayedTyposInExpr(ER, nullptr, RecoverUncorrectedTypos, + Filter); } void diagnoseTypo(const TypoCorrection &Correction, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -12011,7 +12011,8 @@ // Try to correct any TypoExprs in the initialization arguments. for (size_t Idx = 0; Idx < Args.size(); ++Idx) { ExprResult Res = CorrectDelayedTyposInExpr( - Args[Idx], VDecl, [this, Entity, Kind](Expr *E) { + Args[Idx], VDecl, /*RecoverUncorrectedTypos=*/true, + [this, Entity, Kind](Expr *E) { InitializationSequence Init(*this, Entity, Kind, MultiExprArg(E)); return Init.Failed() ? ExprError() : E; }); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8253,6 +8253,7 @@ ExprResult Sema::CorrectDelayedTyposInExpr(Expr *E, VarDecl *InitDecl, + bool RecoverUncorrectedTypos, llvm::function_ref Filter) { // If the current evaluation context indicates there are uncorrected typos // and the current expression isn't guaranteed to not have typos, try to @@ -8265,6 +8266,16 @@ TyposResolved -= DelayedTypos.size(); if (Result.isInvalid() || Result.get() != E) { ExprEvalContexts.back().NumTypos -= TyposResolved; + if (Result.isInvalid() && RecoverUncorrectedTypos) { + struct TyposReplace : TreeTransform { + TyposReplace(Sema &SemaRef) : TreeTransform(SemaRef) {} + ExprResult TransformTypoExpr(clang::TypoExpr *E) { + return this->SemaRef.CreateRecoveryExpr(E->getBeginLoc(), + E->getEndLoc(), {}); + } + } TT(*this); + return TT.TransformExpr(E); + } return Result; } assert(TyposResolved == 0 && "Corrected typo but got same Expr back?"); @@ -8303,20 +8314,10 @@ DiagnoseUnusedExprResult(FullExpr.get()); } - FullExpr = CorrectDelayedTyposInExpr(FullExpr.get()); - if (FullExpr.isInvalid()) { - // Typo-correction fails, we rebuild the broken AST with the typos degraded - // to RecoveryExpr. - struct TyposReplace : TreeTransform { - TyposReplace(Sema &SemaRef) : TreeTransform(SemaRef) {} - ExprResult TransformTypoExpr(TypoExpr *E) { - return this->SemaRef.CreateRecoveryExpr(E->getBeginLoc(), - E->getEndLoc(), {}); - } - } TT(*this); - - return TT.TransformExpr(FE); - } + FullExpr = CorrectDelayedTyposInExpr(FullExpr.get(), nullptr, + /*recoverUncorrectedTypos*/ true); + if (FullExpr.isInvalid()) + return ExprError(); CheckCompletedExpr(FullExpr.get(), CC, IsConstexpr); 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 @@ -25,8 +25,11 @@ // CHECK-NEXT: |-UnresolvedLookupExpr {{.*}} 'some_func' // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors s = some_func(undef1); - // CHECK: `-VarDecl {{.*}} invalid var 'int' - // FIXME: preserve the broken call. + + // CHECK: VarDecl {{.*}} var 'int' + // CHECK-NEXT: `-CallExpr {{.*}} '' contains-errors + // CHECK-NEXT: |-UnresolvedLookupExpr {{.*}} 'some_func' + // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors int var = some_func(undef1); } @@ -176,6 +179,12 @@ // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors // CHECK-NEXT: `-UnresolvedLookupExpr {{.*}} 'invalid' Bar b6 = Bar{invalid()}; + + // CHECK: `-VarDecl {{.*}} var1 + // CHECK-NEXT: `-BinaryOperator {{.*}} '' contains-errors + // CHECK-NEXT: |-RecoveryExpr {{.*}} '' contains-errors + // CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1 + int var1 = undef + 1; } void InitializerForAuto() { // CHECK: `-VarDecl {{.*}} invalid a 'auto' 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 @@ -149,7 +149,8 @@ } namespace PR21905 { -int (*a) () = (void)Z; // expected-error-re {{use of undeclared identifier 'Z'{{$}}}} +int (*a)() = (void)Z; // expected-error-re {{use of undeclared identifier 'Z'{{$}}}} \ + // expected-error {{cannot initialize a variable of type 'int (*)()' with an rvalue of type 'void'}} } namespace PR21947 {