From 733edf9750a4893d5f50329ad68b3901935303a9 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Thu, 19 Mar 2020 16:30:40 +0100 Subject: [PATCH] [AST] Add RecoveryExpr to retain expressions on semantic errors Normally clang avoids creating expressions when it encounters semantic errors, even if the parser knows which expression to produce. This works well for the compiler. However, this is not ideal for source-level tools that have to deal with broken code, e.g. clangd is not able to provide navigation features even for names that compiler knows how to resolve. The new RecoveryExpr aims to capture the minimal set of information useful for the tools that need to deal with incorrect code: source range of the expression being dropped, subexpressions of the expression. We aim to make constructing RecoveryExprs as simple as possible to ensure writing code to avoid dropping expressions is easy. Producing RecoveryExprs can result in new code paths being taken in the frontend. In particular, clang can produce some new diagnostics now and we aim to suppress bogus ones based on Expr::containsErrors. We deliberately produce RecoveryExprs only in the parser for now to minimize the code affected by this patch. Producing RecoveryExprs in Sema potentially allows to preserve more information (e.g. type of an expression), but also results in more code being affected. E.g. SFINAE checks will have to take presence of RecoveryExprs into account. Initial implementation only works in C++ mode, as it relies on compiler postponing diagnostics on dependent expressions. C and ObjC often do not do this, so they require more work to make sure we do not produce too many bogus diagnostics on the new expressions. See documentation of RecoveryExpr for more details. original patch from Ilya This change is based on https://reviews.llvm.org/D61722 Reviewers: sammccall, rsmith Reviewed By: sammccall, rsmith Tags: #clang Differential Revision: https://reviews.llvm.org/D69330 --- clang/include/clang/AST/ComputeDependence.h | 2 + clang/include/clang/AST/Expr.h | 63 ++++++++++++++ clang/include/clang/AST/RecursiveASTVisitor.h | 1 + clang/include/clang/Basic/LangOptions.def | 2 + clang/include/clang/Basic/StmtNodes.td | 1 + clang/include/clang/Driver/CC1Options.td | 5 ++ clang/include/clang/Sema/Sema.h | 4 + .../include/clang/Serialization/ASTBitCodes.h | 3 + clang/lib/AST/ComputeDependence.cpp | 9 ++ clang/lib/AST/Expr.cpp | 29 +++++++ clang/lib/AST/ExprClassification.cpp | 1 + clang/lib/AST/ExprConstant.cpp | 1 + clang/lib/AST/ItaniumMangle.cpp | 3 +- clang/lib/AST/StmtPrinter.cpp | 11 +++ clang/lib/AST/StmtProfile.cpp | 2 + clang/lib/Frontend/CompilerInvocation.cpp | 2 + clang/lib/Parse/ParseExpr.cpp | 78 +++++++++++++---- clang/lib/Sema/SemaDecl.cpp | 4 +- clang/lib/Sema/SemaExceptionSpec.cpp | 1 + clang/lib/Sema/SemaExpr.cpp | 14 +++ clang/lib/Sema/TreeTransform.h | 23 +++++ clang/lib/Serialization/ASTReaderStmt.cpp | 18 ++++ clang/lib/Serialization/ASTWriterDecl.cpp | 8 +- clang/lib/Serialization/ASTWriterStmt.cpp | 10 +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + clang/test/AST/ast-dump-expr-errors.cpp | 46 ++++++++++ clang/test/AST/ast-dump-recovery.cpp | 85 +++++++++++++++++++ clang/test/Index/getcursor-recovery.cpp | 16 ++++ .../SemaTemplate/recovery-tree-transform.cpp | 4 + clang/tools/libclang/CXCursor.cpp | 1 + 30 files changed, 424 insertions(+), 24 deletions(-) create mode 100644 clang/test/AST/ast-dump-expr-errors.cpp create mode 100644 clang/test/AST/ast-dump-recovery.cpp create mode 100644 clang/test/Index/getcursor-recovery.cpp create mode 100644 clang/test/SemaTemplate/recovery-tree-transform.cpp diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index 69ccb6c676e54..02f826438d4df 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -45,6 +45,7 @@ class ExtVectorElementExpr; class BlockExpr; class AsTypeExpr; class DeclRefExpr; +class RecoveryExpr; class CXXRewrittenBinaryOperator; class CXXStdInitializerListExpr; class CXXTypeidExpr; @@ -122,6 +123,7 @@ ExprDependence computeDependence(ExtVectorElementExpr *E); ExprDependence computeDependence(BlockExpr *E); ExprDependence computeDependence(AsTypeExpr *E); ExprDependence computeDependence(DeclRefExpr *E, const ASTContext &Ctx); +ExprDependence computeDependence(RecoveryExpr *E); ExprDependence computeDependence(CXXRewrittenBinaryOperator *E); ExprDependence computeDependence(CXXStdInitializerListExpr *E); ExprDependence computeDependence(CXXTypeidExpr *E); diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index f960fbd9ce31c..e27d7f04093bb 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -5911,6 +5911,69 @@ class TypoExpr : public Expr { } }; + +/// Frontend produces RecoveryExprs on semantic errors that prevent creating +/// other well-formed expressions. E.g. when type-checking of a binary operator +/// fails, we cannot produce a BinaryOperator expression. Instead, we can choose +/// to produce a recovery expression storing left and right operands. +/// +/// RecoveryExpr does not have any semantic meaning in C++, it is only useful to +/// preserve expressions in AST that would otherwise be dropped. It captures +/// subexpressions of some expression that we could not construct and source +/// range covered by the expression. +/// +/// For now, RecoveryExpr is type-, value- and instantiation-dependent to take +/// advantage of existing machinery to deal with dependent code in C++, e.g. +/// RecoveryExpr is preserved in `decltype()` as part of the +/// `DependentDecltypeType`. In addition to that, clang does not report most +/// errors on dependent expressions, so we get rid of bogus errors for free. +/// However, note that unlike other dependent expressions, RecoveryExpr can be +/// produced in non-template contexts. +/// +/// One can also reliably suppress all bogus errors on expressions containing +/// recovery expressions by examining results of Expr::containsErrors(). +class RecoveryExpr final : public Expr, + private llvm::TrailingObjects { +public: + static RecoveryExpr *Create(ASTContext &Ctx, SourceLocation BeginLoc, + SourceLocation EndLoc, ArrayRef SubExprs); + static RecoveryExpr *CreateEmpty(ASTContext &Ctx, unsigned NumSubExprs); + + ArrayRef subExpressions() { + auto *B = getTrailingObjects(); + return llvm::makeArrayRef(B, B + NumExprs); + } + + ArrayRef subExpressions() const { + return const_cast(this)->subExpressions(); + } + + child_range children() { + Stmt **B = reinterpret_cast(getTrailingObjects()); + return child_range(B, B + NumExprs); + } + + SourceLocation getBeginLoc() const { return BeginLoc; } + SourceLocation getEndLoc() const { return EndLoc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == RecoveryExprClass; + } + +private: + RecoveryExpr(ASTContext &Ctx, SourceLocation BeginLoc, SourceLocation EndLoc, + ArrayRef SubExprs); + RecoveryExpr(EmptyShell Empty) : Expr(RecoveryExprClass, Empty) {} + + size_t numTrailingObjects(OverloadToken) const { return NumExprs; } + + SourceLocation BeginLoc, EndLoc; + unsigned NumExprs; + friend TrailingObjects; + friend class ASTStmtReader; + friend class ASTStmtWriter; +}; + } // end namespace clang #endif // LLVM_CLANG_AST_EXPR_H diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 4bd489f4ba6db..2f598564f3077 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2668,6 +2668,7 @@ DEF_TRAVERSE_STMT(CXXRewrittenBinaryOperator, { }) DEF_TRAVERSE_STMT(OpaqueValueExpr, {}) DEF_TRAVERSE_STMT(TypoExpr, {}) +DEF_TRAVERSE_STMT(RecoveryExpr, {}) DEF_TRAVERSE_STMT(CUDAKernelCallExpr, {}) // These operators (all of them) do not need any action except diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 4ba8c766269c2..6152e227d5998 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -148,6 +148,8 @@ LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template t LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes") +COMPATIBLE_LANGOPT(RecoveryAST, 1, 0, "Preserve expressions in AST when encountering errors") + BENIGN_LANGOPT(ThreadsafeStatics , 1, 1, "thread-safe static initializers") LANGOPT(POSIXThreads , 1, 0, "POSIX thread support") LANGOPT(Blocks , 1, 0, "blocks extension to C") diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index a617ab80021f0..478179d4131f4 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -195,6 +195,7 @@ def ConvertVectorExpr : StmtNode; def BlockExpr : StmtNode; def OpaqueValueExpr : StmtNode; def TypoExpr : StmtNode; +def RecoveryExpr : StmtNode; def BuiltinBitCastExpr : StmtNode; // Microsoft Extensions. diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index 66a443b0d0f49..a40923392c94c 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -566,6 +566,11 @@ def fno_concept_satisfaction_caching : Flag<["-"], "fno-concept-satisfaction-caching">, HelpText<"Disable satisfaction caching for C++2a Concepts.">; +def frecovery_ast : Flag<["-"], "frecovery-ast">, + HelpText<"Preserve expressions in AST rather than dropping them when " + "encountering semantic errors">; +def fno_recovery_ast : Flag<["-"], "fno-recovery-ast">; + let Group = Action_Group in { def Eonly : Flag<["-"], "Eonly">, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 9f487e37a9f19..4eaf09a3f92d2 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3885,6 +3885,10 @@ class Sema final { void DiagnoseAmbiguousLookup(LookupResult &Result); //@} + /// Attempts to produce a RecoveryExpr after some AST node cannot be created. + ExprResult CreateRecoveryExpr(SourceLocation Begin, SourceLocation End, + ArrayRef SubExprs); + ObjCInterfaceDecl *getObjCInterfaceDecl(IdentifierInfo *&Id, SourceLocation IdLoc, bool TypoCorrection = false); diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index b28deaa8a0216..f185c1a16834b 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1634,6 +1634,9 @@ namespace serialization { /// An AtomicExpr record. EXPR_ATOMIC, + /// A RecoveryExpr record. + EXPR_RECOVERY, + // Objective-C /// An ObjCStringLiteral record. diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index 20b2837d9d333..a6ccf9aad321e 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -456,6 +456,15 @@ ExprDependence clang::computeDependence(DeclRefExpr *E, const ASTContext &Ctx) { return Deps; } +ExprDependence clang::computeDependence(RecoveryExpr *E) { + // FIXME: drop type+value+instantiation once Error is sufficient to suppress + // bogus dianostics. + auto D = ExprDependence::TypeValueInstantiation | ExprDependence::Error; + for (auto *S : E->subExpressions()) + D |= S->getDependence(); + return D; +} + ExprDependence clang::computeDependence(PredefinedExpr *E) { return toExprDependence(E->getType()->getDependence()) & ~ExprDependence::UnexpandedPack; diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index a2a024a3c2be1..b964eb604d622 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2375,6 +2375,7 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, // If we don't know precisely what we're looking at, let's not warn. case UnresolvedLookupExprClass: case CXXUnresolvedConstructExprClass: + case RecoveryExprClass: return false; case CXXTemporaryObjectExprClass: @@ -3227,6 +3228,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case SubstNonTypeTemplateParmPackExprClass: case FunctionParmPackExprClass: case TypoExprClass: + case RecoveryExprClass: case CXXFoldExprClass: llvm_unreachable("shouldn't see dependent / unresolved nodes here"); @@ -4467,3 +4469,30 @@ QualType OMPArraySectionExpr::getBaseOriginalType(const Expr *Base) { } return OriginalTy; } + +RecoveryExpr::RecoveryExpr(ASTContext &Ctx, SourceLocation BeginLoc, + SourceLocation EndLoc, ArrayRef SubExprs) + : Expr(RecoveryExprClass, Ctx.DependentTy, VK_LValue, OK_Ordinary), + BeginLoc(BeginLoc), EndLoc(EndLoc), NumExprs(SubExprs.size()) { +#ifndef NDEBUG + for (auto *E : SubExprs) + assert(E != nullptr); +#endif + + llvm::copy(SubExprs, getTrailingObjects()); + setDependence(computeDependence(this)); +} + +RecoveryExpr *RecoveryExpr::Create(ASTContext &Ctx, SourceLocation BeginLoc, + SourceLocation EndLoc, + ArrayRef SubExprs) { + void *Mem = Ctx.Allocate(totalSizeToAlloc(SubExprs.size()), + alignof(RecoveryExpr)); + return new (Mem) RecoveryExpr(Ctx, BeginLoc, EndLoc, SubExprs); +} + +RecoveryExpr *RecoveryExpr::CreateEmpty(ASTContext &Ctx, unsigned NumSubExprs) { + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumSubExprs), + alignof(RecoveryExpr)); + return new (Mem) RecoveryExpr(EmptyShell()); +} diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index d201af31f521e..58e70347292c7 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -129,6 +129,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::UnresolvedLookupExprClass: case Expr::UnresolvedMemberExprClass: case Expr::TypoExprClass: + case Expr::RecoveryExprClass: case Expr::DependentCoawaitExprClass: case Expr::CXXDependentScopeMemberExprClass: case Expr::DependentScopeDeclRefExprClass: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index ef56dbf7d2d00..06f4885e47d6c 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -14189,6 +14189,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::CXXPseudoDestructorExprClass: case Expr::UnresolvedLookupExprClass: case Expr::TypoExprClass: + case Expr::RecoveryExprClass: case Expr::DependentScopeDeclRefExprClass: case Expr::CXXConstructExprClass: case Expr::CXXInheritedCtorInitExprClass: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 12e1fc589fdc7..da723ede9c89d 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -3672,7 +3672,8 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { case Expr::LambdaExprClass: case Expr::MSPropertyRefExprClass: case Expr::MSPropertySubscriptExprClass: - case Expr::TypoExprClass: // This should no longer exist in the AST by now. + case Expr::TypoExprClass: // This should no longer exist in the AST by now. + case Expr::RecoveryExprClass: case Expr::OMPArraySectionExprClass: case Expr::CXXInheritedCtorInitExprClass: llvm_unreachable("unexpected statement kind"); diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 68d0b144ced5e..80fdc09a8a6cf 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2506,6 +2506,17 @@ void StmtPrinter::VisitTypoExpr(TypoExpr *Node) { llvm_unreachable("Cannot print TypoExpr nodes"); } +void StmtPrinter::VisitRecoveryExpr(RecoveryExpr *Node) { + OS << "("; + const char *Sep = ""; + for (Expr *E : Node->subExpressions()) { + OS << Sep; + PrintExpr(E); + Sep = ", "; + } + OS << ')'; +} + void StmtPrinter::VisitAsTypeExpr(AsTypeExpr *Node) { OS << "__builtin_astype("; PrintExpr(Node->getSrcExpr()); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index cd39024cd8380..5e87eb3e237c3 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2025,6 +2025,8 @@ void StmtProfiler::VisitSourceLocExpr(const SourceLocExpr *E) { VisitExpr(E); } +void StmtProfiler::VisitRecoveryExpr(const RecoveryExpr *E) { VisitExpr(E); } + void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) { VisitExpr(S); } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 802df03f2dcff..dc5e932c9460a 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2908,6 +2908,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, !Args.hasArg(OPT_fno_concept_satisfaction_caching); if (Args.hasArg(OPT_fconcepts_ts)) Diags.Report(diag::warn_fe_concepts_ts_flag); + Opts.RecoveryAST = + Args.hasFlag(OPT_frecovery_ast, OPT_fno_recovery_ast, false); Opts.HeinousExtensions = Args.hasArg(OPT_fheinous_gnu_extensions); Opts.AccessControl = !Args.hasArg(OPT_fno_access_control); Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 7bd1230a77500..096e295b80a64 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -625,13 +625,31 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { SourceRange(Actions.getExprRange(LHS.get()).getBegin(), Actions.getExprRange(RHS.get()).getEnd())); - LHS = Actions.ActOnBinOp(getCurScope(), OpToken.getLocation(), - OpToken.getKind(), LHS.get(), RHS.get()); - + ExprResult BinOp = + Actions.ActOnBinOp(getCurScope(), OpToken.getLocation(), + OpToken.getKind(), LHS.get(), RHS.get()); + if (BinOp.isInvalid()) + BinOp = Actions.CreateRecoveryExpr(LHS.get()->getBeginLoc(), + RHS.get()->getEndLoc(), + {LHS.get(), RHS.get()}); + + LHS = BinOp; } else { - LHS = Actions.ActOnConditionalOp(OpToken.getLocation(), ColonLoc, - LHS.get(), TernaryMiddle.get(), - RHS.get()); + ExprResult CondOp = Actions.ActOnConditionalOp( + OpToken.getLocation(), ColonLoc, LHS.get(), TernaryMiddle.get(), + RHS.get()); + if (CondOp.isInvalid()) { + std::vector Args; + // TernaryMiddle can be null for the GNU conditional expr extension. + if (TernaryMiddle.get()) + Args = {LHS.get(), TernaryMiddle.get(), RHS.get()}; + else + Args = {LHS.get(), RHS.get()}; + CondOp = Actions.CreateRecoveryExpr(LHS.get()->getBeginLoc(), + RHS.get()->getEndLoc(), Args); + } + + LHS = CondOp; } // In this case, ActOnBinOp or ActOnConditionalOp performed the // CorrectDelayedTyposInExpr check. @@ -1305,9 +1323,14 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, UnconsumeToken(SavedTok); return ExprError(); } - if (!Res.isInvalid()) + if (!Res.isInvalid()) { + Expr *Arg = Res.get(); Res = Actions.ActOnUnaryOp(getCurScope(), SavedTok.getLocation(), - SavedKind, Res.get()); + SavedKind, Arg); + if (Res.isInvalid()) + Res = Actions.CreateRecoveryExpr(SavedTok.getLocation(), + Arg->getEndLoc(), Arg); + } return Res; } case tok::amp: { // unary-expression: '&' cast-expression @@ -1317,8 +1340,13 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, SourceLocation SavedLoc = ConsumeToken(); PreferredType.enterUnary(Actions, Tok.getLocation(), tok::amp, SavedLoc); Res = ParseCastExpression(AnyCastExpr, true); - if (!Res.isInvalid()) - Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get()); + if (!Res.isInvalid()) { + Expr *Arg = Res.get(); + Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Arg); + if (Res.isInvalid()) + Res = Actions.CreateRecoveryExpr(Tok.getLocation(), Arg->getEndLoc(), + Arg); + } return Res; } @@ -1334,8 +1362,12 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, SourceLocation SavedLoc = ConsumeToken(); PreferredType.enterUnary(Actions, Tok.getLocation(), SavedKind, SavedLoc); Res = ParseCastExpression(AnyCastExpr); - if (!Res.isInvalid()) - Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get()); + if (!Res.isInvalid()) { + Expr *Arg = Res.get(); + Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Arg); + if (Res.isInvalid()) + Res = Actions.CreateRecoveryExpr(SavedLoc, Arg->getEndLoc(), Arg); + } return Res; } @@ -1941,12 +1973,18 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { PT.consumeClose(); LHS = ExprError(); } else { - assert((ArgExprs.size() == 0 || - ArgExprs.size()-1 == CommaLocs.size())&& - "Unexpected number of commas!"); - LHS = Actions.ActOnCallExpr(getCurScope(), LHS.get(), Loc, - ArgExprs, Tok.getLocation(), + assert( + (ArgExprs.size() == 0 || ArgExprs.size() - 1 == CommaLocs.size()) && + "Unexpected number of commas!"); + Expr *Fn = LHS.get(); + SourceLocation RParLoc = Tok.getLocation(); + LHS = Actions.ActOnCallExpr(getCurScope(), Fn, Loc, ArgExprs, RParLoc, ExecConfig); + if (LHS.isInvalid()) { + ArgExprs.insert(ArgExprs.begin(), Fn); + LHS = + Actions.CreateRecoveryExpr(Fn->getBeginLoc(), RParLoc, ArgExprs); + } PT.consumeClose(); } @@ -2069,8 +2107,12 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { case tok::plusplus: // postfix-expression: postfix-expression '++' case tok::minusminus: // postfix-expression: postfix-expression '--' if (!LHS.isInvalid()) { + Expr *Arg = LHS.get(); LHS = Actions.ActOnPostfixUnaryOp(getCurScope(), Tok.getLocation(), - Tok.getKind(), LHS.get()); + Tok.getKind(), Arg); + if (LHS.isInvalid()) + LHS = Actions.CreateRecoveryExpr(Arg->getBeginLoc(), + Tok.getLocation(), Arg); } ConsumeToken(); break; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 225f720f94c5e..13fae1e9fed50 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -21,6 +21,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/EvaluatedExprVisitor.h" +#include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/NonTrivialTypeVisitor.h" #include "clang/AST/StmtCXX.h" @@ -11500,6 +11501,7 @@ QualType Sema::deduceVarTypeFromInitializer(VarDecl *VDecl, bool Sema::DeduceVariableDeclarationType(VarDecl *VDecl, bool DirectInit, Expr *Init) { + assert(!Init || !Init->containsErrors()); QualType DeducedType = deduceVarTypeFromInitializer( VDecl, VDecl->getDeclName(), VDecl->getType(), VDecl->getTypeSourceInfo(), VDecl->getSourceRange(), DirectInit, Init); @@ -11837,7 +11839,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { // be deduced based on the chosen correction if the original init contains a // TypoExpr. ExprResult Res = CorrectDelayedTyposInExpr(Init, VDecl); - if (!Res.isUsable()) { + if (!Res.isUsable() || Res.get()->containsErrors()) { RealDecl->setInvalidDecl(); return; } diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 6eb5b71248abe..53c62a1a40177 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1340,6 +1340,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::CXXUnresolvedConstructExprClass: case Expr::DependentScopeDeclRefExprClass: case Expr::CXXFoldExprClass: + case Expr::RecoveryExprClass: return CT_Dependent; case Expr::AsTypeExprClass: diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index afffd968bb489..335ccabfd964a 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -18414,3 +18414,17 @@ bool Sema::IsDependentFunctionNameExpr(Expr *E) { assert(E->isTypeDependent()); return isa(E); } + +ExprResult Sema::CreateRecoveryExpr(SourceLocation Begin, SourceLocation End, + ArrayRef SubExprs) { + // FIXME: enable it for C++, RecoveryExpr is type-dependent to suppress + // bogus diagnostics and this trick does not work in C. + // FIXME: use containsErrors() to suppress unwanted diags in C. + if (!Context.getLangOpts().RecoveryAST) + return ExprError(); + + if (isSFINAEContext()) + return ExprError(); + + return RecoveryExpr::Create(Context, Begin, End, SubExprs); +} diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 65c924a8cb730..867ca5ffa644e 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3504,6 +3504,11 @@ class TreeTransform { Sema::AtomicArgumentOrder::AST); } + ExprResult RebuildRecoveryExpr(SourceLocation BeginLoc, SourceLocation EndLoc, + ArrayRef SubExprs) { + return getSema().CreateRecoveryExpr(BeginLoc, EndLoc, SubExprs); + } + private: TypeLoc TransformTypeInObjectScope(TypeLoc TL, QualType ObjectType, @@ -9867,6 +9872,24 @@ TreeTransform::TransformTypoExpr(TypoExpr *E) { return E; } +template +ExprResult TreeTransform::TransformRecoveryExpr(RecoveryExpr *E) { + llvm::SmallVector Children; + bool Changed = false; + for (Expr *C : E->subExpressions()) { + ExprResult NewC = getDerived().TransformExpr(C); + if (NewC.isInvalid()) + return ExprError(); + Children.push_back(NewC.get()); + + Changed |= NewC.get() != C; + } + if (!getDerived().AlwaysRebuild() && !Changed) + return E; + return getDerived().RebuildRecoveryExpr(E->getBeginLoc(), E->getEndLoc(), + Children); +} + template ExprResult TreeTransform::TransformPseudoObjectExpr(PseudoObjectExpr *E) { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index c0ed123826ebe..5d9033e379777 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2080,6 +2080,19 @@ void ASTStmtReader::VisitTypoExpr(TypoExpr *E) { llvm_unreachable("Cannot read TypoExpr nodes"); } +void ASTStmtReader::VisitRecoveryExpr(RecoveryExpr *E) { + VisitExpr(E); + unsigned NumArgs = Record.readInt(); + E->BeginLoc = readSourceLocation(); + E->EndLoc = readSourceLocation(); + assert( + (NumArgs == std::distance(E->children().begin(), E->children().end())) && + "Wrong NumArgs!"); + (void)NumArgs; + for (Stmt *&Child : E->children()) + Child = Record.readSubStmt(); +} + //===----------------------------------------------------------------------===// // Microsoft Expressions and Statements //===----------------------------------------------------------------------===// @@ -2857,6 +2870,11 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { Context, /*NumArgs=*/Record[ASTStmtReader::NumExprFields], Empty); break; + case EXPR_RECOVERY: + S = RecoveryExpr::CreateEmpty( + Context, /*NumArgs=*/Record[ASTStmtReader::NumExprFields]); + break; + case EXPR_MEMBER: S = MemberExpr::CreateEmpty(Context, Record[ASTStmtReader::NumExprFields], Record[ASTStmtReader::NumExprFields + 1], diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 0fc90f5d835b6..ddda3931d3199 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -2280,7 +2280,7 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //ValueDependent Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //InstantiationDependent Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //UnexpandedParamPack - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // ContainsErrors + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //ContainsErrors Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetValueKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetObjectKind //DeclRefExpr @@ -2304,7 +2304,7 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //ValueDependent Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //InstantiationDependent Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //UnexpandedParamPack - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // ContainsErrors + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //ContainsErrors Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetValueKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetObjectKind //Integer Literal @@ -2323,7 +2323,7 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //ValueDependent Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //InstantiationDependent Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //UnexpandedParamPack - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // ContainsErrors + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //ContainsErrors Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetValueKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetObjectKind //Character Literal @@ -2342,7 +2342,7 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //ValueDependent Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //InstantiationDependent Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //UnexpandedParamPack - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // ContainsErrors + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //ContainsErrors Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetValueKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetObjectKind // CastExpr diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 41f8bb8c4d36d..d64e2330850a1 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -786,6 +786,16 @@ void ASTStmtWriter::VisitCallExpr(CallExpr *E) { Code = serialization::EXPR_CALL; } +void ASTStmtWriter::VisitRecoveryExpr(RecoveryExpr *E) { + VisitExpr(E); + Record.push_back(std::distance(E->children().begin(), E->children().end())); + Record.AddSourceLocation(E->getBeginLoc()); + Record.AddSourceLocation(E->getEndLoc()); + for (Stmt *Child : E->children()) + Record.AddStmt(Child); + Code = serialization::EXPR_RECOVERY; +} + void ASTStmtWriter::VisitMemberExpr(MemberExpr *E) { VisitExpr(E); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1bdf7f170f8bc..b9adee87436a9 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1225,6 +1225,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::UnresolvedLookupExprClass: case Stmt::UnresolvedMemberExprClass: case Stmt::TypoExprClass: + case Stmt::RecoveryExprClass: case Stmt::CXXNoexceptExprClass: case Stmt::PackExpansionExprClass: case Stmt::SubstNonTypeTemplateParmPackExprClass: diff --git a/clang/test/AST/ast-dump-expr-errors.cpp b/clang/test/AST/ast-dump-expr-errors.cpp new file mode 100644 index 0000000000000..e623fad04f4c8 --- /dev/null +++ b/clang/test/AST/ast-dump-expr-errors.cpp @@ -0,0 +1,46 @@ +// RUN: not %clang_cc1 -triple x86_64-unknown-unknown -Wno-unused-value -fcxx-exceptions -std=gnu++17 -ast-dump -frecovery-ast %s | FileCheck -strict-whitespace %s + +// Check errors flag is set for RecoveryExpr. +// +// CHECK: VarDecl {{.*}} a +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: `-UnresolvedLookupExpr {{.*}} 'bar' +int a = bar(); + +// The flag propagates through more complicated calls. +// +// CHECK: VarDecl {{.*}} b +// CHECK-NEXT:`-CallExpr {{.*}} contains-errors +// CHECK-NEXT: |-UnresolvedLookupExpr {{.*}} 'bar' +// CHECK-NEXT: |-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: | `-UnresolvedLookupExpr {{.*}} 'baz' +// CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: `-UnresolvedLookupExpr {{.*}} 'qux' +int b = bar(baz(), qux()); + +// Also propagates through more complicated expressions. +// +// CHECK: |-VarDecl {{.*}} c +// CHECK-NEXT:| `-BinaryOperator {{.*}} '' contains-errors '*' +// CHECK-NEXT:| |-UnaryOperator {{.*}} '' contains-errors prefix '&' +// CHECK-NEXT:| | `-ParenExpr {{.*}} '' contains-errors +// CHECK-NEXT:| | `-BinaryOperator {{.*}} '' contains-errors '+' +// CHECK-NEXT:| | |-RecoveryExpr {{.*}} '' contains-errors +// CHECK-NEXT:| | | `-UnresolvedLookupExpr {{.*}} 'bar' +// CHECK-NEXT:| | `-RecoveryExpr {{.*}} '' contains-errors +// CHECK-NEXT:| | `-UnresolvedLookupExpr {{.*}} 'baz' +int c = &(bar() + baz()) * 10; + +// Errors flag propagates even when type is not dependent anymore. +// CHECK: |-VarDecl {{.*}} d +// CHECK-NEXT:| `-CXXStaticCastExpr {{.*}} 'int' contains-errors +// CHECK-NEXT:| `-BinaryOperator {{.*}} '' contains-errors '+' +// CHECK-NEXT:| |-RecoveryExpr {{.*}} '' contains-errors +// CHECK-NEXT:| | `-UnresolvedLookupExpr {{.*}} 'bar' +// CHECK-NEXT:| `-IntegerLiteral {{.*}} 1 +int d = static_cast(bar() + 1); + +// FIXME: store initializer even when 'auto' could not be deduced. +// Expressions with errors currently do not keep initializers around. +// CHECK: `-VarDecl {{.*}} invalid e 'auto' +auto e = bar(); diff --git a/clang/test/AST/ast-dump-recovery.cpp b/clang/test/AST/ast-dump-recovery.cpp new file mode 100644 index 0000000000000..beb409edc4a6a --- /dev/null +++ b/clang/test/AST/ast-dump-recovery.cpp @@ -0,0 +1,85 @@ +// RUN: not %clang_cc1 -triple x86_64-unknown-unknown -Wno-unused-value -fcxx-exceptions -std=gnu++17 -frecovery-ast -ast-dump %s | FileCheck -strict-whitespace %s +// RUN: not %clang_cc1 -triple x86_64-unknown-unknown -Wno-unused-value -fcxx-exceptions -std=gnu++17 -fno-recovery-ast -ast-dump %s | FileCheck --check-prefix=DISABLED -strict-whitespace %s + +int some_func(int *); + +// CHECK: VarDecl {{.*}} invalid_call +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: |-UnresolvedLookupExpr {{.*}} 'some_func' +// CHECK-NEXT: `-IntegerLiteral {{.*}} 123 +// DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors +int invalid_call = some_func(123); + +int ambig_func(double); +int ambig_func(float); + +// CHECK: VarDecl {{.*}} ambig_call +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: |-UnresolvedLookupExpr {{.*}} 'ambig_func' +// CHECK-NEXT: `-IntegerLiteral {{.*}} 123 +// DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors +int ambig_call = ambig_func(123); + +// CHECK: VarDecl {{.*}} unresolved_call1 +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: `-UnresolvedLookupExpr {{.*}} 'bar' +// DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors +int unresolved_call1 = bar(); + +// CHECK: VarDecl {{.*}} unresolved_call2 +// CHECK-NEXT:`-CallExpr {{.*}} contains-errors +// CHECK-NEXT: |-UnresolvedLookupExpr {{.*}} 'bar' +// CHECK-NEXT: |-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: | `-UnresolvedLookupExpr {{.*}} 'baz' +// CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: `-UnresolvedLookupExpr {{.*}} 'qux' +// DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors +int unresolved_call2 = bar(baz(), qux()); + +constexpr int a = 10; + +// CHECK: VarDecl {{.*}} postfix_inc +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'a' +// DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors +int postfix_inc = a++; + +// CHECK: VarDecl {{.*}} prefix_inc +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'a' +// DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors +int prefix_inc = ++a; + +// CHECK: VarDecl {{.*}} unary_address +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: `-ParenExpr {{.*}} +// CHECK-NEXT: `-BinaryOperator {{.*}} '+' +// CHECK-NEXT: |-ImplicitCastExpr +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'a' +// DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors +int unary_address = &(a + 1); + +// CHECK: VarDecl {{.*}} unary_bitinverse +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: `-ParenExpr {{.*}} +// CHECK-NEXT: `-BinaryOperator {{.*}} '+' +// CHECK-NEXT: |-ImplicitCastExpr +// CHECK-NEXT: | `-ImplicitCastExpr +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'a' +// DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors +int unary_bitinverse = ~(a + 0.0); + +// CHECK: VarDecl {{.*}} binary +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: |-DeclRefExpr {{.*}} 'a' +// CHECK-NEXT: `-CXXNullPtrLiteralExpr +// DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors +int binary = a + nullptr; + +// CHECK: VarDecl {{.*}} ternary +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: |-DeclRefExpr {{.*}} 'a' +// CHECK-NEXT: |-CXXNullPtrLiteralExpr +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'a' +// DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors +int ternary = a ? nullptr : a; diff --git a/clang/test/Index/getcursor-recovery.cpp b/clang/test/Index/getcursor-recovery.cpp new file mode 100644 index 0000000000000..29966f26c8240 --- /dev/null +++ b/clang/test/Index/getcursor-recovery.cpp @@ -0,0 +1,16 @@ +int foo(int, int); +int foo(int, double); +int x; + +void testTypedRecoveryExpr() { + // Inner foo() is a RecoveryExpr, outer foo() is an overloaded call. + foo(x, foo(x)); +} +// RUN: c-index-test -cursor-at=%s:7:3 %s -Xclang -frecovery-ast | FileCheck -check-prefix=OUTER-FOO %s +// OUTER-FOO: OverloadedDeclRef=foo[2:5, 1:5] +// RUN: c-index-test -cursor-at=%s:7:7 %s -Xclang -frecovery-ast | FileCheck -check-prefix=OUTER-X %s +// OUTER-X: DeclRefExpr=x:3:5 +// RUN: c-index-test -cursor-at=%s:7:10 %s -Xclang -frecovery-ast | FileCheck -check-prefix=INNER-FOO %s +// INNER-FOO: OverloadedDeclRef=foo[2:5, 1:5] +// RUN: c-index-test -cursor-at=%s:7:14 %s -Xclang -frecovery-ast | FileCheck -check-prefix=INNER-X %s +// INNER-X: DeclRefExpr=x:3:5 diff --git a/clang/test/SemaTemplate/recovery-tree-transform.cpp b/clang/test/SemaTemplate/recovery-tree-transform.cpp new file mode 100644 index 0000000000000..bf882db3ec2cc --- /dev/null +++ b/clang/test/SemaTemplate/recovery-tree-transform.cpp @@ -0,0 +1,4 @@ +// RUN: %clang_cc1 -verify -frecovery-ast %s + +template int *p = &void(T::error); // expected-error{{cannot take the address of an rvalue}} expected-error{{type 'int' cannot be used prior to '::'}} +int *q = p; // expected-note{{in instantiation of variable template specialization 'p' requested here}} diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index 5e857def4c917..147e3eaf4762c 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -292,6 +292,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::ObjCDictionaryLiteralClass: case Stmt::ObjCBoxedExprClass: case Stmt::ObjCSubscriptRefExprClass: + case Stmt::RecoveryExprClass: K = CXCursor_UnexposedExpr; break;