Skip to content

Commit 59f7dbd

Browse files
committed
[clang] Improve diagnostic on [[nodiscard]] attribute
1 parent 36d936a commit 59f7dbd

File tree

9 files changed

+159
-65
lines changed

9 files changed

+159
-65
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,9 @@ Improvements to Clang's diagnostics
416416
name was a reserved name, which we improperly allowed to suppress the
417417
diagnostic.
418418

419+
- Clang now includes the return type of the function or constructor in the warning generated
420+
when `[[nodiscard]]` is triggered by its placement on return types instead of function itself.
421+
419422
Improvements to Clang's time-trace
420423
----------------------------------
421424

clang/include/clang/AST/Expr.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3181,12 +3181,14 @@ class CallExpr : public Expr {
31813181
QualType getCallReturnType(const ASTContext &Ctx) const;
31823182

31833183
/// Returns the WarnUnusedResultAttr that is either declared on the called
3184-
/// function, or its return type declaration.
3185-
const Attr *getUnusedResultAttr(const ASTContext &Ctx) const;
3184+
/// function, or its return type declaration, together with a NamedDecl that
3185+
/// refers to the declaration the attribute is attached onto.
3186+
std::pair<const NamedDecl *, const Attr *>
3187+
getUnusedResultAttr(const ASTContext &Ctx) const;
31863188

31873189
/// Returns true if this call expression should warn on unused results.
31883190
bool hasUnusedResultAttr(const ASTContext &Ctx) const {
3189-
return getUnusedResultAttr(Ctx) != nullptr;
3191+
return getUnusedResultAttr(Ctx).second != nullptr;
31903192
}
31913193

31923194
SourceLocation getRParenLoc() const { return RParenLoc; }

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9267,6 +9267,12 @@ def warn_unused_container_subscript_expr : Warning<
92679267
def warn_unused_call : Warning<
92689268
"ignoring return value of function declared with %0 attribute">,
92699269
InGroup<UnusedValue>;
9270+
def warn_unused_return_type : Warning<
9271+
"ignoring %select{return value|temporary}0 of type %2 declared with %1 attribute">,
9272+
InGroup<UnusedValue>;
9273+
def warn_unused_return_type_msg : Warning<
9274+
"ignoring %select{return value|temporary}0 of type %2 declared with %1 attribute: %3">,
9275+
InGroup<UnusedValue>;
92709276
def warn_unused_constructor : Warning<
92719277
"ignoring temporary created by a constructor declared with %0 attribute">,
92729278
InGroup<UnusedValue>;

clang/lib/AST/Expr.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,22 +1616,24 @@ QualType CallExpr::getCallReturnType(const ASTContext &Ctx) const {
16161616
return FnType->getReturnType();
16171617
}
16181618

1619-
const Attr *CallExpr::getUnusedResultAttr(const ASTContext &Ctx) const {
1619+
std::pair<const NamedDecl *, const Attr *>
1620+
CallExpr::getUnusedResultAttr(const ASTContext &Ctx) const {
1621+
// If the callee is marked nodiscard, return that attribute
1622+
const Decl *D = getCalleeDecl();
1623+
if (const auto *A = D->getAttr<WarnUnusedResultAttr>())
1624+
return {nullptr, A};
1625+
16201626
// If the return type is a struct, union, or enum that is marked nodiscard,
16211627
// then return the return type attribute.
16221628
if (const TagDecl *TD = getCallReturnType(Ctx)->getAsTagDecl())
16231629
if (const auto *A = TD->getAttr<WarnUnusedResultAttr>())
1624-
return A;
1630+
return {TD, A};
16251631

16261632
for (const auto *TD = getCallReturnType(Ctx)->getAs<TypedefType>(); TD;
16271633
TD = TD->desugar()->getAs<TypedefType>())
16281634
if (const auto *A = TD->getDecl()->getAttr<WarnUnusedResultAttr>())
1629-
return A;
1630-
1631-
// Otherwise, see if the callee is marked nodiscard and return that attribute
1632-
// instead.
1633-
const Decl *D = getCalleeDecl();
1634-
return D ? D->getAttr<WarnUnusedResultAttr>() : nullptr;
1635+
return {TD->getDecl(), A};
1636+
return {nullptr, nullptr};
16351637
}
16361638

16371639
SourceLocation CallExpr::getBeginLoc() const {

clang/lib/Sema/SemaStmt.cpp

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -204,22 +204,28 @@ static bool DiagnoseUnusedComparison(Sema &S, const Expr *E) {
204204
return true;
205205
}
206206

207-
static bool DiagnoseNoDiscard(Sema &S, const WarnUnusedResultAttr *A,
208-
SourceLocation Loc, SourceRange R1,
209-
SourceRange R2, bool IsCtor) {
207+
static bool DiagnoseNoDiscard(Sema &S, const NamedDecl *OffendingDecl,
208+
const WarnUnusedResultAttr *A, SourceLocation Loc,
209+
SourceRange R1, SourceRange R2, bool IsCtor) {
210210
if (!A)
211211
return false;
212212
StringRef Msg = A->getMessage();
213213

214214
if (Msg.empty()) {
215+
if (OffendingDecl)
216+
return S.Diag(Loc, diag::warn_unused_return_type)
217+
<< IsCtor << A << OffendingDecl << R1 << R2;
215218
if (IsCtor)
216219
return S.Diag(Loc, diag::warn_unused_constructor) << A << R1 << R2;
217220
return S.Diag(Loc, diag::warn_unused_result) << A << R1 << R2;
218221
}
219222

223+
if (OffendingDecl)
224+
return S.Diag(Loc, diag::warn_unused_return_type_msg)
225+
<< IsCtor << A << OffendingDecl << Msg << R1 << R2;
220226
if (IsCtor)
221-
return S.Diag(Loc, diag::warn_unused_constructor_msg) << A << Msg << R1
222-
<< R2;
227+
return S.Diag(Loc, diag::warn_unused_constructor_msg)
228+
<< A << Msg << R1 << R2;
223229
return S.Diag(Loc, diag::warn_unused_result_msg) << A << Msg << R1 << R2;
224230
}
225231

@@ -290,9 +296,10 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
290296
if (E->getType()->isVoidType())
291297
return;
292298

293-
if (DiagnoseNoDiscard(*this, cast_or_null<WarnUnusedResultAttr>(
294-
CE->getUnusedResultAttr(Context)),
295-
Loc, R1, R2, /*isCtor=*/false))
299+
auto [OffendingDecl, A] = CE->getUnusedResultAttr(Context);
300+
if (DiagnoseNoDiscard(*this, OffendingDecl,
301+
cast_or_null<WarnUnusedResultAttr>(A), Loc, R1, R2,
302+
/*isCtor=*/false))
296303
return;
297304

298305
// If the callee has attribute pure, const, or warn_unused_result, warn with
@@ -313,16 +320,21 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
313320
}
314321
} else if (const auto *CE = dyn_cast<CXXConstructExpr>(E)) {
315322
if (const CXXConstructorDecl *Ctor = CE->getConstructor()) {
323+
const NamedDecl *OffendingDecl = nullptr;
316324
const auto *A = Ctor->getAttr<WarnUnusedResultAttr>();
317-
A = A ? A : Ctor->getParent()->getAttr<WarnUnusedResultAttr>();
318-
if (DiagnoseNoDiscard(*this, A, Loc, R1, R2, /*isCtor=*/true))
325+
if (!A) {
326+
OffendingDecl = Ctor->getParent();
327+
A = OffendingDecl->getAttr<WarnUnusedResultAttr>();
328+
}
329+
if (DiagnoseNoDiscard(*this, OffendingDecl, A, Loc, R1, R2,
330+
/*isCtor=*/true))
319331
return;
320332
}
321333
} else if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
322334
if (const TagDecl *TD = ILE->getType()->getAsTagDecl()) {
323335

324-
if (DiagnoseNoDiscard(*this, TD->getAttr<WarnUnusedResultAttr>(), Loc, R1,
325-
R2, /*isCtor=*/false))
336+
if (DiagnoseNoDiscard(*this, TD, TD->getAttr<WarnUnusedResultAttr>(), Loc,
337+
R1, R2, /*isCtor=*/false))
326338
return;
327339
}
328340
} else if (ShouldSuppress)
@@ -336,8 +348,8 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
336348
}
337349
const ObjCMethodDecl *MD = ME->getMethodDecl();
338350
if (MD) {
339-
if (DiagnoseNoDiscard(*this, MD->getAttr<WarnUnusedResultAttr>(), Loc, R1,
340-
R2, /*isCtor=*/false))
351+
if (DiagnoseNoDiscard(*this, nullptr, MD->getAttr<WarnUnusedResultAttr>(),
352+
Loc, R1, R2, /*isCtor=*/false))
341353
return;
342354
}
343355
} else if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) {

clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ E get_e();
1717
// cxx11-warning@-1 {{use of the 'nodiscard' attribute is a C++17 extension}}
1818

1919
void f() {
20-
get_s(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
20+
get_s(); // expected-warning {{ignoring return value of type 'S' declared with 'nodiscard' attribute}}
2121
get_i(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
2222
get_vi(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
23-
get_e(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
23+
get_e(); // expected-warning {{ignoring return value of type 'E' declared with 'nodiscard' attribute}}
2424

2525
// Okay, warnings are not encouraged
2626
get_s_ref();
@@ -54,10 +54,10 @@ void f() {
5454
fp3 three;
5555
fp2_alias four;
5656

57-
one(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
58-
two(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
59-
three(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
60-
four(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
57+
one(); // expected-warning {{ignoring return value of type 'E' declared with 'nodiscard' attribute}}
58+
two(); // expected-warning {{ignoring return value of type 'S' declared with 'nodiscard' attribute}}
59+
three(); // expected-warning {{ignoring return value of type 'S' declared with 'nodiscard' attribute}}
60+
four(); // expected-warning {{ignoring return value of type 'S' declared with 'nodiscard' attribute}}
6161

6262
// These are all okay because of the explicit cast to void.
6363
(void)one();
@@ -84,8 +84,8 @@ LaterReason get_later_reason();
8484
// cxx11-17-warning@-1 {{use of the 'nodiscard' attribute is a C++20 extension}}
8585

8686
void cxx20_use() {
87-
get_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: reason}}
88-
get_later_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: later reason}}
87+
get_reason(); // expected-warning {{ignoring return value of type 'ReasonStruct' declared with 'nodiscard' attribute: reason}}
88+
get_later_reason(); // expected-warning {{ignoring return value of type 'LaterReason' declared with 'nodiscard' attribute: later reason}}
8989
another_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: another reason}}
9090
conflicting_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: special reason}}
9191
}
@@ -115,20 +115,20 @@ void usage() {
115115
S('A'); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't let that S-Char go!}}
116116
S(1);
117117
S(2.2);
118-
Y(); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't throw me away either!}}
118+
Y(); // expected-warning {{ignoring temporary of type 'Y' declared with 'nodiscard' attribute: Don't throw me away either!}}
119119
S s;
120-
ConvertTo{}; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw me away!}}
120+
ConvertTo{}; // expected-warning {{ignoring return value of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}}
121121

122122
// AST is different in C++17 mode. Before, a move ctor for ConvertTo is there
123123
// as well, hence the constructor warning.
124124

125-
// since-cxx17-warning@+2 {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw me away!}}
126-
// cxx11-warning@+1 {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't throw me away!}}
125+
// since-cxx17-warning@+2 {{ignoring return value of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}}
126+
// cxx11-warning@+1 {{ignoring temporary of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}}
127127
(ConvertTo) s;
128128
(int)s; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
129129
(S)'c'; // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't let that S-Char go!}}
130-
// since-cxx17-warning@+2 {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw me away!}}
131-
// cxx11-warning@+1 {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't throw me away!}}
130+
// since-cxx17-warning@+2 {{ignoring return value of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}}
131+
// cxx11-warning@+1 {{ignoring temporary of type 'ConvertTo' declared with 'nodiscard' attribute: Don't throw me away!}}
132132
static_cast<ConvertTo>(s);
133133
static_cast<int>(s); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
134134
static_cast<double>(s); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw away as a double}}

clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p3.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace std_example {
88
error_info enable_missile_safety_mode();
99
void launch_missiles();
1010
void test_missiles() {
11-
enable_missile_safety_mode(); // expected-warning {{ignoring return value of function declared with 'nodiscard'}}
11+
enable_missile_safety_mode(); // expected-warning {{ignoring return value of type 'error_info' declared with 'nodiscard'}}
1212
launch_missiles();
1313
}
1414

clang/test/Sema/c2x-nodiscard.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ enum E2 get_e(void);
3131
[[nodiscard]] int get_i(void);
3232

3333
void f2(void) {
34-
get_s(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
35-
get_s3(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: Wrong}}
34+
get_s(); // expected-warning {{ignoring return value of type 'S4' declared with 'nodiscard' attribute}}
35+
get_s3(); // expected-warning {{ignoring return value of type 'S3' declared with 'nodiscard' attribute: Wrong}}
3636
get_i(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
37-
get_e(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
37+
get_e(); // expected-warning {{ignoring return value of type 'E2' declared with 'nodiscard' attribute}}
3838

3939
// Okay, warnings are not encouraged
4040
(void)get_s();
@@ -50,7 +50,7 @@ struct [[nodiscard]] error_info{
5050
struct error_info enable_missile_safety_mode(void);
5151
void launch_missiles(void);
5252
void test_missiles(void) {
53-
enable_missile_safety_mode(); // expected-warning {{ignoring return value of function declared with 'nodiscard'}}
53+
enable_missile_safety_mode(); // expected-warning {{ignoring return value of type 'error_info' declared with 'nodiscard'}}
5454
launch_missiles();
5555
}
5656

0 commit comments

Comments
 (0)