Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -360,6 +360,11 @@ /// just a regular sub-expression. SourceLocation ExprStatementTokLoc; + /// Tests whether an expression value is discarded based on token lookahead. + /// It will return true if the lexer is currently processing the }) + /// terminating a GNU statement expression and false otherwise. + bool isExprValueDiscarded(); + public: Parser(Preprocessor &PP, Sema &Actions, bool SkipFunctionBodies); ~Parser() override; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -1365,6 +1365,7 @@ void PopCompoundScope(); sema::CompoundScopeInfo &getCurCompoundScope() const; + bool isCurCompoundStmtAStmtExpr() const; bool hasAnyUnrecoverableErrorsInThisFunction() const; @@ -3685,16 +3686,17 @@ return MakeFullExpr(Arg, Arg ? Arg->getExprLoc() : SourceLocation()); } FullExprArg MakeFullExpr(Expr *Arg, SourceLocation CC) { - return FullExprArg(ActOnFinishFullExpr(Arg, CC).get()); + return FullExprArg( + ActOnFinishFullExpr(Arg, CC, /*DiscardedValue*/ false).get()); } FullExprArg MakeFullDiscardedValueExpr(Expr *Arg) { ExprResult FE = - ActOnFinishFullExpr(Arg, Arg ? Arg->getExprLoc() : SourceLocation(), - /*DiscardedValue*/ true); + ActOnFinishFullExpr(Arg, Arg ? Arg->getExprLoc() : SourceLocation(), + /*DiscardedValue*/ true); return FullExprArg(FE.get()); } - StmtResult ActOnExprStmt(ExprResult Arg); + StmtResult ActOnExprStmt(ExprResult Arg, bool DiscardedValue = true); StmtResult ActOnExprStmtError(); StmtResult ActOnNullStmt(SourceLocation SemiLoc, @@ -5340,13 +5342,12 @@ CreateMaterializeTemporaryExpr(QualType T, Expr *Temporary, bool BoundToLvalueReference); - ExprResult ActOnFinishFullExpr(Expr *Expr) { - return ActOnFinishFullExpr(Expr, Expr ? Expr->getExprLoc() - : SourceLocation()); + ExprResult ActOnFinishFullExpr(Expr *Expr, bool DiscardedValue) { + return ActOnFinishFullExpr( + Expr, Expr ? Expr->getExprLoc() : SourceLocation(), DiscardedValue); } ExprResult ActOnFinishFullExpr(Expr *Expr, SourceLocation CC, - bool DiscardedValue = false, - bool IsConstexpr = false); + bool DiscardedValue, bool IsConstexpr = false); StmtResult ActOnFinishFullStmt(Stmt *Stmt); // Marks SS invalid if it represents an incomplete type. Index: lib/Parse/ParseObjc.cpp =================================================================== --- lib/Parse/ParseObjc.cpp +++ lib/Parse/ParseObjc.cpp @@ -2741,7 +2741,7 @@ // Otherwise, eat the semicolon. ExpectAndConsumeSemi(diag::err_expected_semi_after_expr); - return Actions.ActOnExprStmt(Res); + return Actions.ActOnExprStmt(Res, isExprValueDiscarded()); } ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) { Index: lib/Parse/ParseOpenMP.cpp =================================================================== --- lib/Parse/ParseOpenMP.cpp +++ lib/Parse/ParseOpenMP.cpp @@ -314,7 +314,7 @@ Actions.ActOnOpenMPDeclareReductionCombinerStart(getCurScope(), D); ExprResult CombinerResult = Actions.ActOnFinishFullExpr(ParseAssignmentExpression().get(), - D->getLocation(), /*DiscardedValue=*/true); + D->getLocation(), /*DiscardedValue*/ false); Actions.ActOnOpenMPDeclareReductionCombinerEnd(D, CombinerResult.get()); if (CombinerResult.isInvalid() && Tok.isNot(tok::r_paren) && @@ -356,7 +356,7 @@ if (Actions.getLangOpts().CPlusPlus) { InitializerResult = Actions.ActOnFinishFullExpr( ParseAssignmentExpression().get(), D->getLocation(), - /*DiscardedValue=*/true); + /*DiscardedValue*/ false); } else { ConsumeToken(); ParseOpenMPReductionInitializerForDecl(OmpPrivParm); @@ -364,7 +364,7 @@ } else { InitializerResult = Actions.ActOnFinishFullExpr( ParseAssignmentExpression().get(), D->getLocation(), - /*DiscardedValue=*/true); + /*DiscardedValue*/ false); } Actions.ActOnOpenMPDeclareReductionInitializerEnd( D, InitializerResult.get(), OmpPrivParm); @@ -1455,7 +1455,7 @@ ExprResult LHS(ParseCastExpression( /*isUnaryExpression=*/false, /*isAddressOfOperand=*/false, NotTypeCast)); ExprResult Val(ParseRHSOfBinaryExpression(LHS, prec::Conditional)); - Val = Actions.ActOnFinishFullExpr(Val.get(), ELoc); + Val = Actions.ActOnFinishFullExpr(Val.get(), ELoc, /*DiscardedValue*/ false); // Parse ')'. RLoc = Tok.getLocation(); @@ -1711,7 +1711,8 @@ SourceLocation ELoc = Tok.getLocation(); ExprResult LHS(ParseCastExpression(false, false, NotTypeCast)); Val = ParseRHSOfBinaryExpression(LHS, prec::Conditional); - Val = Actions.ActOnFinishFullExpr(Val.get(), ELoc); + Val = + Actions.ActOnFinishFullExpr(Val.get(), ELoc, /*DiscardedValue*/ false); } // Parse ')'. @@ -1996,7 +1997,8 @@ Data.ColonLoc = Tok.getLocation(); SourceLocation ELoc = ConsumeToken(); ExprResult Tail = ParseAssignmentExpression(); - Tail = Actions.ActOnFinishFullExpr(Tail.get(), ELoc); + Tail = + Actions.ActOnFinishFullExpr(Tail.get(), ELoc, /*DiscardedValue*/ false); if (Tail.isUsable()) Data.TailExpr = Tail.get(); else Index: lib/Parse/ParseStmt.cpp =================================================================== --- lib/Parse/ParseStmt.cpp +++ lib/Parse/ParseStmt.cpp @@ -439,7 +439,7 @@ // Otherwise, eat the semicolon. ExpectAndConsumeSemi(diag::err_expected_semi_after_expr); - return Actions.ActOnExprStmt(Expr); + return Actions.ActOnExprStmt(Expr, isExprValueDiscarded()); } /// ParseSEHTryBlockCommon @@ -958,6 +958,16 @@ return true; } +bool Parser::isExprValueDiscarded() { + if (Actions.isCurCompoundStmtAStmtExpr()) { + // Look to see if the next two tokens close the statement expression; + // if so, this expression statement is the last statement in a + // statment expression. + return Tok.isNot(tok::r_brace) || NextToken().isNot(tok::r_paren); + } + return true; +} + /// ParseCompoundStatementBody - Parse a sequence of statements and invoke the /// ActOnCompoundStmt action. This expects the '{' to be the current token, and /// consume the '}' at the end of the block. It does not manipulate the scope @@ -1062,7 +1072,7 @@ // Eat the semicolon at the end of stmt and convert the expr into a // statement. ExpectAndConsumeSemi(diag::err_expected_semi_after_expr); - R = Actions.ActOnExprStmt(Res); + R = Actions.ActOnExprStmt(Res, isExprValueDiscarded()); } } Index: lib/Sema/SemaCoroutine.cpp =================================================================== --- lib/Sema/SemaCoroutine.cpp +++ lib/Sema/SemaCoroutine.cpp @@ -647,7 +647,7 @@ return StmtError(); Suspend = BuildResolvedCoawaitExpr(Loc, Suspend.get(), /*IsImplicit*/ true); - Suspend = ActOnFinishFullExpr(Suspend.get()); + Suspend = ActOnFinishFullExpr(Suspend.get(), /*DiscardedValue*/ false); if (Suspend.isInvalid()) { Diag(Loc, diag::note_coroutine_promise_suspend_implicitly_required) << ((Name == "initial_suspend") ? 0 : 1); @@ -868,7 +868,7 @@ if (PC.isInvalid()) return StmtError(); - Expr *PCE = ActOnFinishFullExpr(PC.get()).get(); + Expr *PCE = ActOnFinishFullExpr(PC.get(), /*DiscardedValue*/ false).get(); Stmt *Res = new (Context) CoreturnStmt(Loc, E, PCE, IsImplicit); return Res; @@ -1237,7 +1237,7 @@ ExprResult NewExpr = S.ActOnCallExpr(S.getCurScope(), NewRef.get(), Loc, NewArgs, Loc); - NewExpr = S.ActOnFinishFullExpr(NewExpr.get()); + NewExpr = S.ActOnFinishFullExpr(NewExpr.get(), /*DiscardedValue*/ false); if (NewExpr.isInvalid()) return false; @@ -1263,7 +1263,8 @@ ExprResult DeleteExpr = S.ActOnCallExpr(S.getCurScope(), DeleteRef.get(), Loc, DeleteArgs, Loc); - DeleteExpr = S.ActOnFinishFullExpr(DeleteExpr.get()); + DeleteExpr = + S.ActOnFinishFullExpr(DeleteExpr.get(), /*DiscardedValue*/ false); if (DeleteExpr.isInvalid()) return false; @@ -1348,7 +1349,8 @@ ExprResult UnhandledException = buildPromiseCall(S, Fn.CoroutinePromise, Loc, "unhandled_exception", None); - UnhandledException = S.ActOnFinishFullExpr(UnhandledException.get(), Loc); + UnhandledException = S.ActOnFinishFullExpr(UnhandledException.get(), Loc, + /*DiscardedValue*/ false); if (UnhandledException.isInvalid()) return false; @@ -1401,7 +1403,8 @@ "get_return_object type must no longer be dependent"); if (FnRetType->isVoidType()) { - ExprResult Res = S.ActOnFinishFullExpr(this->ReturnValue, Loc); + ExprResult Res = + S.ActOnFinishFullExpr(this->ReturnValue, Loc, /*DiscardedValue*/ false); if (Res.isInvalid()) return false; @@ -1433,7 +1436,7 @@ if (Res.isInvalid()) return false; - Res = S.ActOnFinishFullExpr(Res.get()); + Res = S.ActOnFinishFullExpr(Res.get(), /*DiscardedValue*/ false); if (Res.isInvalid()) return false; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -11204,9 +11204,9 @@ // struct T { S a, b; } t = { Temp(), Temp() } // // we should destroy the first Temp before constructing the second. - ExprResult Result = ActOnFinishFullExpr(Init, VDecl->getLocation(), - false, - VDecl->isConstexpr()); + ExprResult Result = + ActOnFinishFullExpr(Init, VDecl->getLocation(), + /*DiscardedValue*/ false, VDecl->isConstexpr()); if (Result.isInvalid()) { VDecl->setInvalidDecl(); return; Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -1205,7 +1205,7 @@ E = Seq.Perform(S, Entity, Kind, Init); if (E.isInvalid()) return true; - E = S.ActOnFinishFullExpr(E.get(), Loc); + E = S.ActOnFinishFullExpr(E.get(), Loc, /*DiscardedValue*/ false); if (E.isInvalid()) return true; RefVD->setInit(E.get()); @@ -3682,7 +3682,7 @@ // C++11 [class.base.init]p7: // The initialization of each base and member constitutes a // full-expression. - Init = ActOnFinishFullExpr(Init.get(), InitLoc); + Init = ActOnFinishFullExpr(Init.get(), InitLoc, /*DiscardedValue*/ false); if (Init.isInvalid()) { FD->setInvalidDecl(); return; @@ -4040,7 +4040,8 @@ // C++11 [class.base.init]p7: // The initialization of each base and member constitutes a // full-expression. - MemberInit = ActOnFinishFullExpr(MemberInit.get(), InitRange.getBegin()); + MemberInit = ActOnFinishFullExpr(MemberInit.get(), InitRange.getBegin(), + /*DiscardedValue*/ false); if (MemberInit.isInvalid()) return true; @@ -4095,8 +4096,8 @@ // C++11 [class.base.init]p7: // The initialization of each base and member constitutes a // full-expression. - DelegationInit = ActOnFinishFullExpr(DelegationInit.get(), - InitRange.getBegin()); + DelegationInit = ActOnFinishFullExpr( + DelegationInit.get(), InitRange.getBegin(), /*DiscardedValue*/ false); if (DelegationInit.isInvalid()) return true; @@ -4225,7 +4226,8 @@ // C++11 [class.base.init]p7: // The initialization of each base and member constitutes a // full-expression. - BaseInit = ActOnFinishFullExpr(BaseInit.get(), InitRange.getBegin()); + BaseInit = ActOnFinishFullExpr(BaseInit.get(), InitRange.getBegin(), + /*DiscardedValue*/ false); if (BaseInit.isInvalid()) return true; Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -4736,8 +4736,9 @@ if (Result.isInvalid()) return true; - Result = ActOnFinishFullExpr(Result.getAs(), - Param->getOuterLocStart()); + Result = + ActOnFinishFullExpr(Result.getAs(), Param->getOuterLocStart(), + /*DiscardedValue*/ false); if (Result.isInvalid()) return true; Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -7803,6 +7803,8 @@ FullExpr = IgnoredValueConversions(FullExpr.get()); if (FullExpr.isInvalid()) return ExprError(); + + DiagnoseUnusedExprResult(FullExpr.get()); } FullExpr = CorrectDelayedTyposInExpr(FullExpr.get()); Index: lib/Sema/SemaLambda.cpp =================================================================== --- lib/Sema/SemaLambda.cpp +++ lib/Sema/SemaLambda.cpp @@ -1724,7 +1724,7 @@ /*NRVO=*/false), CurrentLocation, Src); if (!Init.isInvalid()) - Init = ActOnFinishFullExpr(Init.get()); + Init = ActOnFinishFullExpr(Init.get(), /*DiscardedValue*/ false); if (Init.isInvalid()) return ExprError(); Index: lib/Sema/SemaOpenMP.cpp =================================================================== --- lib/Sema/SemaOpenMP.cpp +++ lib/Sema/SemaOpenMP.cpp @@ -5299,7 +5299,7 @@ LastIteration.get(), UB.get()); EUB = SemaRef.BuildBinOp(CurScope, InitLoc, BO_Assign, UB.get(), CondOp.get()); - EUB = SemaRef.ActOnFinishFullExpr(EUB.get()); + EUB = SemaRef.ActOnFinishFullExpr(EUB.get(), /*DiscardedValue*/ false); // If we have a combined directive that combines 'distribute', 'for' or // 'simd' we need to be able to access the bounds of the schedule of the @@ -5328,7 +5328,8 @@ LastIteration.get(), CombUB.get()); CombEUB = SemaRef.BuildBinOp(CurScope, InitLoc, BO_Assign, CombUB.get(), CombCondOp.get()); - CombEUB = SemaRef.ActOnFinishFullExpr(CombEUB.get()); + CombEUB = + SemaRef.ActOnFinishFullExpr(CombEUB.get(), /*DiscardedValue*/ false); const CapturedDecl *CD = cast(AStmt)->getCapturedDecl(); // We expect to have at least 2 more parameters than the 'parallel' @@ -5362,7 +5363,7 @@ ? LB.get() : SemaRef.ActOnIntegerConstant(SourceLocation(), 0).get(); Init = SemaRef.BuildBinOp(CurScope, InitLoc, BO_Assign, IV.get(), RHS); - Init = SemaRef.ActOnFinishFullExpr(Init.get()); + Init = SemaRef.ActOnFinishFullExpr(Init.get(), /*DiscardedValue*/ false); if (isOpenMPLoopBoundSharingDirective(DKind)) { Expr *CombRHS = @@ -5373,7 +5374,8 @@ : SemaRef.ActOnIntegerConstant(SourceLocation(), 0).get(); CombInit = SemaRef.BuildBinOp(CurScope, InitLoc, BO_Assign, IV.get(), CombRHS); - CombInit = SemaRef.ActOnFinishFullExpr(CombInit.get()); + CombInit = + SemaRef.ActOnFinishFullExpr(CombInit.get(), /*DiscardedValue*/ false); } } @@ -5405,7 +5407,7 @@ if (!Inc.isUsable()) return 0; Inc = SemaRef.BuildBinOp(CurScope, IncLoc, BO_Assign, IV.get(), Inc.get()); - Inc = SemaRef.ActOnFinishFullExpr(Inc.get()); + Inc = SemaRef.ActOnFinishFullExpr(Inc.get(), /*DiscardedValue*/ false); if (!Inc.isUsable()) return 0; @@ -5423,7 +5425,8 @@ // LB = LB + ST NextLB = SemaRef.BuildBinOp(CurScope, IncLoc, BO_Assign, LB.get(), NextLB.get()); - NextLB = SemaRef.ActOnFinishFullExpr(NextLB.get()); + NextLB = + SemaRef.ActOnFinishFullExpr(NextLB.get(), /*DiscardedValue*/ false); if (!NextLB.isUsable()) return 0; // UB + ST @@ -5433,7 +5436,8 @@ // UB = UB + ST NextUB = SemaRef.BuildBinOp(CurScope, IncLoc, BO_Assign, UB.get(), NextUB.get()); - NextUB = SemaRef.ActOnFinishFullExpr(NextUB.get()); + NextUB = + SemaRef.ActOnFinishFullExpr(NextUB.get(), /*DiscardedValue*/ false); if (!NextUB.isUsable()) return 0; if (isOpenMPLoopBoundSharingDirective(DKind)) { @@ -5444,7 +5448,8 @@ // LB = LB + ST CombNextLB = SemaRef.BuildBinOp(CurScope, IncLoc, BO_Assign, CombLB.get(), CombNextLB.get()); - CombNextLB = SemaRef.ActOnFinishFullExpr(CombNextLB.get()); + CombNextLB = SemaRef.ActOnFinishFullExpr(CombNextLB.get(), + /*DiscardedValue*/ false); if (!CombNextLB.isUsable()) return 0; // UB + ST @@ -5455,7 +5460,8 @@ // UB = UB + ST CombNextUB = SemaRef.BuildBinOp(CurScope, IncLoc, BO_Assign, CombUB.get(), CombNextUB.get()); - CombNextUB = SemaRef.ActOnFinishFullExpr(CombNextUB.get()); + CombNextUB = SemaRef.ActOnFinishFullExpr(CombNextUB.get(), + /*DiscardedValue*/ false); if (!CombNextUB.isUsable()) return 0; } @@ -5476,7 +5482,8 @@ assert(DistInc.isUsable() && "distribute inc expr was not built"); DistInc = SemaRef.BuildBinOp(CurScope, DistIncLoc, BO_Assign, IV.get(), DistInc.get()); - DistInc = SemaRef.ActOnFinishFullExpr(DistInc.get()); + DistInc = + SemaRef.ActOnFinishFullExpr(DistInc.get(), /*DiscardedValue*/ false); assert(DistInc.isUsable() && "distribute inc expr was not built"); // Build expression: UB = min(UB, prevUB) for #for in composite or combined @@ -5488,7 +5495,8 @@ DistEUBLoc, DistEUBLoc, IsUBGreater.get(), PrevUB.get(), UB.get()); PrevEUB = SemaRef.BuildBinOp(CurScope, DistIncLoc, BO_Assign, UB.get(), CondOp.get()); - PrevEUB = SemaRef.ActOnFinishFullExpr(PrevEUB.get()); + PrevEUB = + SemaRef.ActOnFinishFullExpr(PrevEUB.get(), /*DiscardedValue*/ false); // Build IV <= PrevUB to be used in parallel for is in combination with // a distribute directive with schedule(static, 1) @@ -5592,8 +5600,10 @@ Built.IterationVarRef = IV.get(); Built.LastIteration = LastIteration.get(); Built.NumIterations = NumIterations.get(); - Built.CalcLastIteration = - SemaRef.ActOnFinishFullExpr(CalcLastIteration.get()).get(); + Built.CalcLastIteration = SemaRef + .ActOnFinishFullExpr(CalcLastIteration.get(), + /*DiscardedValue*/ false) + .get(); Built.PreCond = PreCond.get(); Built.PreInits = buildPreInits(C, Captures); Built.Cond = Cond.get(); @@ -10246,8 +10256,8 @@ PseudoDstExpr, PseudoSrcExpr); if (AssignmentOp.isInvalid()) continue; - AssignmentOp = ActOnFinishFullExpr(AssignmentOp.get(), ELoc, - /*DiscardedValue=*/true); + AssignmentOp = + ActOnFinishFullExpr(AssignmentOp.get(), ELoc, /*DiscardedValue*/ false); if (AssignmentOp.isInvalid()) continue; @@ -11253,7 +11263,8 @@ BO_Assign, LHSDRE, ConditionalOp); } if (ReductionOp.isUsable()) - ReductionOp = S.ActOnFinishFullExpr(ReductionOp.get()); + ReductionOp = S.ActOnFinishFullExpr(ReductionOp.get(), + /*DiscardedValue*/ false); } if (!ReductionOp.isUsable()) continue; @@ -11591,7 +11602,7 @@ buildDeclRefExpr(*this, SaveVar, StepExpr->getType(), StepLoc); ExprResult CalcStep = BuildBinOp(CurScope, StepLoc, BO_Assign, SaveRef.get(), StepExpr); - CalcStep = ActOnFinishFullExpr(CalcStep.get()); + CalcStep = ActOnFinishFullExpr(CalcStep.get(), /*DiscardedValue*/ false); // Warn about zero linear step (it would be probably better specified as // making corresponding variables 'const'). @@ -11679,7 +11690,7 @@ else Update = *CurPrivate; Update = SemaRef.ActOnFinishFullExpr(Update.get(), DE->getBeginLoc(), - /*DiscardedValue=*/true); + /*DiscardedValue*/ false); // Build final: Var = InitExpr + NumIterations * Step ExprResult Final; @@ -11690,7 +11701,7 @@ else Final = *CurPrivate; Final = SemaRef.ActOnFinishFullExpr(Final.get(), DE->getBeginLoc(), - /*DiscardedValue=*/true); + /*DiscardedValue*/ false); if (!Update.isUsable() || !Final.isUsable()) { Updates.push_back(nullptr); @@ -11858,7 +11869,7 @@ if (AssignmentOp.isInvalid()) continue; AssignmentOp = ActOnFinishFullExpr(AssignmentOp.get(), DE->getExprLoc(), - /*DiscardedValue=*/true); + /*DiscardedValue*/ false); if (AssignmentOp.isInvalid()) continue; @@ -11966,8 +11977,8 @@ DSAStack->getCurScope(), ELoc, BO_Assign, PseudoDstExpr, PseudoSrcExpr); if (AssignmentOp.isInvalid()) continue; - AssignmentOp = ActOnFinishFullExpr(AssignmentOp.get(), ELoc, - /*DiscardedValue=*/true); + AssignmentOp = + ActOnFinishFullExpr(AssignmentOp.get(), ELoc, /*DiscardedValue*/ false); if (AssignmentOp.isInvalid()) continue; Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -42,12 +42,11 @@ using namespace clang; using namespace sema; -StmtResult Sema::ActOnExprStmt(ExprResult FE) { +StmtResult Sema::ActOnExprStmt(ExprResult FE, bool DiscardedValue) { if (FE.isInvalid()) return StmtError(); - FE = ActOnFinishFullExpr(FE.get(), FE.get()->getExprLoc(), - /*DiscardedValue*/ true); + FE = ActOnFinishFullExpr(FE.get(), FE.get()->getExprLoc(), DiscardedValue); if (FE.isInvalid()) return StmtError(); @@ -349,6 +348,10 @@ return getCurFunction()->CompoundScopes.back(); } +bool Sema::isCurCompoundStmtAStmtExpr() const { + return getCurCompoundScope().IsStmtExpr; +} + StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R, ArrayRef Elts, bool isStmtExpr) { const unsigned NumElts = Elts.size(); @@ -371,14 +374,6 @@ Diag(D->getLocation(), diag::ext_mixed_decls_code); } } - // Warn about unused expressions in statements. - for (unsigned i = 0; i != NumElts; ++i) { - // Ignore statements that are last in a statement expression. - if (isStmtExpr && i == NumElts - 1) - continue; - - DiagnoseUnusedExprResult(Elts[i]); - } // Check for suspicious empty body (null statement) in `for' and `while' // statements. Don't do anything for template instantiations, this just adds @@ -470,15 +465,12 @@ /// ActOnCaseStmtBody - This installs a statement as the body of a case. void Sema::ActOnCaseStmtBody(Stmt *S, Stmt *SubStmt) { - DiagnoseUnusedExprResult(SubStmt); cast(S)->setSubStmt(SubStmt); } StmtResult Sema::ActOnDefaultStmt(SourceLocation DefaultLoc, SourceLocation ColonLoc, Stmt *SubStmt, Scope *CurScope) { - DiagnoseUnusedExprResult(SubStmt); - if (getCurFunction()->SwitchStack.empty()) { Diag(DefaultLoc, diag::err_default_not_in_switch); return SubStmt; @@ -572,9 +564,6 @@ if (IsConstexpr || isa(Cond.get().second)) setFunctionHasBranchProtectedScope(); - DiagnoseUnusedExprResult(thenStmt); - DiagnoseUnusedExprResult(elseStmt); - return IfStmt::Create(Context, IfLoc, IsConstexpr, InitStmt, Cond.get().first, Cond.get().second, thenStmt, ElseLoc, elseStmt); } @@ -1302,8 +1291,6 @@ !Diags.isIgnored(diag::warn_comma_operator, CondVal.second->getExprLoc())) CommaVisitor(*this).Visit(CondVal.second); - DiagnoseUnusedExprResult(Body); - if (isa(Body)) getCurCompoundScope().setHasEmptyLoopBodies(); @@ -1323,7 +1310,7 @@ return StmtError(); Cond = CondResult.get(); - CondResult = ActOnFinishFullExpr(Cond, DoLoc); + CondResult = ActOnFinishFullExpr(Cond, DoLoc, /*DiscardedValue*/ false); if (CondResult.isInvalid()) return StmtError(); Cond = CondResult.get(); @@ -1333,8 +1320,6 @@ !Diags.isIgnored(diag::warn_comma_operator, Cond->getExprLoc())) CommaVisitor(*this).Visit(Cond); - DiagnoseUnusedExprResult(Body); - return new (Context) DoStmt(Body, Cond, DoLoc, WhileLoc, CondRParen); } @@ -1779,11 +1764,6 @@ CommaVisitor(*this).Visit(Second.get().second); Expr *Third = third.release().getAs(); - - DiagnoseUnusedExprResult(First); - DiagnoseUnusedExprResult(Third); - DiagnoseUnusedExprResult(Body); - if (isa(Body)) getCurCompoundScope().setHasEmptyLoopBodies(); @@ -1803,7 +1783,7 @@ if (result.isInvalid()) return StmtError(); E = result.get(); - ExprResult FullExpr = ActOnFinishFullExpr(E); + ExprResult FullExpr = ActOnFinishFullExpr(E, /*DiscardedValue*/ false); if (FullExpr.isInvalid()) return StmtError(); return StmtResult(static_cast(FullExpr.get())); @@ -1957,7 +1937,8 @@ if (CollectionExprResult.isInvalid()) return StmtError(); - CollectionExprResult = ActOnFinishFullExpr(CollectionExprResult.get()); + CollectionExprResult = + ActOnFinishFullExpr(CollectionExprResult.get(), /*DiscardedValue*/ false); if (CollectionExprResult.isInvalid()) return StmtError(); @@ -2594,7 +2575,8 @@ if (!NotEqExpr.isInvalid()) NotEqExpr = CheckBooleanCondition(ColonLoc, NotEqExpr.get()); if (!NotEqExpr.isInvalid()) - NotEqExpr = ActOnFinishFullExpr(NotEqExpr.get()); + NotEqExpr = + ActOnFinishFullExpr(NotEqExpr.get(), /*DiscardedValue*/ false); if (NotEqExpr.isInvalid()) { Diag(RangeLoc, diag::note_for_range_invalid_iterator) << RangeLoc << 0 << BeginRangeRef.get()->getType(); @@ -2617,7 +2599,7 @@ // co_await during the initial parse. IncrExpr = ActOnCoawaitExpr(S, CoawaitLoc, IncrExpr.get()); if (!IncrExpr.isInvalid()) - IncrExpr = ActOnFinishFullExpr(IncrExpr.get()); + IncrExpr = ActOnFinishFullExpr(IncrExpr.get(), /*DiscardedValue*/ false); if (IncrExpr.isInvalid()) { Diag(RangeLoc, diag::note_for_range_invalid_iterator) << RangeLoc << 2 << BeginRangeRef.get()->getType() ; @@ -2872,7 +2854,7 @@ return StmtError(); } - ExprResult ExprRes = ActOnFinishFullExpr(E); + ExprResult ExprRes = ActOnFinishFullExpr(E, /*DiscardedValue*/ false); if (ExprRes.isInvalid()) return StmtError(); E = ExprRes.get(); @@ -3222,7 +3204,8 @@ ExpressionEvaluationContext::DiscardedStatement && (HasDeducedReturnType || CurCap->HasImplicitReturnType)) { if (RetValExp) { - ExprResult ER = ActOnFinishFullExpr(RetValExp, ReturnLoc); + ExprResult ER = + ActOnFinishFullExpr(RetValExp, ReturnLoc, /*DiscardedValue*/ false); if (ER.isInvalid()) return StmtError(); RetValExp = ER.get(); @@ -3349,7 +3332,8 @@ } if (RetValExp) { - ExprResult ER = ActOnFinishFullExpr(RetValExp, ReturnLoc); + ExprResult ER = + ActOnFinishFullExpr(RetValExp, ReturnLoc, /*DiscardedValue*/ false); if (ER.isInvalid()) return StmtError(); RetValExp = ER.get(); @@ -3579,7 +3563,8 @@ ExpressionEvaluationContext::DiscardedStatement && FnRetType->getContainedAutoType()) { if (RetValExp) { - ExprResult ER = ActOnFinishFullExpr(RetValExp, ReturnLoc); + ExprResult ER = + ActOnFinishFullExpr(RetValExp, ReturnLoc, /*DiscardedValue*/ false); if (ER.isInvalid()) return StmtError(); RetValExp = ER.get(); @@ -3673,7 +3658,8 @@ } if (RetValExp) { - ExprResult ER = ActOnFinishFullExpr(RetValExp, ReturnLoc); + ExprResult ER = + ActOnFinishFullExpr(RetValExp, ReturnLoc, /*DiscardedValue*/ false); if (ER.isInvalid()) return StmtError(); RetValExp = ER.get(); @@ -3752,7 +3738,8 @@ } if (RetValExp) { - ExprResult ER = ActOnFinishFullExpr(RetValExp, ReturnLoc); + ExprResult ER = + ActOnFinishFullExpr(RetValExp, ReturnLoc, /*DiscardedValue*/ false); if (ER.isInvalid()) return StmtError(); RetValExp = ER.get(); @@ -3805,7 +3792,7 @@ if (Result.isInvalid()) return StmtError(); - Result = ActOnFinishFullExpr(Result.get()); + Result = ActOnFinishFullExpr(Result.get(), /*DiscardedValue*/ false); if (Result.isInvalid()) return StmtError(); Throw = Result.get(); @@ -3877,7 +3864,7 @@ } // The operand to @synchronized is a full-expression. - return ActOnFinishFullExpr(operand); + return ActOnFinishFullExpr(operand, /*DiscardedValue*/ false); } StmtResult Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -328,7 +328,7 @@ /// other mechanism. /// /// \returns the transformed statement. - StmtResult TransformStmt(Stmt *S); + StmtResult TransformStmt(Stmt *S, bool DiscardedValue = false); /// Transform the given statement. /// @@ -3269,8 +3269,8 @@ bool DeducibleTSTContext); }; -template -StmtResult TreeTransform::TransformStmt(Stmt *S) { +template +StmtResult TreeTransform::TransformStmt(Stmt *S, bool DiscardedValue) { if (!S) return S; @@ -3294,7 +3294,7 @@ if (E.isInvalid()) return StmtError(); - return getSema().ActOnExprStmt(E); + return getSema().ActOnExprStmt(E, DiscardedValue); } } @@ -4715,7 +4715,8 @@ } if (SizeResult.isInvalid()) return QualType(); - SizeResult = SemaRef.ActOnFinishFullExpr(SizeResult.get()); + SizeResult = + SemaRef.ActOnFinishFullExpr(SizeResult.get(), /*DiscardedValue*/ false); if (SizeResult.isInvalid()) return QualType(); @@ -6520,7 +6521,9 @@ bool SubStmtChanged = false; SmallVector Statements; for (auto *B : S->body()) { - StmtResult Result = getDerived().TransformStmt(B); + StmtResult Result = + getDerived().TransformStmt(B, !IsStmtExpr || B != S->body_back()); + if (Result.isInvalid()) { // Immediately fail if this was a DeclStmt, since it's very // likely that this will cause problems for future statements. Index: test/CXX/stmt.stmt/stmt.select/p3.cpp =================================================================== --- test/CXX/stmt.stmt/stmt.select/p3.cpp +++ test/CXX/stmt.stmt/stmt.select/p3.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s -// RUN: %clang_cc1 -fsyntax-only -std=c++1z -Wc++14-compat -verify %s -DCPP17 +// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -std=c++1z -Wc++14-compat -verify %s -DCPP17 int f(); @@ -71,7 +71,6 @@ // last loop above. It would be nice to remove this. void whileInitStatement2() { while (; false) {} // expected-error {{expected expression}} - // expected-warning@-1 {{expression result unused}} - // expected-error@-2 {{expected ';' after expression}} - // expected-error@-3 {{expected expression}} + // expected-error@-1 {{expected ';' after expression}} + // expected-error@-2 {{expected expression}} } Index: test/CodeCompletion/pragma-macro-token-caching.c =================================================================== --- test/CodeCompletion/pragma-macro-token-caching.c +++ test/CodeCompletion/pragma-macro-token-caching.c @@ -12,7 +12,7 @@ void completeParamPragmaError(int param) { Outer(__extension__({ _Pragma(2) })); // expected-error {{_Pragma takes a parenthesized string literal}} - param; + param; // expected-warning {{expression result unused}} } // RUN: %clang_cc1 -fsyntax-only -verify -code-completion-at=%s:16:1 %s | FileCheck %s Index: test/Parser/cxx1z-init-statement.cpp =================================================================== --- test/Parser/cxx1z-init-statement.cpp +++ test/Parser/cxx1z-init-statement.cpp @@ -13,9 +13,9 @@ if (T(n) = 0; n) {} // init-statement expressions - if (T{f()}; f()) {} - if (T{f()}, g, h; f()) {} // expected-warning 2{{unused}} - if (T(f()), g, h + 1; f()) {} // expected-warning 2{{unused}} + if (T{f()}; f()) {} // expected-warning {{expression result unused}} + if (T{f()}, g, h; f()) {} // expected-warning 2{{unused}} expected-warning {{expression result unused}} + if (T(f()), g, h + 1; f()) {} // expected-warning 2{{unused}} expected-warning {{expression result unused}} // condition declarations if (T(n){g}) {} @@ -35,7 +35,7 @@ // Likewise for 'switch' switch (int n; n) {} - switch (g; int g = 5) {} + switch (g; int g = 5) {} // expected-warning {{expression result unused}} if (int a, b; int c = a) { // expected-note 6{{previous}} int a; // expected-error {{redefinition}} Index: test/Parser/switch-recovery.cpp =================================================================== --- test/Parser/switch-recovery.cpp +++ test/Parser/switch-recovery.cpp @@ -105,7 +105,7 @@ expected-error {{expected expression}} 8:: x; // expected-error {{expected ';' after expression}} \ expected-error {{no member named 'x' in the global namespace; did you mean simply 'x'?}} \ - expected-warning 2 {{expression result unused}} + expected-warning {{expression result unused}} 9:: :y; // expected-error {{expected ';' after expression}} \ expected-error {{expected unqualified-id}} \ expected-warning {{expression result unused}} Index: test/SemaCXX/cxx1z-init-statement.cpp =================================================================== --- test/SemaCXX/cxx1z-init-statement.cpp +++ test/SemaCXX/cxx1z-init-statement.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -std=c++1z -verify %s -// RUN: %clang_cc1 -std=c++17 -verify %s +// RUN: %clang_cc1 -std=c++1z -Wno-unused-value -verify %s +// RUN: %clang_cc1 -std=c++17 -Wno-unused-value -verify %s void testIf() { int x = 0; @@ -12,7 +12,7 @@ int x = 0; // expected-error {{redefinition of 'x'}} if (x; int a = 0) ++a; - if (x, +x; int a = 0) // expected-note 2 {{previous definition is here}} expected-warning {{unused}} + if (x, +x; int a = 0) // expected-note 2 {{previous definition is here}} int a = 0; // expected-error {{redefinition of 'a'}} else int a = 0; // expected-error {{redefinition of 'a'}} @@ -48,7 +48,7 @@ ++a; } - switch (x, +x; int a = 0) { // expected-note {{previous definition is here}} expected-warning {{unused}} + switch (x, +x; int a = 0) { // expected-note {{previous definition is here}} case 0: int a = 0; // expected-error {{redefinition of 'a'}} // expected-note {{previous definition is here}} case 1: Index: test/SemaCXX/for-range-examples.cpp =================================================================== --- test/SemaCXX/for-range-examples.cpp +++ test/SemaCXX/for-range-examples.cpp @@ -176,9 +176,9 @@ // Make sure these don't crash. Better diagnostics would be nice. for (: {1, 2, 3}) {} // expected-error {{expected expression}} expected-error {{expected ';'}} - for (1 : {1, 2, 3}) {} // expected-error {{must declare a variable}} + for (1 : {1, 2, 3}) {} // expected-error {{must declare a variable}} expected-warning {{expression result unused}} for (+x : {1, 2, 3}) {} // expected-error {{undeclared identifier}} expected-error {{expected ';'}} - for (+y : {1, 2, 3}) {} // expected-error {{must declare a variable}} + for (+y : {1, 2, 3}) {} // expected-error {{must declare a variable}} expected-warning {{expression result unused}} } } @@ -244,7 +244,7 @@ { int b = 1, a[b]; a[0] = 0; - [&] { for (int c : a) 0; } (); + [&] { for (int c : a) 0; } (); // expected-warning {{expression result unused}} } Index: test/SemaCXX/warn-unused-result.cpp =================================================================== --- test/SemaCXX/warn-unused-result.cpp +++ test/SemaCXX/warn-unused-result.cpp @@ -33,6 +33,36 @@ const S &s4 = g1(); } +void testSubstmts(int i) { + switch (i) { + case 0: + f(); // expected-warning {{ignoring return value}} + default: + f(); // expected-warning {{ignoring return value}} + } + + if (i) + f(); // expected-warning {{ignoring return value}} + else + f(); // expected-warning {{ignoring return value}} + + while (i) + f(); // expected-warning {{ignoring return value}} + + do + f(); // expected-warning {{ignoring return value}} + while (i); + + for (f(); // expected-warning {{ignoring return value}} + ; + f() // expected-warning {{ignoring return value}} + ) + f(); // expected-warning {{ignoring return value}} + + f(), // expected-warning {{ignoring return value}} + (void)f(); +} + struct X { int foo() __attribute__((warn_unused_result)); }; @@ -206,3 +236,13 @@ (void)++p; } } // namespace + +namespace PR39837 { +[[clang::warn_unused_result]] int f(int); + +void g() { + int a[2]; + for (int b : a) + f(b); // expected-warning {{ignoring return value}} +} +} // namespace PR39837 Index: test/SemaObjCXX/foreach.mm =================================================================== --- test/SemaObjCXX/foreach.mm +++ test/SemaObjCXX/foreach.mm @@ -6,8 +6,8 @@ void f(NSArray *a) { id keys; for (int i : a); // expected-error{{selector element type 'int' is not a valid object}} - for ((id)2 : a); // expected-error {{for range declaration must declare a variable}} - for (2 : a); // expected-error {{for range declaration must declare a variable}} + for ((id)2 : a); // expected-error {{for range declaration must declare a variable}} expected-warning {{expression result unused}} + for (2 : a); // expected-error {{for range declaration must declare a variable}} expected-warning {{expression result unused}} for (id thisKey : keys); @@ -71,7 +71,7 @@ @end void test2(NSObject *collection) { Test2 *obj; - for (obj.prop : collection) { // expected-error {{for range declaration must declare a variable}} + for (obj.prop : collection) { // expected-error {{for range declaration must declare a variable}} expected-warning {{property access result unused}} } }