Skip to content

Commit 422a454

Browse files
Mick235711Sirraide
authored andcommitted
[Clang] Improve diagnostic on [[nodiscard]] attribute (llvm#112521)
A follow-up to llvm#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 <[email protected]> Change-Id: I03f6ee4fde4b9df42e5b3b56881d28ccaeccb5b7
1 parent ef4ec4b commit 422a454

File tree

10 files changed

+159
-81
lines changed

10 files changed

+159
-81
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,6 @@ Improvements to Clang's diagnostics
666666
getS(); // Now diagnoses "Reason 2", previously diagnoses "Reason 1"
667667
}
668668

669-
- Clang now diagnoses ``= delete("reason")`` extension warnings only in pedantic mode rather than on by default. (#GH109311).
670669

671670
- Clang now diagnoses missing return value in functions containing ``if consteval`` (#GH116485).
672671

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: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9307,11 +9307,11 @@ def warn_unused_container_subscript_expr : Warning<
93079307
def warn_unused_call : Warning<
93089308
"ignoring return value of function declared with %0 attribute">,
93099309
InGroup<UnusedValue>;
9310-
def warn_unused_constructor : Warning<
9311-
"ignoring temporary created by a constructor declared with %0 attribute">,
9310+
def warn_unused_return_type : Warning<
9311+
"ignoring %select{return value|temporary}0 of type %2 declared with %1 attribute%select{|: %4}3">,
93129312
InGroup<UnusedValue>;
9313-
def warn_unused_constructor_msg : Warning<
9314-
"ignoring temporary created by a constructor declared with %0 attribute: %1">,
9313+
def warn_unused_constructor : Warning<
9314+
"ignoring temporary created by a constructor declared with %0 attribute%select{|: %2}1">,
93159315
InGroup<UnusedValue>;
93169316
def warn_side_effects_unevaluated_context : Warning<
93179317
"expression with side effects has no effect in an unevaluated context">,
@@ -9320,10 +9320,7 @@ def warn_side_effects_typeid : Warning<
93209320
"expression with side effects will be evaluated despite being used as an "
93219321
"operand to 'typeid'">, InGroup<PotentiallyEvaluatedExpression>;
93229322
def warn_unused_result : Warning<
9323-
"ignoring return value of function declared with %0 attribute">,
9324-
InGroup<UnusedResult>;
9325-
def warn_unused_result_msg : Warning<
9326-
"ignoring return value of function declared with %0 attribute: %1">,
9323+
"ignoring return value of function declared with %0 attribute%select{|: %2}1">,
93279324
InGroup<UnusedResult>;
93289325
def warn_unused_result_typedef_unsupported_spelling : Warning<
93299326
"'[[%select{nodiscard|gnu::warn_unused_result}0]]' attribute ignored when "

clang/lib/AST/Expr.cpp

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

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

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

16361638
SourceLocation CallExpr::getBeginLoc() const {

clang/lib/Sema/SemaStmt.cpp

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -200,23 +200,30 @@ static bool DiagnoseUnusedComparison(Sema &S, const Expr *E) {
200200
return true;
201201
}
202202

203-
static bool DiagnoseNoDiscard(Sema &S, const WarnUnusedResultAttr *A,
204-
SourceLocation Loc, SourceRange R1,
205-
SourceRange R2, bool IsCtor) {
203+
static bool DiagnoseNoDiscard(Sema &S, const NamedDecl *OffendingDecl,
204+
const WarnUnusedResultAttr *A, SourceLocation Loc,
205+
SourceRange R1, SourceRange R2, bool IsCtor) {
206206
if (!A)
207207
return false;
208208
StringRef Msg = A->getMessage();
209209

210210
if (Msg.empty()) {
211+
if (OffendingDecl)
212+
return S.Diag(Loc, diag::warn_unused_return_type)
213+
<< IsCtor << A << OffendingDecl << false << R1 << R2;
211214
if (IsCtor)
212-
return S.Diag(Loc, diag::warn_unused_constructor) << A << R1 << R2;
213-
return S.Diag(Loc, diag::warn_unused_result) << A << R1 << R2;
215+
return S.Diag(Loc, diag::warn_unused_constructor)
216+
<< A << false << R1 << R2;
217+
return S.Diag(Loc, diag::warn_unused_result) << A << false << R1 << R2;
214218
}
215219

220+
if (OffendingDecl)
221+
return S.Diag(Loc, diag::warn_unused_return_type)
222+
<< IsCtor << A << OffendingDecl << true << Msg << R1 << R2;
216223
if (IsCtor)
217-
return S.Diag(Loc, diag::warn_unused_constructor_msg) << A << Msg << R1
218-
<< R2;
219-
return S.Diag(Loc, diag::warn_unused_result_msg) << A << Msg << R1 << R2;
224+
return S.Diag(Loc, diag::warn_unused_constructor)
225+
<< A << true << Msg << R1 << R2;
226+
return S.Diag(Loc, diag::warn_unused_result) << A << true << Msg << R1 << R2;
220227
}
221228

222229
void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
@@ -286,9 +293,10 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
286293
if (E->getType()->isVoidType())
287294
return;
288295

289-
if (DiagnoseNoDiscard(*this, cast_or_null<WarnUnusedResultAttr>(
290-
CE->getUnusedResultAttr(Context)),
291-
Loc, R1, R2, /*isCtor=*/false))
296+
auto [OffendingDecl, A] = CE->getUnusedResultAttr(Context);
297+
if (DiagnoseNoDiscard(*this, OffendingDecl,
298+
cast_or_null<WarnUnusedResultAttr>(A), Loc, R1, R2,
299+
/*isCtor=*/false))
292300
return;
293301

294302
// 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) {
309317
}
310318
} else if (const auto *CE = dyn_cast<CXXConstructExpr>(E)) {
311319
if (const CXXConstructorDecl *Ctor = CE->getConstructor()) {
320+
const NamedDecl *OffendingDecl = nullptr;
312321
const auto *A = Ctor->getAttr<WarnUnusedResultAttr>();
313-
A = A ? A : Ctor->getParent()->getAttr<WarnUnusedResultAttr>();
314-
if (DiagnoseNoDiscard(*this, A, Loc, R1, R2, /*isCtor=*/true))
322+
if (!A) {
323+
OffendingDecl = Ctor->getParent();
324+
A = OffendingDecl->getAttr<WarnUnusedResultAttr>();
325+
}
326+
if (DiagnoseNoDiscard(*this, OffendingDecl, A, Loc, R1, R2,
327+
/*isCtor=*/true))
315328
return;
316329
}
317330
} else if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
318331
if (const TagDecl *TD = ILE->getType()->getAsTagDecl()) {
319332

320-
if (DiagnoseNoDiscard(*this, TD->getAttr<WarnUnusedResultAttr>(), Loc, R1,
321-
R2, /*isCtor=*/false))
333+
if (DiagnoseNoDiscard(*this, TD, TD->getAttr<WarnUnusedResultAttr>(), Loc,
334+
R1, R2, /*isCtor=*/false))
322335
return;
323336
}
324337
} else if (ShouldSuppress)
@@ -332,8 +345,8 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
332345
}
333346
const ObjCMethodDecl *MD = ME->getMethodDecl();
334347
if (MD) {
335-
if (DiagnoseNoDiscard(*this, MD->getAttr<WarnUnusedResultAttr>(), Loc, R1,
336-
R2, /*isCtor=*/false))
348+
if (DiagnoseNoDiscard(*this, nullptr, MD->getAttr<WarnUnusedResultAttr>(),
349+
Loc, R1, R2, /*isCtor=*/false))
337350
return;
338351
}
339352
} 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)