Author: aaronballman Date: Mon Mar 7 16:44:55 2016 New Revision: 262872 URL: http://llvm.org/viewvc/llvm-project?rev=262872&view=rev Log: Implement support for [[nodiscard]] in C++1z that is based off existing support for warn_unused_result, and treat it as an extension pre-C++1z. This also means extending the existing warn_unused_result attribute so that it can be placed on an enum as well as a class.
Added: cfe/trunk/test/SemaCXX/nodiscard.cpp Modified: cfe/trunk/include/clang/AST/Decl.h cfe/trunk/include/clang/Basic/Attr.td cfe/trunk/include/clang/Basic/AttrDocs.td cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/include/clang/Sema/AttributeList.h cfe/trunk/lib/AST/Decl.cpp cfe/trunk/lib/Parse/ParseDeclCXX.cpp cfe/trunk/lib/Sema/SemaDeclAttr.cpp cfe/trunk/lib/Sema/SemaStmt.cpp cfe/trunk/test/Sema/unused-expr.c cfe/trunk/test/SemaObjC/method-warn-unused-attribute.m cfe/trunk/www/cxx_status.html Modified: cfe/trunk/include/clang/AST/Decl.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=262872&r1=262871&r2=262872&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/Decl.h (original) +++ cfe/trunk/include/clang/AST/Decl.h Mon Mar 7 16:44:55 2016 @@ -2025,12 +2025,16 @@ public: return getType()->getAs<FunctionType>()->getCallResultType(getASTContext()); } + /// \brief Returns the WarnUnusedResultAttr that is either declared on this + /// function, or its return type declaration. + const Attr *getUnusedResultAttr() const; + /// \brief Returns true if this function or its return type has the /// warn_unused_result attribute. If the return type has the attribute and /// this function is a method of the return type's class, then false will be /// returned to avoid spurious warnings on member methods such as assignment /// operators. - bool hasUnusedResultAttr() const; + bool hasUnusedResultAttr() const { return getUnusedResultAttr() != nullptr; } /// \brief Returns the storage class as written in the source. For the /// computed linkage of symbol, see getLinkage. Modified: cfe/trunk/include/clang/Basic/Attr.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=262872&r1=262871&r2=262872&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/Attr.td (original) +++ cfe/trunk/include/clang/Basic/Attr.td Mon Mar 7 16:44:55 2016 @@ -1537,11 +1537,12 @@ def WarnUnused : InheritableAttr { } def WarnUnusedResult : InheritableAttr { - let Spellings = [GCC<"warn_unused_result">, - CXX11<"clang", "warn_unused_result">]; - let Subjects = SubjectList<[ObjCMethod, CXXRecord, FunctionLike], WarnDiag, - "ExpectedFunctionMethodOrClass">; - let Documentation = [Undocumented]; + let Spellings = [CXX11<"", "nodiscard", 201603>, + CXX11<"clang", "warn_unused_result">, + GCC<"warn_unused_result">]; + let Subjects = SubjectList<[ObjCMethod, Enum, CXXRecord, FunctionLike], + WarnDiag, "ExpectedFunctionMethodEnumOrClass">; + let Documentation = [WarnUnusedResultsDocs]; } def Weak : InheritableAttr { Modified: cfe/trunk/include/clang/Basic/AttrDocs.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/AttrDocs.td?rev=262872&r1=262871&r2=262872&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/AttrDocs.td (original) +++ cfe/trunk/include/clang/Basic/AttrDocs.td Mon Mar 7 16:44:55 2016 @@ -729,6 +729,31 @@ When one method overrides another, the o }]; } +def WarnUnusedResultsDocs : Documentation { + let Category = DocCatFunction; + let Heading = "nodiscard, warn_unused_result, clang::warn_unused_result, gnu::warn_unused_result"; + let Content = [{ +Clang supports the ability to diagnose when the results of a function call +expression are discarded under suspicious circumstances. A diagnostic is +generated when a function or its return type is marked with ``[[nodiscard]]`` +(or ``__attribute__((warn_unused_result))``) and the function call appears as a +potentially-evaluated discarded-value expression that is not explicitly cast to +`void`. + +.. code-block: c++ + struct [[nodiscard]] error_info { /*...*/ }; + error_info enable_missile_safety_mode(); + + void launch_missiles(); + void test_missiles() { + enable_missile_safety_mode(); // diagnoses + launch_missiles(); + } + error_info &foo(); + void f() { foo(); } // Does not diagnose, error_info is a reference. + }]; +} + def FallthroughDocs : Documentation { let Category = DocCatStmt; let Content = [{ Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=262872&r1=262871&r2=262872&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Mar 7 16:44:55 2016 @@ -2475,7 +2475,7 @@ def warn_attribute_wrong_decl_type : War "variables, functions and classes|Objective-C protocols|" "functions and global variables|structs, unions, and typedefs|structs and typedefs|" "interface or protocol declarations|kernel functions|non-K&R-style functions|" - "variables, enums, fields and typedefs}1">, + "variables, enums, fields and typedefs|functions, methods, enums, and classes}1">, InGroup<IgnoredAttributes>; def err_attribute_wrong_decl_type : Error<warn_attribute_wrong_decl_type.Text>; def warn_type_attribute_wrong_type : Warning< @@ -6587,11 +6587,13 @@ def warn_side_effects_typeid : Warning< "expression with side effects will be evaluated despite being used as an " "operand to 'typeid'">, InGroup<PotentiallyEvaluatedExpression>; def warn_unused_result : Warning< - "ignoring return value of function declared with warn_unused_result " - "attribute">, InGroup<DiagGroup<"unused-result">>; + "ignoring return value of function declared with %0 attribute">, + InGroup<DiagGroup<"unused-result">>; def warn_unused_volatile : Warning< "expression result unused; assign into a variable to force a volatile load">, InGroup<DiagGroup<"unused-volatile-lvalue">>; +def ext_nodiscard_attr_is_a_cxx1z_extension : ExtWarn< + "use of the 'nodiscard' attribute is a C++1z extension">, InGroup<CXX1z>; def warn_unused_comparison : Warning< "%select{%select{|in}1equality|relational}0 comparison result unused">, Modified: cfe/trunk/include/clang/Sema/AttributeList.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/AttributeList.h?rev=262872&r1=262871&r2=262872&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/AttributeList.h (original) +++ cfe/trunk/include/clang/Sema/AttributeList.h Mon Mar 7 16:44:55 2016 @@ -893,7 +893,8 @@ enum AttributeDeclKind { ExpectedObjectiveCInterfaceOrProtocol, ExpectedKernelFunction, ExpectedFunctionWithProtoType, - ExpectedVariableEnumFieldOrTypedef + ExpectedVariableEnumFieldOrTypedef, + ExpectedFunctionMethodEnumOrClass }; } // end namespace clang Modified: cfe/trunk/lib/AST/Decl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=262872&r1=262871&r2=262872&view=diff ============================================================================== --- cfe/trunk/lib/AST/Decl.cpp (original) +++ cfe/trunk/lib/AST/Decl.cpp Mon Mar 7 16:44:55 2016 @@ -2935,16 +2935,22 @@ SourceRange FunctionDecl::getReturnTypeS return RTRange; } -bool FunctionDecl::hasUnusedResultAttr() const { +const Attr *FunctionDecl::getUnusedResultAttr() const { QualType RetType = getReturnType(); if (RetType->isRecordType()) { const CXXRecordDecl *Ret = RetType->getAsCXXRecordDecl(); const auto *MD = dyn_cast<CXXMethodDecl>(this); - if (Ret && Ret->hasAttr<WarnUnusedResultAttr>() && - !(MD && MD->getCorrespondingMethodInClass(Ret, true))) - return true; + if (Ret && !(MD && MD->getCorrespondingMethodInClass(Ret, true))) { + 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 hasAttr<WarnUnusedResultAttr>(); + return getAttr<WarnUnusedResultAttr>(); } /// \brief For an inline function definition in C, or for a gnu_inline function Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=262872&r1=262871&r2=262872&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original) +++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Mon Mar 7 16:44:55 2016 @@ -3635,7 +3635,8 @@ static bool IsBuiltInOrStandardCXX11Attr case AttributeList::AT_FallThrough: case AttributeList::AT_CXX11NoReturn: return true; - + case AttributeList::AT_WarnUnusedResult: + return !ScopeName && AttrName->getName().equals("nodiscard"); default: return false; } Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=262872&r1=262871&r2=262872&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Mon Mar 7 16:44:55 2016 @@ -2463,6 +2463,12 @@ static void handleWarnUnusedResult(Sema return; } + // If this is spelled as the standard C++1z attribute, but not in C++1z, warn + // about using it as an extension. + if (!S.getLangOpts().CPlusPlus1z && Attr.isCXX11Attribute() && + !Attr.getScopeName()) + S.Diag(Attr.getLoc(), diag::ext_nodiscard_attr_is_a_cxx1z_extension); + D->addAttr(::new (S.Context) WarnUnusedResultAttr(Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); Modified: cfe/trunk/lib/Sema/SemaStmt.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaStmt.cpp?rev=262872&r1=262871&r2=262872&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaStmt.cpp (original) +++ cfe/trunk/lib/Sema/SemaStmt.cpp Mon Mar 7 16:44:55 2016 @@ -249,10 +249,10 @@ void Sema::DiagnoseUnusedExprResult(cons // is written in a macro body, only warn if it has the warn_unused_result // attribute. if (const Decl *FD = CE->getCalleeDecl()) { - const FunctionDecl *Func = dyn_cast<FunctionDecl>(FD); - if (Func ? Func->hasUnusedResultAttr() - : FD->hasAttr<WarnUnusedResultAttr>()) { - Diag(Loc, diag::warn_unused_result) << R1 << R2; + 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) @@ -276,8 +276,8 @@ void Sema::DiagnoseUnusedExprResult(cons } const ObjCMethodDecl *MD = ME->getMethodDecl(); if (MD) { - if (MD->hasAttr<WarnUnusedResultAttr>()) { - Diag(Loc, diag::warn_unused_result) << R1 << R2; + if (const auto *A = MD->getAttr<WarnUnusedResultAttr>()) { + Diag(Loc, diag::warn_unused_result) << A << R1 << R2; return; } } Modified: cfe/trunk/test/Sema/unused-expr.c URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/unused-expr.c?rev=262872&r1=262871&r2=262872&view=diff ============================================================================== --- cfe/trunk/test/Sema/unused-expr.c (original) +++ cfe/trunk/test/Sema/unused-expr.c Mon Mar 7 16:44:55 2016 @@ -76,7 +76,7 @@ void t4(int a) { // rdar://7186119 int t5f(void) __attribute__((warn_unused_result)); void t5() { - t5f(); // expected-warning {{ignoring return value of function declared with warn_unused_result}} + t5f(); // expected-warning {{ignoring return value of function declared with 'warn_unused_result' attribute}} } @@ -88,11 +88,11 @@ int t6() { if (fn1() < 0 || fn2(2,1) < 0 || fn3(2) < 0) // no warnings return -1; - fn1(); // expected-warning {{ignoring return value of function declared with warn_unused_result attribute}} + fn1(); // expected-warning {{ignoring return value of function declared with 'warn_unused_result' attribute}} fn2(92, 21); // expected-warning {{ignoring return value of function declared with pure attribute}} fn3(42); // expected-warning {{ignoring return value of function declared with const attribute}} __builtin_abs(0); // expected-warning {{ignoring return value of function declared with const attribute}} - (void)0, fn1(); // expected-warning {{ignoring return value of function declared with warn_unused_result attribute}} + (void)0, fn1(); // expected-warning {{ignoring return value of function declared with 'warn_unused_result' attribute}} return 0; } @@ -101,7 +101,7 @@ int t7 __attribute__ ((warn_unused_resul // PR4010 int (*fn4)(void) __attribute__ ((warn_unused_result)); void t8() { - fn4(); // expected-warning {{ignoring return value of function declared with warn_unused_result attribute}} + fn4(); // expected-warning {{ignoring return value of function declared with 'warn_unused_result' attribute}} } void t9() __attribute__((warn_unused_result)); // expected-warning {{attribute 'warn_unused_result' cannot be applied to functions without return value}} Added: cfe/trunk/test/SemaCXX/nodiscard.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/nodiscard.cpp?rev=262872&view=auto ============================================================================== --- cfe/trunk/test/SemaCXX/nodiscard.cpp (added) +++ cfe/trunk/test/SemaCXX/nodiscard.cpp Mon Mar 7 16:44:55 2016 @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++1z -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify -DEXT %s + +#if !defined(EXT) +static_assert(__has_cpp_attribute(nodiscard) == 201603); + +struct [[nodiscard]] S {}; +S get_s(); +S& get_s_ref(); + +enum [[nodiscard]] E {}; +E get_e(); + +[[nodiscard]] int get_i(); + +void f() { + get_s(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + get_i(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + get_e(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + + // Okay, warnings are not encouraged + get_s_ref(); + (void)get_s(); + (void)get_i(); + (void)get_e(); +} + +[[nodiscard nodiscard]] int wrong1(); // expected-error {{attribute 'nodiscard' cannot appear multiple times in an attribute specifier}} + +namespace [[nodiscard]] N {} // expected-warning {{'nodiscard' attribute only applies to functions, methods, enums, and classes}} +#else +struct [[nodiscard]] S {}; // expected-warning {{use of the 'nodiscard' attribute is a C++1z extension}} +#endif // EXT Modified: cfe/trunk/test/SemaObjC/method-warn-unused-attribute.m URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/method-warn-unused-attribute.m?rev=262872&r1=262871&r2=262872&view=diff ============================================================================== --- cfe/trunk/test/SemaObjC/method-warn-unused-attribute.m (original) +++ cfe/trunk/test/SemaObjC/method-warn-unused-attribute.m Mon Mar 7 16:44:55 2016 @@ -9,8 +9,8 @@ void foo(INTF *a) { [a garf]; - [a fee]; // expected-warning {{ignoring return value of function declared with warn_unused_result attribute}} - [INTF c]; // expected-warning {{ignoring return value of function declared with warn_unused_result attribute}} + [a fee]; // expected-warning {{ignoring return value of function declared with 'warn_unused_result' attribute}} + [INTF c]; // expected-warning {{ignoring return value of function declared with 'warn_unused_result' attribute}} } Modified: cfe/trunk/www/cxx_status.html URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=262872&r1=262871&r2=262872&view=diff ============================================================================== --- cfe/trunk/www/cxx_status.html (original) +++ cfe/trunk/www/cxx_status.html Mon Mar 7 16:44:55 2016 @@ -634,7 +634,7 @@ as the draft C++1z standard evolves.</p> <tr> <td><tt>[[nodiscard]]</tt> attribute</td> <td><a href="http://wg21.link/p0189r1">P0189R1</a></td> - <td class="none" align="center">No</td> + <td class="full" align="center">SVN</td> </tr> <tr> <td><tt>[[maybe_unused]]</tt> attribute</td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits