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 @@ -254,8 +254,8 @@ /// and ranges with expr to warn on and source locations/ranges appropriate /// for a warning. bool isUnusedResultAWarning(const Expr *&WarnExpr, SourceLocation &Loc, - SourceRange &R1, SourceRange &R2, - ASTContext &Ctx) const; + SourceRange &R1, SourceRange &R2, ASTContext &Ctx, + bool Always) const; /// isLValue - True if this expression is an "l-value" according to /// the rules of the current language. C and C++ give somewhat diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -775,6 +775,7 @@ def UnusedParameter : DiagGroup<"unused-parameter">; def UnusedButSetParameter : DiagGroup<"unused-but-set-parameter">; def UnusedResult : DiagGroup<"unused-result">; +def UnusedResultAlways : DiagGroup<"unused-result-always">; def PotentiallyEvaluatedExpression : DiagGroup<"potentially-evaluated-expression">; def UnevaluatedExpression : DiagGroup<"unevaluated-expression", [PotentiallyEvaluatedExpression]>; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8790,6 +8790,9 @@ def warn_unused_result_msg : Warning< "ignoring return value of function declared with %0 attribute: %1">, InGroup; +def warn_unused_result_always : Warning< + "ignoring return value of function">, + InGroup, DefaultIgnore; def warn_unused_result_typedef_unsupported_spelling : Warning< "'[[%select{nodiscard|gnu::warn_unused_result}0]]' attribute ignored when " "applied to a typedef; consider using '__attribute__((warn_unused_result))' " 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 @@ -2578,7 +2578,7 @@ /// warning. bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, SourceRange &R1, SourceRange &R2, - ASTContext &Ctx) const { + ASTContext &Ctx, bool Always) const { // Don't warn if the expr is type dependent. The type could end up // instantiating to void. if (isTypeDependent()) @@ -2593,18 +2593,20 @@ R1 = getSourceRange(); return true; case ParenExprClass: - return cast(this)->getSubExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return cast(this)->getSubExpr()->isUnusedResultAWarning( + WarnE, Loc, R1, R2, Ctx, Always); case GenericSelectionExprClass: - return cast(this)->getResultExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return cast(this) + ->getResultExpr() + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, Always); case CoawaitExprClass: case CoyieldExprClass: - return cast(this)->getResumeExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return cast(this) + ->getResumeExpr() + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, Always); case ChooseExprClass: - return cast(this)->getChosenSubExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return cast(this)->getChosenSubExpr()->isUnusedResultAWarning( + WarnE, Loc, R1, R2, Ctx, Always); case UnaryOperatorClass: { const UnaryOperator *UO = cast(this); @@ -2632,7 +2634,8 @@ return false; break; case UO_Extension: - return UO->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return UO->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + Always); } WarnE = this; Loc = UO->getOperatorLoc(); @@ -2653,12 +2656,15 @@ dyn_cast(BO->getRHS()->IgnoreParens())) if (IE->getValue() == 0) return false; - return BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + Always); // Consider '||', '&&' to have side effects if the LHS or RHS does. case BO_LAnd: case BO_LOr: - if (!BO->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx) || - !BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)) + if (!BO->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + Always) || + !BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + Always)) return false; break; } @@ -2680,12 +2686,15 @@ // be being used for control flow. Only warn if both the LHS and // RHS are warnings. const auto *Exp = cast(this); - return Exp->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx) && - Exp->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return Exp->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + Always) && + Exp->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + Always); } case BinaryConditionalOperatorClass: { const auto *Exp = cast(this); - return Exp->getFalseExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return Exp->getFalseExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + Always); } case MemberExprClass: @@ -2737,13 +2746,14 @@ // If this is a direct call, get the callee. const CallExpr *CE = cast(this); if (const Decl *FD = CE->getCalleeDecl()) { - // If the callee has attribute pure, const, or warn_unused_result, warn - // about it. void foo() { strlen("bar"); } should warn. + // If the callee has attribute pure, const, or warn_unused_result, or if + // -Wunused-result-always is enabled, warn about it. + // void foo() { strlen("bar"); } should warn. // // Note: If new cases are added here, DiagnoseUnusedExprResult should be // updated to match for QoI. - if (CE->hasUnusedResultAttr(Ctx) || - FD->hasAttr() || FD->hasAttr()) { + if (Always || CE->hasUnusedResultAttr(Ctx) || FD->hasAttr() || + FD->hasAttr()) { WarnE = this; Loc = CE->getCallee()->getBeginLoc(); R1 = CE->getCallee()->getSourceRange(); @@ -2845,7 +2855,8 @@ // Otherwise, warn if the result expression would warn. const Expr *Result = POE->getResultExpr(); - return Result && Result->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return Result && + Result->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, Always); } case StmtExprClass: { @@ -2857,10 +2868,10 @@ const CompoundStmt *CS = cast(this)->getSubStmt(); if (!CS->body_empty()) { if (const Expr *E = dyn_cast(CS->body_back())) - return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, Always); if (const LabelStmt *Label = dyn_cast(CS->body_back())) if (const Expr *E = dyn_cast(Label->getSubStmt())) - return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, Always); } if (getType()->isVoidType()) @@ -2896,7 +2907,7 @@ if (SubE->getType()->isArrayType()) return false; - return SubE->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return SubE->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, Always); } return false; } @@ -2904,7 +2915,8 @@ // If this is a cast to a constructor conversion, check the operand. // Otherwise, the result of the cast is unused. if (CE->getCastKind() == CK_ConstructorConversion) - return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + Always); if (CE->getCastKind() == CK_Dependent) return false; @@ -2928,14 +2940,15 @@ ICE->getSubExpr()->getType().isVolatileQualified()) return false; - return ICE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return ICE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + Always); } case CXXDefaultArgExprClass: - return (cast(this) - ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); + return (cast(this)->getExpr()->isUnusedResultAWarning( + WarnE, Loc, R1, R2, Ctx, Always)); case CXXDefaultInitExprClass: - return (cast(this) - ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); + return (cast(this)->getExpr()->isUnusedResultAWarning( + WarnE, Loc, R1, R2, Ctx, Always)); case CXXNewExprClass: // FIXME: In theory, there might be new expressions that don't have side @@ -2945,13 +2958,14 @@ case MaterializeTemporaryExprClass: return cast(this) ->getSubExpr() - ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, Always); case CXXBindTemporaryExprClass: - return cast(this)->getSubExpr() - ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return cast(this) + ->getSubExpr() + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, Always); case ExprWithCleanupsClass: - return cast(this)->getSubExpr() - ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return cast(this)->getSubExpr()->isUnusedResultAWarning( + WarnE, Loc, R1, R2, Ctx, Always); } } diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -200,8 +200,13 @@ static bool DiagnoseNoDiscard(Sema &S, const WarnUnusedResultAttr *A, SourceLocation Loc, SourceRange R1, SourceRange R2, bool IsCtor) { - if (!A) + if (!A && S.Diags.isIgnored(diag::warn_unused_result_always, Loc)) return false; + + if (!A) { + return S.Diag(Loc, diag::warn_unused_result_always) << R1 << R2; + } + StringRef Msg = A->getMessage(); if (Msg.empty()) { @@ -242,7 +247,10 @@ const Expr *WarnExpr; SourceLocation Loc; SourceRange R1, R2; - if (!E->isUnusedResultAWarning(WarnExpr, Loc, R1, R2, Context)) + bool WarnUnusedResultAlways = + !Diags.isIgnored(diag::warn_unused_result_always, ExprLoc); + if (!E->isUnusedResultAWarning(WarnExpr, Loc, R1, R2, Context, + WarnUnusedResultAlways)) return; // If this is a GNU statement expression expanded from a macro, it is probably diff --git a/clang/test/Sema/unused-result-always.c b/clang/test/Sema/unused-result-always.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/unused-result-always.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -Wno-unreachable-code -Wno-strict-prototypes -Wunused-result-always + +int t1(int X, int Y); +void t2(); +void *t3(); + +#define M1(a, b) t1(a, b) + +void bar() { + t1(1, 2); // expected-warning {{ignoring return value of function}} + + t2(); // no warning. + + (void)t1(1, 2); // no warning; + + t3(); // expected-warning {{ignoring return value of function}} + + M1(1, 2); // expected-warning {{ignoring return value of function}} +}