Refactor the way we handle diagnosing unused expression results.

Rather than sprinkle calls to DiagnoseUnusedExprResult() around in places where we want diagnostics, we now diagnose unused expression statements and full expressions in a more generic way when acting on the final expression statement. This results in more appropriate diagnostics for [[nodiscard]] where we were previously lacking them, such as when the body of a for loop is not a compound statement.

This patch fixes PR39837.

llvm-svn: 350404
This commit is contained in:
Aaron Ballman 2019-01-04 16:58:14 +00:00
parent c2054144ee
commit fb6deeb984
21 changed files with 196 additions and 122 deletions

View File

@ -360,6 +360,11 @@ class Parser : public CodeCompletionHandler {
/// 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;

View File

@ -1365,6 +1365,7 @@ public:
void PopCompoundScope();
sema::CompoundScopeInfo &getCurCompoundScope() const;
bool isCurCompoundStmtAStmtExpr() const;
bool hasAnyUnrecoverableErrorsInThisFunction() const;
@ -3685,16 +3686,17 @@ public:
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 @@ public:
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.

View File

@ -2741,7 +2741,7 @@ StmtResult Parser::ParseObjCAtStatement(SourceLocation AtLoc) {
// 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) {

View File

@ -314,7 +314,7 @@ Parser::ParseOpenMPDeclareReductionDirective(AccessSpecifier AS) {
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 @@ Parser::ParseOpenMPDeclareReductionDirective(AccessSpecifier AS) {
if (Actions.getLangOpts().CPlusPlus) {
InitializerResult = Actions.ActOnFinishFullExpr(
ParseAssignmentExpression().get(), D->getLocation(),
/*DiscardedValue=*/true);
/*DiscardedValue*/ false);
} else {
ConsumeToken();
ParseOpenMPReductionInitializerForDecl(OmpPrivParm);
@ -364,7 +364,7 @@ Parser::ParseOpenMPDeclareReductionDirective(AccessSpecifier AS) {
} else {
InitializerResult = Actions.ActOnFinishFullExpr(
ParseAssignmentExpression().get(), D->getLocation(),
/*DiscardedValue=*/true);
/*DiscardedValue*/ false);
}
Actions.ActOnOpenMPDeclareReductionInitializerEnd(
D, InitializerResult.get(), OmpPrivParm);
@ -1455,7 +1455,7 @@ ExprResult Parser::ParseOpenMPParensExpr(StringRef ClauseName,
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 @@ OMPClause *Parser::ParseOpenMPSingleExprWithArgClause(OpenMPClauseKind Kind,
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 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
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

View File

@ -439,7 +439,7 @@ StmtResult Parser::ParseExprStatement() {
// 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 @@ bool Parser::ConsumeNullStmt(StmtVector &Stmts) {
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 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) {
// 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());
}
}
@ -1698,8 +1708,16 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
if (!Value.isInvalid()) {
if (ForEach)
FirstPart = Actions.ActOnForEachLValueExpr(Value.get());
else
FirstPart = Actions.ActOnExprStmt(Value);
else {
// We already know this is not an init-statement within a for loop, so
// if we are parsing a C++11 range-based for loop, we should treat this
// expression statement as being a discarded value expression because
// we will err below. This way we do not warn on an unused expression
// that was an error in the first place, like with: for (expr : expr);
bool IsRangeBasedFor =
getLangOpts().CPlusPlus11 && !ForEach && Tok.is(tok::colon);
FirstPart = Actions.ActOnExprStmt(Value, !IsRangeBasedFor);
}
}
if (Tok.is(tok::semi)) {

View File

@ -647,7 +647,7 @@ bool Sema::ActOnCoroutineBodyStart(Scope *SC, SourceLocation KWLoc,
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 @@ StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E,
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 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
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 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
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 @@ bool CoroutineStmtBuilder::makeOnException() {
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 @@ bool CoroutineStmtBuilder::makeGroDeclAndReturnStmt() {
"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 @@ bool CoroutineStmtBuilder::makeGroDeclAndReturnStmt() {
if (Res.isInvalid())
return false;
Res = S.ActOnFinishFullExpr(Res.get());
Res = S.ActOnFinishFullExpr(Res.get(), /*DiscardedValue*/ false);
if (Res.isInvalid())
return false;

View File

@ -11204,9 +11204,9 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
// 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;

View File

@ -1205,7 +1205,7 @@ static bool checkTupleLikeDecomposition(Sema &S,
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 @@ void Sema::ActOnFinishCXXInClassMemberInitializer(Decl *D,
// 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 @@ Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init,
// 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 @@ Sema::BuildDelegatingInitializer(TypeSourceInfo *TInfo, Expr *Init,
// 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 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo,
// 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;

View File

@ -4736,8 +4736,9 @@ bool Sema::CheckCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD,
if (Result.isInvalid())
return true;
Result = ActOnFinishFullExpr(Result.getAs<Expr>(),
Param->getOuterLocStart());
Result =
ActOnFinishFullExpr(Result.getAs<Expr>(), Param->getOuterLocStart(),
/*DiscardedValue*/ false);
if (Result.isInvalid())
return true;

View File

@ -7803,6 +7803,8 @@ ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC,
FullExpr = IgnoredValueConversions(FullExpr.get());
if (FullExpr.isInvalid())
return ExprError();
DiagnoseUnusedExprResult(FullExpr.get());
}
FullExpr = CorrectDelayedTyposInExpr(FullExpr.get());

View File

@ -1724,7 +1724,7 @@ ExprResult Sema::BuildBlockForLambdaConversion(SourceLocation CurrentLocation,
/*NRVO=*/false),
CurrentLocation, Src);
if (!Init.isInvalid())
Init = ActOnFinishFullExpr(Init.get());
Init = ActOnFinishFullExpr(Init.get(), /*DiscardedValue*/ false);
if (Init.isInvalid())
return ExprError();

View File

@ -5320,7 +5320,7 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
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
@ -5349,7 +5349,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
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<CapturedStmt>(AStmt)->getCapturedDecl();
// We expect to have at least 2 more parameters than the 'parallel'
@ -5383,7 +5384,7 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
? 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 =
@ -5394,7 +5395,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
: 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);
}
}
@ -5426,7 +5428,7 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
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;
@ -5444,7 +5446,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
// 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
@ -5454,7 +5457,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
// 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)) {
@ -5465,7 +5469,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
// 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
@ -5476,7 +5481,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
// 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;
}
@ -5497,7 +5503,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
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
@ -5509,7 +5516,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
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)
@ -5613,8 +5621,10 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
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();
@ -10267,8 +10277,8 @@ OMPClause *Sema::ActOnOpenMPLastprivateClause(ArrayRef<Expr *> VarList,
PseudoDstExpr, PseudoSrcExpr);
if (AssignmentOp.isInvalid())
continue;
AssignmentOp = ActOnFinishFullExpr(AssignmentOp.get(), ELoc,
/*DiscardedValue=*/true);
AssignmentOp =
ActOnFinishFullExpr(AssignmentOp.get(), ELoc, /*DiscardedValue*/ false);
if (AssignmentOp.isInvalid())
continue;
@ -11274,7 +11284,8 @@ static bool actOnOMPReductionKindClause(
BO_Assign, LHSDRE, ConditionalOp);
}
if (ReductionOp.isUsable())
ReductionOp = S.ActOnFinishFullExpr(ReductionOp.get());
ReductionOp = S.ActOnFinishFullExpr(ReductionOp.get(),
/*DiscardedValue*/ false);
}
if (!ReductionOp.isUsable())
continue;
@ -11612,7 +11623,7 @@ OMPClause *Sema::ActOnOpenMPLinearClause(
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').
@ -11700,7 +11711,7 @@ static bool FinishOpenMPLinearClause(OMPLinearClause &Clause, DeclRefExpr *IV,
else
Update = *CurPrivate;
Update = SemaRef.ActOnFinishFullExpr(Update.get(), DE->getBeginLoc(),
/*DiscardedValue=*/true);
/*DiscardedValue*/ false);
// Build final: Var = InitExpr + NumIterations * Step
ExprResult Final;
@ -11711,7 +11722,7 @@ static bool FinishOpenMPLinearClause(OMPLinearClause &Clause, DeclRefExpr *IV,
else
Final = *CurPrivate;
Final = SemaRef.ActOnFinishFullExpr(Final.get(), DE->getBeginLoc(),
/*DiscardedValue=*/true);
/*DiscardedValue*/ false);
if (!Update.isUsable() || !Final.isUsable()) {
Updates.push_back(nullptr);
@ -11879,7 +11890,7 @@ OMPClause *Sema::ActOnOpenMPCopyinClause(ArrayRef<Expr *> VarList,
if (AssignmentOp.isInvalid())
continue;
AssignmentOp = ActOnFinishFullExpr(AssignmentOp.get(), DE->getExprLoc(),
/*DiscardedValue=*/true);
/*DiscardedValue*/ false);
if (AssignmentOp.isInvalid())
continue;
@ -11987,8 +11998,8 @@ OMPClause *Sema::ActOnOpenMPCopyprivateClause(ArrayRef<Expr *> VarList,
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;

View File

@ -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();
@ -348,6 +347,10 @@ sema::CompoundScopeInfo &Sema::getCurCompoundScope() const {
return getCurFunction()->CompoundScopes.back();
}
bool Sema::isCurCompoundStmtAStmtExpr() const {
return getCurCompoundScope().IsStmtExpr;
}
StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R,
ArrayRef<Stmt *> Elts, bool isStmtExpr) {
const unsigned NumElts = Elts.size();
@ -370,14 +373,6 @@ StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R,
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
@ -469,15 +464,12 @@ Sema::ActOnCaseStmt(SourceLocation CaseLoc, ExprResult LHSVal,
/// ActOnCaseStmtBody - This installs a statement as the body of a case.
void Sema::ActOnCaseStmtBody(Stmt *S, Stmt *SubStmt) {
DiagnoseUnusedExprResult(SubStmt);
cast<CaseStmt>(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;
@ -571,9 +563,6 @@ StmtResult Sema::BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
if (IsConstexpr || isa<ObjCAvailabilityCheckExpr>(Cond.get().second))
setFunctionHasBranchProtectedScope();
DiagnoseUnusedExprResult(thenStmt);
DiagnoseUnusedExprResult(elseStmt);
return IfStmt::Create(Context, IfLoc, IsConstexpr, InitStmt, Cond.get().first,
Cond.get().second, thenStmt, ElseLoc, elseStmt);
}
@ -1301,8 +1290,6 @@ StmtResult Sema::ActOnWhileStmt(SourceLocation WhileLoc, ConditionResult Cond,
!Diags.isIgnored(diag::warn_comma_operator, CondVal.second->getExprLoc()))
CommaVisitor(*this).Visit(CondVal.second);
DiagnoseUnusedExprResult(Body);
if (isa<NullStmt>(Body))
getCurCompoundScope().setHasEmptyLoopBodies();
@ -1322,7 +1309,7 @@ Sema::ActOnDoStmt(SourceLocation DoLoc, Stmt *Body,
return StmtError();
Cond = CondResult.get();
CondResult = ActOnFinishFullExpr(Cond, DoLoc);
CondResult = ActOnFinishFullExpr(Cond, DoLoc, /*DiscardedValue*/ false);
if (CondResult.isInvalid())
return StmtError();
Cond = CondResult.get();
@ -1332,8 +1319,6 @@ Sema::ActOnDoStmt(SourceLocation DoLoc, Stmt *Body,
!Diags.isIgnored(diag::warn_comma_operator, Cond->getExprLoc()))
CommaVisitor(*this).Visit(Cond);
DiagnoseUnusedExprResult(Body);
return new (Context) DoStmt(Body, Cond, DoLoc, WhileLoc, CondRParen);
}
@ -1778,11 +1763,6 @@ StmtResult Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
CommaVisitor(*this).Visit(Second.get().second);
Expr *Third = third.release().getAs<Expr>();
DiagnoseUnusedExprResult(First);
DiagnoseUnusedExprResult(Third);
DiagnoseUnusedExprResult(Body);
if (isa<NullStmt>(Body))
getCurCompoundScope().setHasEmptyLoopBodies();
@ -1802,7 +1782,7 @@ StmtResult Sema::ActOnForEachLValueExpr(Expr *E) {
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<Stmt*>(FullExpr.get()));
@ -1956,7 +1936,8 @@ Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc,
if (CollectionExprResult.isInvalid())
return StmtError();
CollectionExprResult = ActOnFinishFullExpr(CollectionExprResult.get());
CollectionExprResult =
ActOnFinishFullExpr(CollectionExprResult.get(), /*DiscardedValue*/ false);
if (CollectionExprResult.isInvalid())
return StmtError();
@ -2593,7 +2574,8 @@ StmtResult Sema::BuildCXXForRangeStmt(SourceLocation ForLoc,
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();
@ -2616,7 +2598,7 @@ StmtResult Sema::BuildCXXForRangeStmt(SourceLocation ForLoc,
// 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() ;
@ -2871,7 +2853,7 @@ Sema::ActOnIndirectGotoStmt(SourceLocation GotoLoc, SourceLocation StarLoc,
return StmtError();
}
ExprResult ExprRes = ActOnFinishFullExpr(E);
ExprResult ExprRes = ActOnFinishFullExpr(E, /*DiscardedValue*/ false);
if (ExprRes.isInvalid())
return StmtError();
E = ExprRes.get();
@ -3221,7 +3203,8 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
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();
@ -3348,7 +3331,8 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
}
if (RetValExp) {
ExprResult ER = ActOnFinishFullExpr(RetValExp, ReturnLoc);
ExprResult ER =
ActOnFinishFullExpr(RetValExp, ReturnLoc, /*DiscardedValue*/ false);
if (ER.isInvalid())
return StmtError();
RetValExp = ER.get();
@ -3578,7 +3562,8 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
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();
@ -3672,7 +3657,8 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
}
if (RetValExp) {
ExprResult ER = ActOnFinishFullExpr(RetValExp, ReturnLoc);
ExprResult ER =
ActOnFinishFullExpr(RetValExp, ReturnLoc, /*DiscardedValue*/ false);
if (ER.isInvalid())
return StmtError();
RetValExp = ER.get();
@ -3751,7 +3737,8 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
}
if (RetValExp) {
ExprResult ER = ActOnFinishFullExpr(RetValExp, ReturnLoc);
ExprResult ER =
ActOnFinishFullExpr(RetValExp, ReturnLoc, /*DiscardedValue*/ false);
if (ER.isInvalid())
return StmtError();
RetValExp = ER.get();
@ -3804,7 +3791,7 @@ StmtResult Sema::BuildObjCAtThrowStmt(SourceLocation AtLoc, Expr *Throw) {
if (Result.isInvalid())
return StmtError();
Result = ActOnFinishFullExpr(Result.get());
Result = ActOnFinishFullExpr(Result.get(), /*DiscardedValue*/ false);
if (Result.isInvalid())
return StmtError();
Throw = Result.get();
@ -3876,7 +3863,7 @@ Sema::ActOnObjCAtSynchronizedOperand(SourceLocation atLoc, Expr *operand) {
}
// The operand to @synchronized is a full-expression.
return ActOnFinishFullExpr(operand);
return ActOnFinishFullExpr(operand, /*DiscardedValue*/ false);
}
StmtResult

View File

@ -328,7 +328,7 @@ public:
/// 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 @@ private:
bool DeducibleTSTContext);
};
template<typename Derived>
StmtResult TreeTransform<Derived>::TransformStmt(Stmt *S) {
template <typename Derived>
StmtResult TreeTransform<Derived>::TransformStmt(Stmt *S, bool DiscardedValue) {
if (!S)
return S;
@ -3294,7 +3294,7 @@ StmtResult TreeTransform<Derived>::TransformStmt(Stmt *S) {
if (E.isInvalid())
return StmtError();
return getSema().ActOnExprStmt(E);
return getSema().ActOnExprStmt(E, DiscardedValue);
}
}
@ -4715,7 +4715,8 @@ TreeTransform<Derived>::TransformVariableArrayType(TypeLocBuilder &TLB,
}
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 @@ TreeTransform<Derived>::TransformCompoundStmt(CompoundStmt *S,
bool SubStmtChanged = false;
SmallVector<Stmt*, 8> 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.

View File

@ -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 @@ void whileInitStatement() {
// 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}}
}

View File

@ -12,7 +12,7 @@ void completeParam(int param) {
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

View File

@ -13,9 +13,9 @@ int f() {
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 @@ int f() {
// 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}}

View File

@ -105,7 +105,7 @@ void test9(int x) { // expected-note {{'x' declared here}}
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}}

View File

@ -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 @@ void testIf() {
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 @@ void testSwitch() {
++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:

View File

@ -244,7 +244,7 @@ void foo ()
{
int b = 1, a[b];
a[0] = 0;
[&] { for (int c : a) 0; } ();
[&] { for (int c : a) 0; } (); // expected-warning {{expression result unused}}
}

View File

@ -33,6 +33,36 @@ void test() {
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 f() {
(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