Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -2327,14 +2327,6 @@ 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 { Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -2567,6 +2567,15 @@ /// 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; } Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -3231,20 +3231,6 @@ return FTL.getExceptionSpecRange(); } -const Attr *FunctionDecl::getUnusedResultAttr() const { - QualType RetType = getReturnType(); - if (const auto *Ret = RetType->getAsRecordDecl()) { - if (const auto *R = Ret->getAttr()) - return R; - } else if (const auto *ET = RetType->getAs()) { - if (const EnumDecl *ED = ET->getDecl()) { - if (const auto *R = ED->getAttr()) - return R; - } - } - return getAttr(); -} - /// For an inline function definition in C, or for a gnu_inline function /// in C++, determine whether the definition will be externally visible. /// Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1363,6 +1363,19 @@ 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()) + return A; + + // Otherwise, see if the callee is marked nodiscard and return that attribute + // instead. + const Decl *D = getCalleeDecl(); + return D ? D->getAttr() : nullptr; +} + SourceLocation CallExpr::getBeginLoc() const { if (isa(this)) return cast(this)->getBeginLoc(); @@ -2274,16 +2287,12 @@ // If this is a direct call, get the callee. const CallExpr *CE = cast(this); if (const Decl *FD = CE->getCalleeDecl()) { - const FunctionDecl *Func = dyn_cast(FD); - bool HasWarnUnusedResultAttr = Func ? Func->hasUnusedResultAttr() - : FD->hasAttr(); - // 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() || FD->hasAttr()) { WarnE = this; Loc = CE->getCallee()->getBeginLoc(); Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -259,17 +259,16 @@ 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(FD) - ? cast(FD)->getUnusedResultAttr() - : FD->getAttr()) { - Diag(Loc, diag::warn_unused_result) << A << R1 << R2; - return; - } if (ShouldSuppress) return; if (FD->hasAttr()) { Index: test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp =================================================================== --- test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp +++ test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp @@ -32,6 +32,35 @@ // 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}}