diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -45,6 +45,7 @@ class BlockExpr; class AsTypeExpr; class DeclRefExpr; +class RecoveryExpr; class CXXRewrittenBinaryOperator; class CXXStdInitializerListExpr; class CXXTypeidExpr; @@ -122,6 +123,7 @@ 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 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -5911,6 +5911,69 @@ } }; + +/// 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 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2668,6 +2668,7 @@ }) 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 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -148,6 +148,8 @@ 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 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -195,6 +195,7 @@ 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 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -566,6 +566,11 @@ "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 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3885,6 +3885,10 @@ 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 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1634,6 +1634,9 @@ /// 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 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -456,6 +456,15 @@ 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 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2375,6 +2375,7 @@ // 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 @@ case SubstNonTypeTemplateParmPackExprClass: case FunctionParmPackExprClass: case TypoExprClass: + case RecoveryExprClass: case CXXFoldExprClass: llvm_unreachable("shouldn't see dependent / unresolved nodes here"); @@ -4467,3 +4469,30 @@ } 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 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -129,6 +129,7 @@ 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 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -14189,6 +14189,7 @@ 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 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -3672,7 +3672,8 @@ 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 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2506,6 +2506,17 @@ 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 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2025,6 +2025,8 @@ 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 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2908,6 +2908,8 @@ !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 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -625,13 +625,31 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 --- 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 @@ 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 @@ // 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 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1340,6 +1340,7 @@ 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 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -18414,3 +18414,17 @@ 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 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3504,6 +3504,11 @@ 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 @@ 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 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2080,6 +2080,19 @@ 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 @@ 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 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -2280,7 +2280,7 @@ 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 @@ 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 @@ 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 @@ 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 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -786,6 +786,16 @@ 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 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1225,6 +1225,7 @@ 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 --- /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 --- /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 --- /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 --- /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 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -292,6 +292,7 @@ case Stmt::ObjCDictionaryLiteralClass: case Stmt::ObjCBoxedExprClass: case Stmt::ObjCSubscriptRefExprClass: + case Stmt::RecoveryExprClass: K = CXCursor_UnexposedExpr; break;