[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
This commit is contained in:
Haojian Wu 2020-03-19 16:30:40 +01:00
parent e0279d720a
commit 733edf9750
30 changed files with 423 additions and 23 deletions

View File

@ -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);

View File

@ -5911,6 +5911,69 @@ public:
}
};
/// 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

View File

@ -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

View File

@ -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")

View File

@ -195,6 +195,7 @@ def ConvertVectorExpr : StmtNode<Expr>;
def BlockExpr : StmtNode<Expr>;
def OpaqueValueExpr : StmtNode<Expr>;
def TypoExpr : StmtNode<Expr>;
def RecoveryExpr : StmtNode<Expr>;
def BuiltinBitCastExpr : StmtNode<ExplicitCastExpr>;
// Microsoft Extensions.

View File

@ -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">,

View File

@ -3885,6 +3885,10 @@ public:
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);

View File

@ -1634,6 +1634,9 @@ namespace serialization {
/// An AtomicExpr record.
EXPR_ATOMIC,
/// A RecoveryExpr record.
EXPR_RECOVERY,
// Objective-C
/// An ObjCStringLiteral record.

View File

@ -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;

View File

@ -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<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());
}

View File

@ -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:

View File

@ -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:

View File

@ -3672,7 +3672,8 @@ recurse:
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");

View File

@ -2506,6 +2506,17 @@ void StmtPrinter::VisitTypoExpr(TypoExpr *Node) {
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());

View File

@ -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);
}

View File

@ -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);

View File

@ -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<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.
@ -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;

View File

@ -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;
}

View File

@ -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:

View File

@ -18414,3 +18414,17 @@ bool Sema::IsDependentFunctionNameExpr(Expr *E) {
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);
}

View File

@ -3504,6 +3504,11 @@ public:
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,
@ -9867,6 +9872,24 @@ TreeTransform<Derived>::TransformTypoExpr(TypoExpr *E) {
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) {

View File

@ -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],

View File

@ -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

View File

@ -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);

View File

@ -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:

View File

@ -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 {{.*}} '<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();

View File

@ -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;

View File

@ -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

View File

@ -0,0 +1,4 @@
// 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}}

View File

@ -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;