Author: halbi2
Date: 2025-08-20T14:14:35Z
New Revision: 2f237670b1f1ed792a3b1b6a12f8e9af08685983

URL: 
https://github.com/llvm/llvm-project/commit/2f237670b1f1ed792a3b1b6a12f8e9af08685983
DIFF: 
https://github.com/llvm/llvm-project/commit/2f237670b1f1ed792a3b1b6a12f8e9af08685983.diff

LOG: [Clang] [Sema] Enable nodiscard warnings for function pointers (#154250)

A call through a function pointer has no associated FunctionDecl, but it
still might have a nodiscard return type. Ensure there is a codepath to
emit the nodiscard warning in this case.

Fixes #142453

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/AST/Expr.cpp
    clang/test/Sema/c2x-nodiscard.c
    clang/test/SemaCXX/warn-unused-result.cpp
    clang/test/SemaObjC/attr-nodiscard.m
    clang/test/SemaObjCXX/attr-nodiscard.mm

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e88d68fa99664..6d60b03d9f62b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -216,8 +216,8 @@ Bug Fixes to Compiler Builtins
 Bug Fixes to Attribute Support
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-- ``[[nodiscard]]`` is now respected on Objective-C and Objective-C++ methods.
-  (#GH141504)
+- ``[[nodiscard]]`` is now respected on Objective-C and Objective-C++ methods
+  (#GH141504) and on types returned from indirect calls (#GH142453).
 - Fixes some late parsed attributes, when applied to function definitions, not 
being parsed
   in function try blocks, and some situations where parsing of the function 
body
   is skipped, such as error recovery and code completion. (#GH153551)

diff  --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index e14cff552c922..340de6d4be934 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -2776,23 +2776,22 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, 
SourceLocation &Loc,
   case UserDefinedLiteralClass: {
     // 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.
-      //
-      // 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>()) {
-        WarnE = this;
-        Loc = CE->getCallee()->getBeginLoc();
-        R1 = CE->getCallee()->getSourceRange();
-
-        if (unsigned NumArgs = CE->getNumArgs())
-          R2 = SourceRange(CE->getArg(0)->getBeginLoc(),
-                           CE->getArg(NumArgs - 1)->getEndLoc());
-        return true;
-      }
+    // If the callee has attribute pure, const, or warn_unused_result, warn
+    // about it. void foo() { strlen("bar"); } should warn.
+    // Note: If new cases are added here, DiagnoseUnusedExprResult should be
+    // updated to match for QoI.
+    const Decl *FD = CE->getCalleeDecl();
+    bool PureOrConst =
+        FD && (FD->hasAttr<PureAttr>() || FD->hasAttr<ConstAttr>());
+    if (CE->hasUnusedResultAttr(Ctx) || PureOrConst) {
+      WarnE = this;
+      Loc = getBeginLoc();
+      R1 = getSourceRange();
+
+      if (unsigned NumArgs = CE->getNumArgs())
+        R2 = SourceRange(CE->getArg(0)->getBeginLoc(),
+                         CE->getArg(NumArgs - 1)->getEndLoc());
+      return true;
     }
     return false;
   }

diff  --git a/clang/test/Sema/c2x-nodiscard.c b/clang/test/Sema/c2x-nodiscard.c
index e2537bcf1d29d..852c74721693b 100644
--- a/clang/test/Sema/c2x-nodiscard.c
+++ b/clang/test/Sema/c2x-nodiscard.c
@@ -41,6 +41,10 @@ void f2(void) {
   (void)get_s3();
   (void)get_i();
   (void)get_e();
+
+  One; // expected-warning {{expression result unused}}
+  (enum E2)(0); // expected-warning {{expression result unused}}
+  (struct S4){1}; // expected-warning {{expression result unused}}
 }
 
 struct [[nodiscard]] error_info{
@@ -60,3 +64,16 @@ void GH104391() {
 #define M (unsigned int) f3()
   M; // expected-warning {{ignoring return value of function declared with 
'nodiscard' attribute}}
 }
+
+[[nodiscard]] typedef int NoDInt; // expected-warning {{'[[nodiscard]]' 
attribute ignored when applied to a typedef}}
+typedef __attribute__((warn_unused)) int WUInt; // expected-warning 
{{'warn_unused' attribute only applies to structs, unions, and classes}}
+typedef __attribute__((warn_unused_result)) int WURInt;
+NoDInt get_nodint();
+WUInt get_wuint();
+WURInt get_wurint();
+
+void f4(void) {
+  get_nodint(); // no warning because attribute is ignored
+  get_wuint();  // no warning because attribute is ignored
+  get_wurint(); // expected-warning {{ignoring return value of type 'WURInt' 
declared with 'warn_unused_result' attribute}}
+}

diff  --git a/clang/test/SemaCXX/warn-unused-result.cpp 
b/clang/test/SemaCXX/warn-unused-result.cpp
index 1f7913f1aa994..098817729efb1 100644
--- a/clang/test/SemaCXX/warn-unused-result.cpp
+++ b/clang/test/SemaCXX/warn-unused-result.cpp
@@ -407,3 +407,88 @@ void doGccThings() {
 }
 
 } // namespace BuildStringOnClangScope
+
+namespace candiscard {
+
+struct [[nodiscard]] NoDiscard {
+  [[nodiscard]] NoDiscard(int);
+  NoDiscard(const char *);
+};
+
+struct [[gnu::warn_unused]] WarnUnused {
+  [[gnu::warn_unused]] WarnUnused(int); // expected-warning 
{{'gnu::warn_unused' attribute only applies to structs, unions, and classes}}
+  WarnUnused(const char*);
+};
+
+struct [[gnu::warn_unused_result]] WarnUnusedResult {
+  [[gnu::warn_unused_result]] WarnUnusedResult(int);
+  WarnUnusedResult(const char*);
+};
+
+NoDiscard return_nodiscard();
+WarnUnused return_warnunused();
+WarnUnusedResult return_warnunusedresult();
+
+NoDiscard (*p_return_nodiscard)();
+WarnUnused (*p_return_warnunused)();
+WarnUnusedResult (*p_return_warnunusedresult)();
+
+NoDiscard (*(*pp_return_nodiscard)())();
+WarnUnused (*(*pp_return_warnunused)())();
+WarnUnusedResult (*(*pp_return_warnunusedresult)())();
+
+template <class T> T from_a_template();
+
+void test() {
+  // Unused but named variables
+  NoDiscard unused_variable1(1);         // no warning
+  NoDiscard unused_variable2("");        // no warning
+  WarnUnused unused_variable3(1);        // no warning
+  WarnUnused unused_variable4("");       // no warning
+  WarnUnusedResult unused_variable5(1);  // no warning
+  WarnUnusedResult unused_variable6(""); // no warning
+
+  // Constructor return values
+  NoDiscard(1);         // expected-warning {{ignoring temporary created by a 
constructor declared with 'nodiscard' attribute}}
+  NoDiscard("");        // expected-warning {{ignoring temporary of type 
'NoDiscard' declared with 'nodiscard' attribute}}
+  WarnUnused(1);        // expected-warning {{expression result unused}}
+  WarnUnused("");       // expected-warning {{expression result unused}}
+  WarnUnusedResult(1);  // expected-warning {{ignoring temporary created by a 
constructor declared with 'gnu::warn_unused_result' attribute}}
+  WarnUnusedResult(""); // expected-warning {{ignoring temporary of type 
'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
+
+  NoDiscard{1};         // expected-warning {{ignoring temporary created by a 
constructor declared with 'nodiscard' attribute}}
+  NoDiscard{""};        // expected-warning {{ignoring temporary of type 
'NoDiscard' declared with 'nodiscard' attribute}}
+  WarnUnused{1};        // expected-warning {{expression result unused}}
+  WarnUnused{""};       // expected-warning {{expression result unused}}
+  WarnUnusedResult{1};  // expected-warning {{ignoring temporary created by a 
constructor declared with 'gnu::warn_unused_result' attribute}}
+  WarnUnusedResult{""}; // expected-warning {{ignoring temporary of type 
'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
+
+  static_cast<NoDiscard>(1);         // expected-warning {{ignoring temporary 
created by a constructor declared with 'nodiscard' attribute}}
+  static_cast<NoDiscard>("");        // expected-warning {{ignoring temporary 
of type 'NoDiscard' declared with 'nodiscard' attribute}}
+  static_cast<WarnUnused>(1);        // expected-warning {{expression result 
unused}}
+  static_cast<WarnUnused>("");       // expected-warning {{expression result 
unused}}
+  static_cast<WarnUnusedResult>(1);  // expected-warning {{ignoring temporary 
created by a constructor declared with 'gnu::warn_unused_result' attribute}}
+  static_cast<WarnUnusedResult>(""); // expected-warning {{ignoring temporary 
of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
+
+  // Function return values
+  return_nodiscard(); // expected-warning {{ignoring return value of type 
'NoDiscard' declared with 'nodiscard' attribute}}
+  return_warnunused(); // no warning
+  return_warnunusedresult(); // expected-warning {{ignoring return value of 
type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
+
+  // Function pointer return values
+  p_return_nodiscard(); // expected-warning {{ignoring return value of type 
'NoDiscard' declared with 'nodiscard' attribute}}
+  p_return_warnunused(); // no warning
+  p_return_warnunusedresult(); // expected-warning {{ignoring return value of 
type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
+
+  // Function pointer expression return values
+  pp_return_nodiscard()(); // expected-warning {{ignoring return value of type 
'NoDiscard' declared with 'nodiscard' attribute}}
+  pp_return_warnunused()(); // no warning
+  pp_return_warnunusedresult()(); // expected-warning {{ignoring return value 
of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
+
+  // From a template
+  from_a_template<NoDiscard>(); // expected-warning {{ignoring return value of 
type 'NoDiscard' declared with 'nodiscard' attribute}}
+  from_a_template<WarnUnused>(); // no warning
+  from_a_template<WarnUnusedResult>(); // expected-warning {{ignoring return 
value of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' 
attribute}}
+}
+
+} // namespace candiscard

diff  --git a/clang/test/SemaObjC/attr-nodiscard.m 
b/clang/test/SemaObjC/attr-nodiscard.m
index 6d04665da25ca..26bbd247d4a3d 100644
--- a/clang/test/SemaObjC/attr-nodiscard.m
+++ b/clang/test/SemaObjC/attr-nodiscard.m
@@ -4,6 +4,9 @@
 
 typedef struct expected E;
 
+[[nodiscard]] typedef int NI; // expected-warning {{'[[nodiscard]]' attribute 
ignored when applied to a typedef}}
+typedef __attribute__((warn_unused_result)) int WUR;
+
 @interface INTF
 - (int) a [[nodiscard]];
 + (int) b [[nodiscard]];
@@ -12,6 +15,8 @@ + (struct expected) d;
 - (E) e;
 + (E) f;
 - (void) g [[nodiscard]]; // expected-warning {{attribute 'nodiscard' cannot 
be applied to Objective-C method without return value}}
+- (NI) h;
+- (WUR) i;
 @end
 
 void foo(INTF *a) {
@@ -21,5 +26,7 @@ void foo(INTF *a) {
   [INTF d]; // expected-warning {{ignoring return value of type 'expected' 
declared with 'nodiscard' attribute}}
   [a e]; // expected-warning {{ignoring return value of type 'expected' 
declared with 'nodiscard' attribute}}
   [INTF f]; // expected-warning {{ignoring return value of type 'expected' 
declared with 'nodiscard' attribute}}
-  [a g];
+  [a g]; // no warning because g returns void
+  [a h]; // no warning because attribute is ignored when applied to a typedef
+  [a i]; // expected-warning {{ignoring return value of type 'WUR' declared 
with 'warn_unused_result' attribute}}
 }

diff  --git a/clang/test/SemaObjCXX/attr-nodiscard.mm 
b/clang/test/SemaObjCXX/attr-nodiscard.mm
index e1eefb74d3961..18d829632e428 100644
--- a/clang/test/SemaObjCXX/attr-nodiscard.mm
+++ b/clang/test/SemaObjCXX/attr-nodiscard.mm
@@ -5,6 +5,9 @@
 
 using E = expected<int>;
 
+using NI [[nodiscard]] = int; // expected-warning {{'[[nodiscard]]' attribute 
ignored when applied to a typedef}}
+using WURI [[clang::warn_unused_result]] = int;
+
 @interface INTF
 - (int) a [[nodiscard]];
 + (int) b [[nodiscard]];
@@ -13,6 +16,8 @@ + (int) b [[nodiscard]];
 - (E) e;
 + (E) f;
 - (void) g [[nodiscard]]; // expected-warning {{attribute 'nodiscard' cannot 
be applied to Objective-C method without return value}}
+- (NI) h;
+- (WURI) i;
 @end
 
 void foo(INTF *a) {
@@ -22,5 +27,7 @@ void foo(INTF *a) {
   [INTF d]; // expected-warning {{ignoring return value of type 
'expected<int>' declared with 'nodiscard' attribute}}
   [a e]; // expected-warning {{ignoring return value of type 'expected<int>' 
declared with 'nodiscard' attribute}}
   [INTF f]; // expected-warning {{ignoring return value of type 
'expected<int>' declared with 'nodiscard' attribute}}
-  [a g];
+  [a g]; // no warning because g returns void
+  [a h]; // no warning because attribute is ignored
+  [a i]; // expected-warning {{ignoring return value of type 'WURI' declared 
with 'clang::warn_unused_result' attribute}}
 }


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to