class BlockExpr;
class AsTypeExpr;
class DeclRefExpr;
+class RecoveryExpr;
class CXXRewrittenBinaryOperator;
class CXXStdInitializerListExpr;
class CXXTypeidExpr;
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);
}
};
+
+/// 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(<broken-expr>)` 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<RecoveryExpr, Expr *> {
+public:
+ static RecoveryExpr *Create(ASTContext &Ctx, SourceLocation BeginLoc,
+ SourceLocation EndLoc, ArrayRef<Expr *> SubExprs);
+ static RecoveryExpr *CreateEmpty(ASTContext &Ctx, unsigned NumSubExprs);
+
+ ArrayRef<Expr *> subExpressions() {
+ auto *B = getTrailingObjects<Expr *>();
+ return llvm::makeArrayRef(B, B + NumExprs);
+ }
+
+ ArrayRef<const Expr *> subExpressions() const {
+ return const_cast<RecoveryExpr *>(this)->subExpressions();
+ }
+
+ child_range children() {
+ Stmt **B = reinterpret_cast<Stmt **>(getTrailingObjects<Expr *>());
+ 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<Expr *> SubExprs);
+ RecoveryExpr(EmptyShell Empty) : Expr(RecoveryExprClass, Empty) {}
+
+ size_t numTrailingObjects(OverloadToken<Stmt *>) 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
})
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
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")
def BlockExpr : StmtNode<Expr>;
def OpaqueValueExpr : StmtNode<Expr>;
def TypoExpr : StmtNode<Expr>;
+def RecoveryExpr : StmtNode<Expr>;
def BuiltinBitCastExpr : StmtNode<ExplicitCastExpr>;
// Microsoft Extensions.
"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">,
void DiagnoseAmbiguousLookup(LookupResult &Result);
//@}
+ /// Attempts to produce a RecoveryExpr after some AST node cannot be created.
+ ExprResult CreateRecoveryExpr(SourceLocation Begin, SourceLocation End,
+ ArrayRef<Expr *> SubExprs);
+
ObjCInterfaceDecl *getObjCInterfaceDecl(IdentifierInfo *&Id,
SourceLocation IdLoc,
bool TypoCorrection = false);
/// An AtomicExpr record.
EXPR_ATOMIC,
+ /// A RecoveryExpr record.
+ EXPR_RECOVERY,
+
// Objective-C
/// An ObjCStringLiteral record.
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;
// 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:
case SubstNonTypeTemplateParmPackExprClass:
case FunctionParmPackExprClass:
case TypoExprClass:
+ case RecoveryExprClass:
case CXXFoldExprClass:
llvm_unreachable("shouldn't see dependent / unresolved nodes here");
}
return OriginalTy;
}
+
+RecoveryExpr::RecoveryExpr(ASTContext &Ctx, SourceLocation BeginLoc,
+ SourceLocation EndLoc, ArrayRef<Expr *> 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<Expr *>());
+ setDependence(computeDependence(this));
+}
+
+RecoveryExpr *RecoveryExpr::Create(ASTContext &Ctx, SourceLocation BeginLoc,
+ SourceLocation EndLoc,
+ ArrayRef<Expr *> SubExprs) {
+ void *Mem = Ctx.Allocate(totalSizeToAlloc<Expr *>(SubExprs.size()),
+ alignof(RecoveryExpr));
+ return new (Mem) RecoveryExpr(Ctx, BeginLoc, EndLoc, SubExprs);
+}
+
+RecoveryExpr *RecoveryExpr::CreateEmpty(ASTContext &Ctx, unsigned NumSubExprs) {
+ void *Mem = Ctx.Allocate(totalSizeToAlloc<Expr *>(NumSubExprs),
+ alignof(RecoveryExpr));
+ return new (Mem) RecoveryExpr(EmptyShell());
+}
case Expr::UnresolvedLookupExprClass:
case Expr::UnresolvedMemberExprClass:
case Expr::TypoExprClass:
+ case Expr::RecoveryExprClass:
case Expr::DependentCoawaitExprClass:
case Expr::CXXDependentScopeMemberExprClass:
case Expr::DependentScopeDeclRefExprClass:
case Expr::CXXPseudoDestructorExprClass:
case Expr::UnresolvedLookupExprClass:
case Expr::TypoExprClass:
+ case Expr::RecoveryExprClass:
case Expr::DependentScopeDeclRefExprClass:
case Expr::CXXConstructExprClass:
case Expr::CXXInheritedCtorInitExprClass:
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");
llvm_unreachable("Cannot print TypoExpr nodes");
}
+void StmtPrinter::VisitRecoveryExpr(RecoveryExpr *Node) {
+ OS << "<recovery-expr>(";
+ 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());
VisitExpr(E);
}
+void StmtProfiler::VisitRecoveryExpr(const RecoveryExpr *E) { VisitExpr(E); }
+
void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) {
VisitExpr(S);
}
!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);
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<clang::Expr *> 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.
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
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;
}
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;
}
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();
}
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;
#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"
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);
// 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;
}
case Expr::CXXUnresolvedConstructExprClass:
case Expr::DependentScopeDeclRefExprClass:
case Expr::CXXFoldExprClass:
+ case Expr::RecoveryExprClass:
return CT_Dependent;
case Expr::AsTypeExprClass:
assert(E->isTypeDependent());
return isa<UnresolvedLookupExpr>(E);
}
+
+ExprResult Sema::CreateRecoveryExpr(SourceLocation Begin, SourceLocation End,
+ ArrayRef<Expr *> 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);
+}
Sema::AtomicArgumentOrder::AST);
}
+ ExprResult RebuildRecoveryExpr(SourceLocation BeginLoc, SourceLocation EndLoc,
+ ArrayRef<Expr *> SubExprs) {
+ return getSema().CreateRecoveryExpr(BeginLoc, EndLoc, SubExprs);
+ }
+
private:
TypeLoc TransformTypeInObjectScope(TypeLoc TL,
QualType ObjectType,
return E;
}
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformRecoveryExpr(RecoveryExpr *E) {
+ llvm::SmallVector<Expr *, 8> 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<typename Derived>
ExprResult
TreeTransform<Derived>::TransformPseudoObjectExpr(PseudoObjectExpr *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
//===----------------------------------------------------------------------===//
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],
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
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
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
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
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);
case Stmt::UnresolvedLookupExprClass:
case Stmt::UnresolvedMemberExprClass:
case Stmt::TypoExprClass:
+ case Stmt::RecoveryExprClass:
case Stmt::CXXNoexceptExprClass:
case Stmt::PackExpansionExprClass:
case Stmt::SubstNonTypeTemplateParmPackExprClass:
--- /dev/null
+// 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 {{.*}} '<dependent type>' contains-errors '*'
+// CHECK-NEXT:| |-UnaryOperator {{.*}} '<dependent type>' contains-errors prefix '&'
+// CHECK-NEXT:| | `-ParenExpr {{.*}} '<dependent type>' contains-errors
+// CHECK-NEXT:| | `-BinaryOperator {{.*}} '<dependent type>' contains-errors '+'
+// CHECK-NEXT:| | |-RecoveryExpr {{.*}} '<dependent type>' contains-errors
+// CHECK-NEXT:| | | `-UnresolvedLookupExpr {{.*}} 'bar'
+// CHECK-NEXT:| | `-RecoveryExpr {{.*}} '<dependent type>' 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 {{.*}} '<dependent type>' contains-errors '+'
+// CHECK-NEXT:| |-RecoveryExpr {{.*}} '<dependent type>' contains-errors
+// CHECK-NEXT:| | `-UnresolvedLookupExpr {{.*}} 'bar'
+// CHECK-NEXT:| `-IntegerLiteral {{.*}} 1
+int d = static_cast<int>(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();
--- /dev/null
+// 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;
--- /dev/null
+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
--- /dev/null
+// RUN: %clang_cc1 -verify -frecovery-ast %s
+
+template<typename T> 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<int>; // expected-note{{in instantiation of variable template specialization 'p<int>' requested here}}
case Stmt::ObjCDictionaryLiteralClass:
case Stmt::ObjCBoxedExprClass:
case Stmt::ObjCSubscriptRefExprClass:
+ case Stmt::RecoveryExprClass:
K = CXCursor_UnexposedExpr;
break;