diff options
-rw-r--r-- | include/clang/AST/Decl.h | 8 | ||||
-rw-r--r-- | include/clang/AST/Expr.h | 9 | ||||
-rw-r--r-- | lib/AST/Decl.cpp | 14 | ||||
-rw-r--r-- | lib/AST/Expr.cpp | 19 | ||||
-rw-r--r-- | lib/Sema/SemaStmt.cpp | 11 | ||||
-rw-r--r-- | test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp | 29 |
6 files changed, 57 insertions, 33 deletions
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 3145f35ead..52703e60aa 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -2327,14 +2327,6 @@ public: getASTContext()); } - /// Returns the WarnUnusedResultAttr that is either declared on this - /// function, or its return type declaration. - const Attr *getUnusedResultAttr() const; - - /// Returns true if this function or its return type has the - /// warn_unused_result attribute. - bool hasUnusedResultAttr() const { return getUnusedResultAttr() != nullptr; } - /// Returns the storage class as written in the source. For the /// computed linkage of symbol, see getLinkage. StorageClass getStorageClass() const { diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 156782b330..025ef0642a 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -2624,6 +2624,15 @@ public: /// type. 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; + + /// Returns true if this call expression should warn on unused results. + bool hasUnusedResultAttr(const ASTContext &Ctx) const { + return getUnusedResultAttr(Ctx) != nullptr; + } + SourceLocation getRParenLoc() const { return RParenLoc; } void setRParenLoc(SourceLocation L) { RParenLoc = L; } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index b32e5d9aa0..5536358b1e 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3231,20 +3231,6 @@ SourceRange FunctionDecl::getExceptionSpecSourceRange() const { return FTL.getExceptionSpecRange(); } -const Attr *FunctionDecl::getUnusedResultAttr() const { - QualType RetType = getReturnType(); - if (const auto *Ret = RetType->getAsRecordDecl()) { - if (const auto *R = Ret->getAttr<WarnUnusedResultAttr>()) - return R; - } else if (const auto *ET = RetType->getAs<EnumType>()) { - if (const EnumDecl *ED = ET->getDecl()) { - if (const auto *R = ED->getAttr<WarnUnusedResultAttr>()) - return R; - } - } - return getAttr<WarnUnusedResultAttr>(); -} - /// For an inline function definition in C, or for a gnu_inline function /// in C++, determine whether the definition will be externally visible. /// diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 6aa1199add..0775ff5773 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1412,6 +1412,19 @@ QualType CallExpr::getCallReturnType(const ASTContext &Ctx) const { return FnType->getReturnType(); } +const Attr *CallExpr::getUnusedResultAttr(const ASTContext &Ctx) const { + // 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; + + // Otherwise, see if the callee is marked nodiscard and return that attribute + // instead. + const Decl *D = getCalleeDecl(); + return D ? D->getAttr<WarnUnusedResultAttr>() : nullptr; +} + SourceLocation CallExpr::getBeginLoc() const { if (isa<CXXOperatorCallExpr>(this)) return cast<CXXOperatorCallExpr>(this)->getBeginLoc(); @@ -2323,16 +2336,12 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, // If this is a direct call, get the callee. const CallExpr *CE = cast<CallExpr>(this); if (const Decl *FD = CE->getCalleeDecl()) { - const FunctionDecl *Func = dyn_cast<FunctionDecl>(FD); - bool HasWarnUnusedResultAttr = Func ? Func->hasUnusedResultAttr() - : FD->hasAttr<WarnUnusedResultAttr>(); - // 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 (HasWarnUnusedResultAttr || + if (CE->hasUnusedResultAttr(Ctx) || FD->hasAttr<PureAttr>() || FD->hasAttr<ConstAttr>()) { WarnE = this; Loc = CE->getCallee()->getBeginLoc(); diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 47130cf480..dacf8d0cf4 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -259,17 +259,16 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) { if (E->getType()->isVoidType()) return; + if (const Attr *A = CE->getUnusedResultAttr(Context)) { + Diag(Loc, diag::warn_unused_result) << A << R1 << R2; + return; + } + // If the callee has attribute pure, const, or warn_unused_result, warn with // a more specific message to make it clear what is happening. If the call // is written in a macro body, only warn if it has the warn_unused_result // attribute. if (const Decl *FD = CE->getCalleeDecl()) { - if (const Attr *A = isa<FunctionDecl>(FD) - ? cast<FunctionDecl>(FD)->getUnusedResultAttr() - : FD->getAttr<WarnUnusedResultAttr>()) { - Diag(Loc, diag::warn_unused_result) << A << R1 << R2; - return; - } if (ShouldSuppress) return; if (FD->hasAttr<PureAttr>()) { diff --git a/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp b/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp index 49c418a687..43de9343bd 100644 --- a/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp +++ b/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp @@ -32,6 +32,35 @@ void g() { // OK, warning suppressed. (void)fp(); } + +namespace PR31526 { +typedef E (*fp1)(); +typedef S (*fp2)(); + +typedef S S_alias; +typedef S_alias (*fp3)(); + +typedef fp2 fp2_alias; + +void f() { + fp1 one; + fp2 two; + 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}} + + // These are all okay because of the explicit cast to void. + (void)one(); + (void)two(); + (void)three(); + (void)four(); +} +} // namespace PR31526 + #ifdef EXT // expected-warning@4 {{use of the 'nodiscard' attribute is a C++17 extension}} // expected-warning@8 {{use of the 'nodiscard' attribute is a C++17 extension}} |