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; @@ -120,6 +121,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 @@ -5896,6 +5896,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. +/// +/// 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 NumStmts); + + 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 @@ -2628,6 +2628,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/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -194,6 +194,7 @@ def BlockExpr : StmtNode; def OpaqueValueExpr : StmtNode; def TypoExpr : StmtNode; +def RecoveryExpr : StmtNode; def BuiltinBitCastExpr : StmtNode; // Microsoft Extensions. 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 @@ -36,6 +36,7 @@ #include "clang/Basic/Module.h" #include "clang/Basic/OpenMPKinds.h" #include "clang/Basic/PragmaKinds.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TemplateKinds.h" #include "clang/Basic/TypeTraits.h" @@ -3718,6 +3719,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 @@ -1628,6 +1628,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 @@ -346,6 +346,13 @@ return Deps; } +ExprDependence clang::computeDependence(RecoveryExpr *E) { + auto Deps = ExprDependence::TypeValueInstantiation | ExprDependence::Error; + for (auto *S : E->subExpressions()) + Deps |= S->getDependence(); + return Deps; +} + ExprDependence clang::computeDependence(CXXRewrittenBinaryOperator *E) { return E->getSemanticForm()->getDependence(); } 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 @@ -2382,6 +2382,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"); @@ -4466,3 +4468,30 @@ } return OriginalTy; } + +RecoveryExpr::RecoveryExpr(ASTContext &Ctx, SourceLocation BeginLoc, + SourceLocation EndLoc, ArrayRef SubExprs) + : Expr(RecoveryExprClass, Ctx.DependentTy, VK_RValue, OK_Ordinary), + BeginLoc(BeginLoc), EndLoc(EndLoc), NumExprs(SubExprs.size()) { +#ifndef NDEBUG + for (auto *E : SubExprs) + assert(E != nullptr); +#endif + + llvm::copy(SubExprs, getTrailingObjects()); + setDependencies(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 NumStmts) { + void *Mem = + Ctx.Allocate(totalSizeToAlloc(NumStmts), 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 @@ -14104,6 +14104,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 @@ -3633,7 +3633,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 @@ -2445,6 +2445,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 @@ -1947,6 +1947,8 @@ VisitExpr(E); } +void StmtProfiler::VisitRecoveryExpr(const RecoveryExpr *E) { VisitExpr(E); } + void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) { VisitExpr(S); } 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,25 @@ 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()) + CondOp = Actions.CreateRecoveryExpr( + LHS.get()->getBeginLoc(), RHS.get()->getEndLoc(), + {LHS.get(), TernaryMiddle.get(), RHS.get()}); + + LHS = CondOp; } // In this case, ActOnBinOp or ActOnConditionalOp performed the // CorrectDelayedTyposInExpr check. @@ -1304,9 +1316,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 @@ -1316,8 +1333,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; } @@ -1333,8 +1355,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; } @@ -1936,11 +1962,17 @@ 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(), + 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(); } @@ -2064,8 +2096,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" @@ -11363,6 +11364,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); @@ -11697,7 +11699,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 @@ -1332,6 +1332,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 @@ -18141,3 +18141,16 @@ assert(E->isTypeDependent()); return isa(E); } + +ExprResult Sema::CreateRecoveryExpr(SourceLocation Begin, SourceLocation End, + ArrayRef SubExprs) { + // Only enable RecoveryExpr in 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().CPlusPlus) + 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 @@ -3382,6 +3382,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, @@ -9581,6 +9586,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 @@ -1955,6 +1955,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 //===----------------------------------------------------------------------===// @@ -2715,6 +2728,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/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -708,6 +708,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 @@ -1226,6 +1226,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 %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,74 @@ +// RUN: not %clang_cc1 -triple x86_64-unknown-unknown -Wno-unused-value -fcxx-exceptions -std=gnu++17 -ast-dump %s | FileCheck -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 +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 +int ambig_call = ambig_func(123); + +// CHECK: VarDecl {{.*}} unresolved_call1 +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: `-UnresolvedLookupExpr {{.*}} 'bar' +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' +int unresolved_call2 = bar(baz(), qux()); + +constexpr int a = 10; + +// CHECK: VarDecl {{.*}} postfix_inc +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'a' +int postfix_inc = a++; + +// CHECK: VarDecl {{.*}} prefix_inc +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'a' +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' +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' +int unary_bitinverse = ~(a + 0.0); + +// CHECK: VarDecl {{.*}} binary +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: |-DeclRefExpr {{.*}} 'a' +// CHECK-NEXT: `-CXXNullPtrLiteralExpr +int binary = a + nullptr; + +// CHECK: VarDecl {{.*}} ternary +// CHECK-NEXT:`-RecoveryExpr {{.*}} contains-errors +// CHECK-NEXT: |-DeclRefExpr {{.*}} 'a' +// CHECK-NEXT: |-CXXNullPtrLiteralExpr +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'a' +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 | FileCheck -check-prefix=OUTER-FOO %s +// OUTER-FOO: OverloadedDeclRef=foo[2:5, 1:5] +// RUN: c-index-test -cursor-at=%s:7:7 %s | FileCheck -check-prefix=OUTER-X %s +// OUTER-X: DeclRefExpr=x:3:5 +// RUN: c-index-test -cursor-at=%s:7:10 %s | FileCheck -check-prefix=INNER-FOO %s +// INNER-FOO: OverloadedDeclRef=foo[2:5, 1:5] +// RUN: c-index-test -cursor-at=%s:7:14 %s | FileCheck -check-prefix=INNER-X %s +// INNER-X: DeclRefExpr=x:3:5 diff --git a/clang/test/Parser/cxx-concepts-ambig-constraint-expr.cpp b/clang/test/Parser/cxx-concepts-ambig-constraint-expr.cpp --- a/clang/test/Parser/cxx-concepts-ambig-constraint-expr.cpp +++ b/clang/test/Parser/cxx-concepts-ambig-constraint-expr.cpp @@ -5,5 +5,26 @@ // the syntax is consumed without backtracking. // type-specifier-seq in conversion-type-id -template requires T::operator short -unsigned int foo(); // expected-error {{C++ requires a type specifier for all declarations}} \ No newline at end of file +template requires (bool)&T::operator short +unsigned int foo(); // expected-error {{C++ requires a type specifier for all declarations}} + +// type-specifier-seq in new-type-id +template requires (bool)sizeof new (T::f()) short +unsigned int bar(); // expected-error {{C++ requires a type specifier for all declarations}} + +template requires (bool)sizeof new (T::f()) unsigned // expected-error {{'struct' cannot be signed or unsigned}} +struct X { }; // expected-error {{'X' cannot be defined in a type specifier}} + +// C-style cast +// of function call on function-style cast +template requires (bool(T())) +T (*fp)(); // expected-error {{use of undeclared identifier 'fp'}} + +// function-style cast +// as the callee in a function call +struct A { + static int t; + template requires bool(T()) + (A(T (&t))) { } // expected-error {{called object type 'bool' is not a function or function pointer}} \ + // expected-error {{expected member name or ';' after declaration specifiers}} +}; diff --git a/clang/test/Parser/objcxx0x-lambda-expressions.mm b/clang/test/Parser/objcxx0x-lambda-expressions.mm --- a/clang/test/Parser/objcxx0x-lambda-expressions.mm +++ b/clang/test/Parser/objcxx0x-lambda-expressions.mm @@ -11,7 +11,8 @@ []; // expected-error {{expected body of lambda expression}} [=,foo+] {}; // expected-error {{expected ',' or ']' in lambda capture list}} - [&this] {}; // expected-error {{cannot take the address of an rvalue of type 'C *'}} + [&this] {}; // expected-error {{cannot take the address of an rvalue of type 'C *'}} \ + // expected-error {{expected identifier}} [] {}; [=] (int i) {}; [&] (int) mutable -> void {}; @@ -24,7 +25,8 @@ [foo{bar}] () {}; [foo = {bar}] () {}; // expected-error {{}} - [foo(bar) baz] () {}; // expected-error {{called object type 'int' is not a function}} + [foo(bar) baz] () {}; // expected-error {{called object type 'int' is not a function}} \ + // expected-error {{expected ';'}} [foo(bar), baz] () {}; // ok [foo = bar baz]; // expected-warning {{receiver type 'int'}} expected-warning {{instance method '-baz'}} diff --git a/clang/test/Parser/objcxx11-invalid-lambda.cpp b/clang/test/Parser/objcxx11-invalid-lambda.cpp --- a/clang/test/Parser/objcxx11-invalid-lambda.cpp +++ b/clang/test/Parser/objcxx11-invalid-lambda.cpp @@ -1,10 +1,11 @@ // RUN: %clang_cc1 -fsyntax-only -verify -x objective-c++ -std=c++11 %s -void foo() { // expected-note {{to match this '{'}} +void foo() { int bar; auto baz = [ - bar( // expected-note {{to match this '('}} expected-note {{to match this '('}} + bar( // expected-note 2{{to match this '('}} \ + // expected-warning {{captures are a C++14 extension}} foo_undeclared() // expected-error{{use of undeclared identifier 'foo_undeclared'}} /* ) */ - ] () { }; // expected-error{{expected ')'}} -} // expected-error{{expected ')'}} expected-error {{expected ',' or ']'}} expected-error{{expected ';' at end of declaration}} expected-error{{expected '}'}} + ] () { }; // expected-error 2{{expected ')'}} +} diff --git a/clang/test/SemaCXX/builtins.cpp b/clang/test/SemaCXX/builtins.cpp --- a/clang/test/SemaCXX/builtins.cpp +++ b/clang/test/SemaCXX/builtins.cpp @@ -14,8 +14,7 @@ int equal(const char *s1, const char *s2) { return Compare(s1, s2) == 0; } -// FIXME: Our error recovery here sucks -template int equal<&__builtin_strcmp>(const char*, const char*); // expected-error {{builtin functions must be directly called}} expected-error {{expected unqualified-id}} expected-error {{expected ')'}} expected-note {{to match this '('}} +template int equal<&__builtin_strcmp>(const char*, const char*); // expected-error {{builtin functions must be directly called}} // PR13195 void f2() { diff --git a/clang/test/SemaCXX/cast-conversion.cpp b/clang/test/SemaCXX/cast-conversion.cpp --- a/clang/test/SemaCXX/cast-conversion.cpp +++ b/clang/test/SemaCXX/cast-conversion.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -triple x86_64-unknown-unknown -verify %s -std=c++11 +// RUN: %clang_cc1 -fsyntax-only -triple x86_64-unknown-unknown -verify %s -std=c++11 -Wno-unused struct R { R(int); diff --git a/clang/test/SemaCXX/cxx1z-copy-omission.cpp b/clang/test/SemaCXX/cxx1z-copy-omission.cpp --- a/clang/test/SemaCXX/cxx1z-copy-omission.cpp +++ b/clang/test/SemaCXX/cxx1z-copy-omission.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++1z -verify %s +// RUN: %clang_cc1 -std=c++1z -verify -Wno-unused %s struct Noncopyable { Noncopyable(); @@ -107,8 +107,10 @@ sizeof(make_indestructible()); // expected-error {{deleted}} sizeof(make_incomplete()); // expected-error {{incomplete}} typeid(Indestructible{}); // expected-error {{deleted}} - typeid(make_indestructible()); // expected-error {{deleted}} - typeid(make_incomplete()); // expected-error {{incomplete}} + typeid(make_indestructible()); // expected-error {{deleted}} \ + // expected-error {{need to include }} + typeid(make_incomplete()); // expected-error {{incomplete}} \ + // expected-error {{need to include }} // FIXME: The first two cases here are now also valid in C++17 onwards. using I = decltype(Indestructible()); // expected-error {{deleted}} diff --git a/clang/test/SemaCXX/decltype-crash.cpp b/clang/test/SemaCXX/decltype-crash.cpp --- a/clang/test/SemaCXX/decltype-crash.cpp +++ b/clang/test/SemaCXX/decltype-crash.cpp @@ -3,5 +3,8 @@ int& a(); void f() { - decltype(a()) c; // expected-warning {{'decltype' is a keyword in C++11}} expected-error {{use of undeclared identifier 'decltype'}} + decltype(a()) c; // expected-warning {{'decltype' is a keyword in C++11}} \ + // expected-error {{use of undeclared identifier 'decltype'}} \ + // expected-error {{expected ';' after expression}} \ + // expected-error {{use of undeclared identifier 'c'}} } diff --git a/clang/test/SemaCXX/varargs.cpp b/clang/test/SemaCXX/varargs.cpp --- a/clang/test/SemaCXX/varargs.cpp +++ b/clang/test/SemaCXX/varargs.cpp @@ -22,7 +22,8 @@ // default ctor. void record_context(int a, ...) { struct Foo { - // expected-error@+1 {{'va_start' cannot be used outside a function}} + // expected-error@+2 {{'va_start' cannot be used outside a function}} + // expected-error@+1 {{default argument references parameter 'a'}} void meth(int a, int b = (__builtin_va_start(ap, a), 0)) {} }; } diff --git a/clang/test/SemaOpenCLCXX/address-space-references.cl b/clang/test/SemaOpenCLCXX/address-space-references.cl --- a/clang/test/SemaOpenCLCXX/address-space-references.cl +++ b/clang/test/SemaOpenCLCXX/address-space-references.cl @@ -11,5 +11,5 @@ int bar(const unsigned int &i); void foo() { - bar(1) // expected-error{{binding reference of type 'const __global unsigned int' to value of type 'int' changes address space}} + bar(1); // expected-error{{binding reference of type 'const __global unsigned int' to value of type 'int' changes address space}} } diff --git a/clang/test/SemaTemplate/instantiate-init.cpp b/clang/test/SemaTemplate/instantiate-init.cpp --- a/clang/test/SemaTemplate/instantiate-init.cpp +++ b/clang/test/SemaTemplate/instantiate-init.cpp @@ -100,7 +100,7 @@ integral_c<1> ic1 = array_lengthof(Description::data); (void)sizeof(array_lengthof(Description::data)); - sizeof(array_lengthof( // expected-error{{no matching function for call to 'array_lengthof'}} + (void)sizeof(array_lengthof( // expected-error{{no matching function for call to 'array_lengthof'}} Description::data // expected-note{{in instantiation of static data member 'PR7985::Description::data' 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 @@ -291,6 +291,7 @@ case Stmt::ObjCDictionaryLiteralClass: case Stmt::ObjCBoxedExprClass: case Stmt::ObjCSubscriptRefExprClass: + case Stmt::RecoveryExprClass: K = CXCursor_UnexposedExpr; break;