gpanders created this revision.
gpanders added a reviewer: theraven.
Herald added a project: All.
gpanders requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

This flag enables warnings for unused function return values
universally, even for functions which do not have a "warn_unused_result"
attribute.

This is disabled by default as it is far too noisy for most C/C++ code;
however, there are contexts where this is a desirable warning to have
(for example, in safety critical code where ignoring return values is
usually forbidden).


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D149380

Files:
  clang/include/clang/AST/Expr.h
  clang/include/clang/Basic/DiagnosticGroups.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/AST/Expr.cpp
  clang/lib/Sema/SemaStmt.cpp
  clang/test/Sema/unused-result-always.c

Index: clang/test/Sema/unused-result-always.c
===================================================================
--- /dev/null
+++ 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}}
+}
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ 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
Index: clang/lib/AST/Expr.cpp
===================================================================
--- clang/lib/AST/Expr.cpp
+++ 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<ParenExpr>(this)->getSubExpr()->
-      isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
+    return cast<ParenExpr>(this)->getSubExpr()->isUnusedResultAWarning(
+        WarnE, Loc, R1, R2, Ctx, Always);
   case GenericSelectionExprClass:
-    return cast<GenericSelectionExpr>(this)->getResultExpr()->
-      isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
+    return cast<GenericSelectionExpr>(this)
+        ->getResultExpr()
+        ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, Always);
   case CoawaitExprClass:
   case CoyieldExprClass:
-    return cast<CoroutineSuspendExpr>(this)->getResumeExpr()->
-      isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
+    return cast<CoroutineSuspendExpr>(this)
+        ->getResumeExpr()
+        ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, Always);
   case ChooseExprClass:
-    return cast<ChooseExpr>(this)->getChosenSubExpr()->
-      isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
+    return cast<ChooseExpr>(this)->getChosenSubExpr()->isUnusedResultAWarning(
+        WarnE, Loc, R1, R2, Ctx, Always);
   case UnaryOperatorClass: {
     const UnaryOperator *UO = cast<UnaryOperator>(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<IntegerLiteral>(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<ConditionalOperator>(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<BinaryConditionalOperator>(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<CallExpr>(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<PureAttr>() || FD->hasAttr<ConstAttr>()) {
+      if (Always || CE->hasUnusedResultAttr(Ctx) || FD->hasAttr<PureAttr>() ||
+          FD->hasAttr<ConstAttr>()) {
         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<StmtExpr>(this)->getSubStmt();
     if (!CS->body_empty()) {
       if (const Expr *E = dyn_cast<Expr>(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<LabelStmt>(CS->body_back()))
         if (const Expr *E = dyn_cast<Expr>(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<CXXDefaultArgExpr>(this)
-            ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx));
+    return (cast<CXXDefaultArgExpr>(this)->getExpr()->isUnusedResultAWarning(
+        WarnE, Loc, R1, R2, Ctx, Always));
   case CXXDefaultInitExprClass:
-    return (cast<CXXDefaultInitExpr>(this)
-            ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx));
+    return (cast<CXXDefaultInitExpr>(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<MaterializeTemporaryExpr>(this)
         ->getSubExpr()
-        ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
+        ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, Always);
   case CXXBindTemporaryExprClass:
-    return cast<CXXBindTemporaryExpr>(this)->getSubExpr()
-               ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
+    return cast<CXXBindTemporaryExpr>(this)
+        ->getSubExpr()
+        ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, Always);
   case ExprWithCleanupsClass:
-    return cast<ExprWithCleanups>(this)->getSubExpr()
-               ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
+    return cast<ExprWithCleanups>(this)->getSubExpr()->isUnusedResultAWarning(
+        WarnE, Loc, R1, R2, Ctx, Always);
   }
 }
 
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ 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<UnusedResult>;
+def warn_unused_result_always : Warning<
+  "ignoring return value of function">,
+  InGroup<UnusedResultAlways>, 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))' "
Index: clang/include/clang/Basic/DiagnosticGroups.td
===================================================================
--- clang/include/clang/Basic/DiagnosticGroups.td
+++ 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]>;
Index: clang/include/clang/AST/Expr.h
===================================================================
--- clang/include/clang/AST/Expr.h
+++ 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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to