[clang] 51ad290 - [Clang] Improve diagnostic on `[[nodiscard]]` attribute (#112521)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Nov 19 02:04:58 PST 2024
Author: Yihe Li
Date: 2024-11-19T11:04:53+01:00
New Revision: 51ad2901ca54a01a72db71622b116eb27ea6dcf2
URL: https://github.com/llvm/llvm-project/commit/51ad2901ca54a01a72db71622b116eb27ea6dcf2
DIFF: https://github.com/llvm/llvm-project/commit/51ad2901ca54a01a72db71622b116eb27ea6dcf2.diff
LOG: [Clang] Improve diagnostic on `[[nodiscard]]` attribute (#112521)
A follow-up to #112289.
When diagnosing an unused return value, if the diagnostic
is triggered by an attribute attached to a type, the type name
is now included in the diagnostic.
---------
Co-authored-by: Sirraide <aeternalmail at gmail.com>
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/Expr.h
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/AST/Expr.cpp
clang/lib/Sema/SemaStmt.cpp
clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp
clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p3.cpp
clang/test/Sema/c2x-nodiscard.c
clang/test/SemaCXX/warn-unused-result.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4d6275fbc9810c..4514f9f591ffa4 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -539,6 +539,22 @@ Improvements to Clang's diagnostics
- Clang now omits shadow warnings for enum constants in separate class scopes (#GH62588).
+- When diagnosing an unused return value of a type declared ``[[nodiscard]]``, the type
+ itself is now included in the diagnostic.
+
+- Clang will now prefer the ``[[nodiscard]]`` declaration on function declarations over ``[[nodiscard]]``
+ declaration on the return type of a function. Previously, when both have a ``[[nodiscard]]`` declaration attached,
+ the one on the return type would be preferred. This may affect the generated warning message:
+
+ .. code-block:: c++
+
+ struct [[nodiscard("Reason 1")]] S {};
+ [[nodiscard("Reason 2")]] S getS();
+ void use()
+ {
+ getS(); // Now diagnoses "Reason 2", previously diagnoses "Reason 1"
+ }
+
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 466c65a9685ad3..708c8656decbe0 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -3181,12 +3181,14 @@ class CallExpr : public Expr {
QualType getCallReturnType(const ASTContext &Ctx) const;
/// Returns the WarnUnusedResultAttr that is either declared on the called
- /// function, or its return type declaration.
- const Attr *getUnusedResultAttr(const ASTContext &Ctx) const;
+ /// function, or its return type declaration, together with a NamedDecl that
+ /// refers to the declaration the attribute is attached onto.
+ std::pair<const NamedDecl *, const Attr *>
+ getUnusedResultAttr(const ASTContext &Ctx) const;
/// Returns true if this call expression should warn on unused results.
bool hasUnusedResultAttr(const ASTContext &Ctx) const {
- return getUnusedResultAttr(Ctx) != nullptr;
+ return getUnusedResultAttr(Ctx).second != nullptr;
}
SourceLocation getRParenLoc() const { return RParenLoc; }
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index ad2a932ca463ec..dfb90501ce72d7 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9300,11 +9300,11 @@ def warn_unused_container_subscript_expr : Warning<
def warn_unused_call : Warning<
"ignoring return value of function declared with %0 attribute">,
InGroup<UnusedValue>;
-def warn_unused_constructor : Warning<
- "ignoring temporary created by a constructor declared with %0 attribute">,
+def warn_unused_return_type : Warning<
+ "ignoring %select{return value|temporary}0 of type %2 declared with %1 attribute%select{|: %4}3">,
InGroup<UnusedValue>;
-def warn_unused_constructor_msg : Warning<
- "ignoring temporary created by a constructor declared with %0 attribute: %1">,
+def warn_unused_constructor : Warning<
+ "ignoring temporary created by a constructor declared with %0 attribute%select{|: %2}1">,
InGroup<UnusedValue>;
def warn_side_effects_unevaluated_context : Warning<
"expression with side effects has no effect in an unevaluated context">,
@@ -9313,10 +9313,7 @@ def warn_side_effects_typeid : Warning<
"expression with side effects will be evaluated despite being used as an "
"operand to 'typeid'">, InGroup<PotentiallyEvaluatedExpression>;
def warn_unused_result : Warning<
- "ignoring return value of function declared with %0 attribute">,
- InGroup<UnusedResult>;
-def warn_unused_result_msg : Warning<
- "ignoring return value of function declared with %0 attribute: %1">,
+ "ignoring return value of function declared with %0 attribute%select{|: %2}1">,
InGroup<UnusedResult>;
def warn_unused_result_typedef_unsupported_spelling : Warning<
"'[[%select{nodiscard|gnu::warn_unused_result}0]]' attribute ignored when "
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 8aab8d2d2ce93d..a4fb4d5a1f2ec4 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -1615,22 +1615,24 @@ QualType CallExpr::getCallReturnType(const ASTContext &Ctx) const {
return FnType->getReturnType();
}
-const Attr *CallExpr::getUnusedResultAttr(const ASTContext &Ctx) const {
+std::pair<const NamedDecl *, const Attr *>
+CallExpr::getUnusedResultAttr(const ASTContext &Ctx) const {
+ // If the callee is marked nodiscard, return that attribute
+ const Decl *D = getCalleeDecl();
+ if (const auto *A = D->getAttr<WarnUnusedResultAttr>())
+ return {nullptr, A};
+
// If the return type is a struct, union, or enum that is marked nodiscard,
// then return the return type attribute.
if (const TagDecl *TD = getCallReturnType(Ctx)->getAsTagDecl())
if (const auto *A = TD->getAttr<WarnUnusedResultAttr>())
- return A;
+ return {TD, A};
for (const auto *TD = getCallReturnType(Ctx)->getAs<TypedefType>(); TD;
TD = TD->desugar()->getAs<TypedefType>())
if (const auto *A = TD->getDecl()->getAttr<WarnUnusedResultAttr>())
- return A;
-
- // Otherwise, see if the callee is marked nodiscard and return that attribute
- // instead.
- const Decl *D = getCalleeDecl();
- return D ? D->getAttr<WarnUnusedResultAttr>() : nullptr;
+ return {TD->getDecl(), A};
+ return {nullptr, nullptr};
}
SourceLocation CallExpr::getBeginLoc() const {
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index f3ee5211acdd11..d6bc66246c758f 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -200,23 +200,30 @@ static bool DiagnoseUnusedComparison(Sema &S, const Expr *E) {
return true;
}
-static bool DiagnoseNoDiscard(Sema &S, const WarnUnusedResultAttr *A,
- SourceLocation Loc, SourceRange R1,
- SourceRange R2, bool IsCtor) {
+static bool DiagnoseNoDiscard(Sema &S, const NamedDecl *OffendingDecl,
+ const WarnUnusedResultAttr *A, SourceLocation Loc,
+ SourceRange R1, SourceRange R2, bool IsCtor) {
if (!A)
return false;
StringRef Msg = A->getMessage();
if (Msg.empty()) {
+ if (OffendingDecl)
+ return S.Diag(Loc, diag::warn_unused_return_type)
+ << IsCtor << A << OffendingDecl << false << R1 << R2;
if (IsCtor)
- return S.Diag(Loc, diag::warn_unused_constructor) << A << R1 << R2;
- return S.Diag(Loc, diag::warn_unused_result) << A << R1 << R2;
+ return S.Diag(Loc, diag::warn_unused_constructor)
+ << A << false << R1 << R2;
+ return S.Diag(Loc, diag::warn_unused_result) << A << false << R1 << R2;
}
+ if (OffendingDecl)
+ return S.Diag(Loc, diag::warn_unused_return_type)
+ << IsCtor << A << OffendingDecl << true << Msg << R1 << R2;
if (IsCtor)
- return S.Diag(Loc, diag::warn_unused_constructor_msg) << A << Msg << R1
- << R2;
- return S.Diag(Loc, diag::warn_unused_result_msg) << A << Msg << R1 << R2;
+ return S.Diag(Loc, diag::warn_unused_constructor)
+ << A << true << Msg << R1 << R2;
+ return S.Diag(Loc, diag::warn_unused_result) << A << true << Msg << R1 << R2;
}
void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
@@ -286,9 +293,10 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
if (E->getType()->isVoidType())
return;
- if (DiagnoseNoDiscard(*this, cast_or_null<WarnUnusedResultAttr>(
- CE->getUnusedResultAttr(Context)),
- Loc, R1, R2, /*isCtor=*/false))
+ auto [OffendingDecl, A] = CE->getUnusedResultAttr(Context);
+ if (DiagnoseNoDiscard(*this, OffendingDecl,
+ cast_or_null<WarnUnusedResultAttr>(A), Loc, R1, R2,
+ /*isCtor=*/false))
return;
// If the callee has attribute pure, const, or warn_unused_result, warn with
@@ -309,16 +317,21 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
}
} else if (const auto *CE = dyn_cast<CXXConstructExpr>(E)) {
if (const CXXConstructorDecl *Ctor = CE->getConstructor()) {
+ const NamedDecl *OffendingDecl = nullptr;
const auto *A = Ctor->getAttr<WarnUnusedResultAttr>();
- A = A ? A : Ctor->getParent()->getAttr<WarnUnusedResultAttr>();
- if (DiagnoseNoDiscard(*this, A, Loc, R1, R2, /*isCtor=*/true))
+ if (!A) {
+ OffendingDecl = Ctor->getParent();
+ A = OffendingDecl->getAttr<WarnUnusedResultAttr>();
+ }
+ if (DiagnoseNoDiscard(*this, OffendingDecl, A, Loc, R1, R2,
+ /*isCtor=*/true))
return;
}
} else if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
if (const TagDecl *TD = ILE->getType()->getAsTagDecl()) {
- if (DiagnoseNoDiscard(*this, TD->getAttr<WarnUnusedResultAttr>(), Loc, R1,
- R2, /*isCtor=*/false))
+ if (DiagnoseNoDiscard(*this, TD, TD->getAttr<WarnUnusedResultAttr>(), Loc,
+ R1, R2, /*isCtor=*/false))
return;
}
} else if (ShouldSuppress)
@@ -332,8 +345,8 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
}
const ObjCMethodDecl *MD = ME->getMethodDecl();
if (MD) {
- if (DiagnoseNoDiscard(*this, MD->getAttr<WarnUnusedResultAttr>(), Loc, R1,
- R2, /*isCtor=*/false))
+ if (DiagnoseNoDiscard(*this, nullptr, MD->getAttr<WarnUnusedResultAttr>(),
+ Loc, R1, R2, /*isCtor=*/false))
return;
}
} else if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) {
diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp
index 693ca29370cf3f..da1f8201f55dcc 100644
--- a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp
@@ -17,10 +17,10 @@ E get_e();
// cxx11-warning at -1 {{use of the 'nodiscard' attribute is a C++17 extension}}
void f() {
- get_s(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ get_s(); // expected-warning {{ignoring return value of type 'S' declared with 'nodiscard' attribute}}
get_i(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
get_vi(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
- get_e(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ get_e(); // expected-warning {{ignoring return value of type 'E' declared with 'nodiscard' attribute}}
// Okay, warnings are not encouraged
get_s_ref();
@@ -54,10 +54,10 @@ void f() {
fp3 three;
fp2_alias four;
- one(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
- two(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
- three(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
- four(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ one(); // expected-warning {{ignoring return value of type 'E' declared with 'nodiscard' attribute}}
+ two(); // expected-warning {{ignoring return value of type 'S' declared with 'nodiscard' attribute}}
+ three(); // expected-warning {{ignoring return value of type 'S' declared with 'nodiscard' attribute}}
+ four(); // expected-warning {{ignoring return value of type 'S' declared with 'nodiscard' attribute}}
// These are all okay because of the explicit cast to void.
(void)one();
@@ -84,8 +84,8 @@ LaterReason get_later_reason();
// cxx11-17-warning at -1 {{use of the 'nodiscard' attribute is a C++20 extension}}
void cxx20_use() {
- get_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: reason}}
- get_later_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: later reason}}
+ get_reason(); // expected-warning {{ignoring return value of type 'ReasonStruct' declared with 'nodiscard' attribute: reason}}
+ get_later_reason(); // expected-warning {{ignoring return value of type 'LaterReason' declared with 'nodiscard' attribute: later reason}}
another_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: another reason}}
conflicting_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: special reason}}
}
@@ -115,20 +115,20 @@ void usage() {
S('A'); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't let that S-Char go!}}
S(1);
S(2.2);
- Y(); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't throw me away either!}}
+ Y(); // expected-warning {{ignoring temporary of type 'Y' declared with 'nodiscard' attribute: Don't throw me away either!}}
S s;
- ConvertTo{}; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw me away!}}
+ ConvertTo{}; // expected-warning {{ignoring return value of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}}
// AST is
diff erent in C++17 mode. Before, a move ctor for ConvertTo is there
// as well, hence the constructor warning.
- // since-cxx17-warning at +2 {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw me away!}}
- // cxx11-warning at +1 {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't throw me away!}}
+ // since-cxx17-warning at +2 {{ignoring return value of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}}
+ // cxx11-warning at +1 {{ignoring temporary of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}}
(ConvertTo) s;
(int)s; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
(S)'c'; // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't let that S-Char go!}}
- // since-cxx17-warning at +2 {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw me away!}}
- // cxx11-warning at +1 {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't throw me away!}}
+ // since-cxx17-warning at +2 {{ignoring return value of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}}
+ // cxx11-warning at +1 {{ignoring temporary of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}}
static_cast<ConvertTo>(s);
static_cast<int>(s); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
static_cast<double>(s); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw away as a double}}
diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p3.cpp
index a3543cff7d2c92..b37517921b1ca4 100644
--- a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p3.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p3.cpp
@@ -8,7 +8,7 @@ namespace std_example {
error_info enable_missile_safety_mode();
void launch_missiles();
void test_missiles() {
- enable_missile_safety_mode(); // expected-warning {{ignoring return value of function declared with 'nodiscard'}}
+ enable_missile_safety_mode(); // expected-warning {{ignoring return value of type 'error_info' declared with 'nodiscard'}}
launch_missiles();
}
diff --git a/clang/test/Sema/c2x-nodiscard.c b/clang/test/Sema/c2x-nodiscard.c
index f8b0567366465d..e2537bcf1d29d6 100644
--- a/clang/test/Sema/c2x-nodiscard.c
+++ b/clang/test/Sema/c2x-nodiscard.c
@@ -31,10 +31,10 @@ enum E2 get_e(void);
[[nodiscard]] int get_i(void);
void f2(void) {
- get_s(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
- get_s3(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: Wrong}}
+ get_s(); // expected-warning {{ignoring return value of type 'S4' declared with 'nodiscard' attribute}}
+ get_s3(); // expected-warning {{ignoring return value of type 'S3' declared with 'nodiscard' attribute: Wrong}}
get_i(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
- get_e(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ get_e(); // expected-warning {{ignoring return value of type 'E2' declared with 'nodiscard' attribute}}
// Okay, warnings are not encouraged
(void)get_s();
@@ -50,7 +50,7 @@ struct [[nodiscard]] error_info{
struct error_info enable_missile_safety_mode(void);
void launch_missiles(void);
void test_missiles(void) {
- enable_missile_safety_mode(); // expected-warning {{ignoring return value of function declared with 'nodiscard'}}
+ enable_missile_safety_mode(); // expected-warning {{ignoring return value of type 'error_info' declared with 'nodiscard'}}
launch_missiles();
}
diff --git a/clang/test/SemaCXX/warn-unused-result.cpp b/clang/test/SemaCXX/warn-unused-result.cpp
index 4b7a2503ecc0dd..682c500dc1d96d 100644
--- a/clang/test/SemaCXX/warn-unused-result.cpp
+++ b/clang/test/SemaCXX/warn-unused-result.cpp
@@ -108,7 +108,7 @@ void lazy() {
(void)DoAnotherThing();
(void)DoYetAnotherThing();
- DoSomething(); // expected-warning {{ignoring return value}}
+ DoSomething(); // expected-warning {{ignoring return value of type 'Status' declared with 'warn_unused_result'}}
DoSomethingElse();
DoAnotherThing();
DoYetAnotherThing();
@@ -120,11 +120,11 @@ class [[clang::warn_unused_result]] StatusOr {
StatusOr<int> doit();
void test() {
Foo f;
- f.doStuff(); // expected-warning {{ignoring return value}}
- doit(); // expected-warning {{ignoring return value}}
+ f.doStuff(); // expected-warning {{ignoring return value of type 'Status' declared with 'warn_unused_result'}}
+ doit(); // expected-warning {{ignoring return value of type 'StatusOr<int>' declared with 'warn_unused_result'}}
auto func = []() { return Status(); };
- func(); // expected-warning {{ignoring return value}}
+ func(); // expected-warning {{ignoring return value of type 'Status' declared with 'warn_unused_result'}}
}
}
@@ -139,7 +139,7 @@ struct Status {};
void Bar() {
Foo f;
- f.Bar(); // expected-warning {{ignoring return value}}
+ f.Bar(); // expected-warning {{ignoring return value of type 'Status' declared with 'warn_unused_result'}}
};
}
@@ -215,18 +215,18 @@ P operator--(const P &) { return {}; };
void f() {
S s;
P p;
- s.DoThing(); // expected-warning {{ignoring return value}}
- p.DoThing(); // expected-warning {{ignoring return value}}
+ s.DoThing(); // expected-warning {{ignoring return value of type 'S' declared with 'warn_unused_result'}}
+ p.DoThing(); // expected-warning {{ignoring return value of type 'P' declared with 'warn_unused_result'}}
// Only postfix is expected to warn when written correctly.
- s++; // expected-warning {{ignoring return value}}
- s--; // expected-warning {{ignoring return value}}
- p++; // expected-warning {{ignoring return value}}
- p--; // expected-warning {{ignoring return value}}
+ s++; // expected-warning {{ignoring return value of type 'S' declared with 'warn_unused_result'}}
+ s--; // expected-warning {{ignoring return value of type 'S' declared with 'warn_unused_result'}}
+ p++; // expected-warning {{ignoring return value of type 'P' declared with 'warn_unused_result'}}
+ p--; // expected-warning {{ignoring return value of type 'P' declared with 'warn_unused_result'}}
// Improperly written prefix operators should still warn.
- ++s; // expected-warning {{ignoring return value}}
- --s; // expected-warning {{ignoring return value}}
- ++p; // expected-warning {{ignoring return value}}
- --p; // expected-warning {{ignoring return value}}
+ ++s; // expected-warning {{ignoring return value of type 'S' declared with 'warn_unused_result'}}
+ --s; // expected-warning {{ignoring return value of type 'S' declared with 'warn_unused_result'}}
+ ++p; // expected-warning {{ignoring return value of type 'P' declared with 'warn_unused_result'}}
+ --p; // expected-warning {{ignoring return value of type 'P' declared with 'warn_unused_result'}}
// Silencing the warning by cast to void still works.
(void)s.DoThing();
@@ -243,7 +243,7 @@ namespace PR39837 {
void g() {
int a[2];
for (int b : a)
- f(b); // expected-warning {{ignoring return value}}
+ f(b); // expected-warning {{ignoring return value of function declared with 'warn_unused_result'}}
}
} // namespace PR39837
@@ -261,12 +261,12 @@ typedef a indirect;
a af1();
indirect indirectf1();
void af2() {
- af1(); // expected-warning {{ignoring return value}}
+ af1(); // expected-warning {{ignoring return value of type 'a' declared with 'warn_unused_result'}}
void *(*a1)();
a1(); // no warning
a (*a2)();
- a2(); // expected-warning {{ignoring return value}}
- indirectf1(); // expected-warning {{ignoring return value}}
+ a2(); // expected-warning {{ignoring return value of type 'a' declared with 'warn_unused_result'}}
+ indirectf1(); // expected-warning {{ignoring return value of type 'a' declared with 'warn_unused_result'}}
}
[[nodiscard]] typedef void *b1; // expected-warning {{'[[nodiscard]]' attribute ignored when applied to a typedef; consider using '__attribute__((warn_unused_result))' or '[[clang::warn_unused_result]]' instead}}
[[gnu::warn_unused_result]] typedef void *b2; // expected-warning {{'[[gnu::warn_unused_result]]' attribute ignored when applied to a typedef; consider using '__attribute__((warn_unused_result))' or '[[clang::warn_unused_result]]' instead}}
@@ -279,10 +279,79 @@ void bf2() {
__attribute__((warn_unused_result)) typedef void *c;
c cf1();
void cf2() {
- cf1(); // expected-warning {{ignoring return value}}
+ cf1(); // expected-warning {{ignoring return value of type 'c' declared with 'warn_unused_result'}}
void *(*c1)();
c1();
c (*c2)();
- c2(); // expected-warning {{ignoring return value}}
+ c2(); // expected-warning {{ignoring return value of type 'c' declared with 'warn_unused_result'}}
}
}
+
+namespace nodiscard_specialization {
+// Test to only mark a specialization of class template as nodiscard
+template<typename T> struct S { S(int) {} };
+template<> struct [[nodiscard]] S<int> { S(int) {} };
+template<typename T> struct [[clang::warn_unused_result]] S<const T> { S(int) {} };
+
+template<typename T>
+S<T> obtain(const T&) { return {2}; }
+
+template<typename T>
+[[nodiscard]] S<T> obtain2(const T&) { return {2}; }
+
+template<typename T>
+__attribute__((warn_unused_result)) S<T> obtain3(const T&) { return {2}; }
+
+void use() {
+ obtain(1.0); // no warning
+ obtain(1); // expected-warning {{ignoring return value of type 'S<int>' declared with 'nodiscard'}}
+ obtain<const double>(1); // expected-warning {{ignoring return value of type 'S<const double>' declared with 'warn_unused_result'}}
+
+ S<double>(2); // no warning
+ S<int>(2); // expected-warning {{ignoring temporary of type 'S<int>' declared with 'nodiscard'}}
+ S<const char>(2); // no warning (warn_unused_result does not diagnose constructor temporaries)
+
+ // function should take precedence over type
+ obtain2(1.0); // expected-warning {{ignoring return value of function declared with 'nodiscard'}}
+ obtain2(1); // expected-warning {{ignoring return value of function declared with 'nodiscard'}}
+ obtain2<const double>(1); // expected-warning {{ignoring return value of function declared with 'nodiscard'}}
+ obtain3(1.0); // expected-warning {{ignoring return value of function declared with 'warn_unused_result'}}
+ obtain3(1); // expected-warning {{ignoring return value of function declared with 'warn_unused_result'}}
+ obtain3<const double>(1); // expected-warning {{ignoring return value of function declared with 'warn_unused_result'}}
+}
+
+// Test on constructor nodiscard
+struct H {
+ explicit H(int) {}
+ [[nodiscard]] explicit H(double) {}
+ __attribute__((warn_unused_result)) H(const char*) {}
+};
+
+struct [[nodiscard]] G {
+ explicit G(int) {}
+ [[nodiscard]] explicit G(double) {}
+ [[clang::warn_unused_result]] G(const char*) {}
+};
+
+void use2() {
+ H{2}; // no warning
+ H(2.0); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard'}}
+ H("Hello"); // no warning (warn_unused_result does not diagnose constructor temporaries)
+
+ // no warning for explicit cast to void
+ (void)H(2);
+ (void)H{2.0};
+ (void)H{"Hello"};
+
+ // warns for all these invocations
+ // here, constructor/function should take precedence over type
+ G{2}; // expected-warning {{ignoring temporary of type 'G' declared with 'nodiscard'}}
+ G(2.0); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard'}}
+ G("Hello"); // expected-warning {{ignoring temporary created by a constructor declared with 'warn_unused_result'}}
+
+ // no warning for explicit cast to void
+ (void)G(2);
+ (void)G{2.0};
+ (void)G{"Hello"};
+}
+} // namespace nodiscard_specialization
More information about the cfe-commits
mailing list