Author: aaronballman Date: Thu Jan 3 06:24:31 2019 New Revision: 350317 URL: http://llvm.org/viewvc/llvm-project?rev=350317&view=rev Log: Diagnose an unused result from a call through a function pointer whose return type is marked [[nodiscard]].
When a function returns a type and that type was declared [[nodiscard]], we diagnose any unused results from that call as though the function were marked nodiscard. The same behavior should apply to calls through a function pointer. This addresses PR31526. Modified: cfe/trunk/include/clang/AST/Decl.h cfe/trunk/include/clang/AST/Expr.h cfe/trunk/lib/AST/Decl.cpp cfe/trunk/lib/AST/Expr.cpp cfe/trunk/lib/Sema/SemaStmt.cpp cfe/trunk/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp Modified: cfe/trunk/include/clang/AST/Decl.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=350317&r1=350316&r2=350317&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/Decl.h (original) +++ cfe/trunk/include/clang/AST/Decl.h Thu Jan 3 06:24:31 2019 @@ -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 { Modified: cfe/trunk/include/clang/AST/Expr.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Expr.h?rev=350317&r1=350316&r2=350317&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/Expr.h (original) +++ cfe/trunk/include/clang/AST/Expr.h Thu Jan 3 06:24:31 2019 @@ -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; } Modified: cfe/trunk/lib/AST/Decl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=350317&r1=350316&r2=350317&view=diff ============================================================================== --- cfe/trunk/lib/AST/Decl.cpp (original) +++ cfe/trunk/lib/AST/Decl.cpp Thu Jan 3 06:24:31 2019 @@ -3231,20 +3231,6 @@ SourceRange FunctionDecl::getExceptionSp 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. /// Modified: cfe/trunk/lib/AST/Expr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Expr.cpp?rev=350317&r1=350316&r2=350317&view=diff ============================================================================== --- cfe/trunk/lib/AST/Expr.cpp (original) +++ cfe/trunk/lib/AST/Expr.cpp Thu Jan 3 06:24:31 2019 @@ -1412,6 +1412,19 @@ QualType CallExpr::getCallReturnType(con 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 // 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(); Modified: cfe/trunk/lib/Sema/SemaStmt.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaStmt.cpp?rev=350317&r1=350316&r2=350317&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaStmt.cpp (original) +++ cfe/trunk/lib/Sema/SemaStmt.cpp Thu Jan 3 06:24:31 2019 @@ -259,17 +259,16 @@ void Sema::DiagnoseUnusedExprResult(cons 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>()) { Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp?rev=350317&r1=350316&r2=350317&view=diff ============================================================================== --- cfe/trunk/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp (original) +++ cfe/trunk/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp Thu Jan 3 06:24:31 2019 @@ -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}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits