erichkeane updated this revision to Diff 211482. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D64914/new/
https://reviews.llvm.org/D64914 Files: clang/include/clang/Basic/Attr.td clang/include/clang/Basic/AttrDocs.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/AST/Expr.cpp clang/lib/Sema/SemaDeclAttr.cpp clang/lib/Sema/SemaStmt.cpp clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp clang/test/Preprocessor/has_attribute.cpp clang/www/cxx_status.html
Index: clang/www/cxx_status.html =================================================================== --- clang/www/cxx_status.html +++ clang/www/cxx_status.html @@ -664,7 +664,7 @@ </tr> <tr> <!-- from Cologne 2019 --> <td><a href="http://wg21.link/p1771r1">P1771R1</a> (<a href="#dr">DR</a>)</td> - <td class="none" align="center">No</td> + <td class="none" align="center">SVN</td> </tr> <tr> <td><tt>[[maybe_unused]]</tt> attribute</td> Index: clang/test/Preprocessor/has_attribute.cpp =================================================================== --- clang/test/Preprocessor/has_attribute.cpp +++ clang/test/Preprocessor/has_attribute.cpp @@ -63,7 +63,7 @@ // CHECK: maybe_unused: 201603L // ITANIUM: no_unique_address: 201803L // WINDOWS: no_unique_address: 0 -// CHECK: nodiscard: 201603L +// CHECK: nodiscard: 201907L // CHECK: noreturn: 200809L // FIXME(201803L) CHECK: unlikely: 0 Index: clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp =================================================================== --- clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp +++ clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp @@ -80,6 +80,48 @@ conflicting_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: special reason}} } +namespace p1771 { +struct[[nodiscard("Don't throw me away!")]] ConvertTo{}; +struct S { + [[nodiscard]] S(); + [[nodiscard("Don't let that S-Char go!")]] S(char); + S(int); + [[gnu::warn_unused_result]] S(double); + operator ConvertTo(); + [[nodiscard]] operator int(); +}; + +struct[[nodiscard("Don't throw me away either!")]] Y{}; + +void usage() { + S(); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute}} + S('A'); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't let that S-Char go!}} + S(1); + S(2.2); + Y(); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't throw me away either!}} + S s; + ConvertTo{}; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw me away!}} + +// AST is different in C++20 mode, pre-2017 a move ctor for ConvertTo is there +// as well, hense the constructor warning. +#if __cplusplus >= 201703L +// expected-warning@+4 {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw me away!}} +#else +// expected-warning@+2 {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't throw me away!}} +#endif + (ConvertTo) s; + (int)s; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + (S)'c'; // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't let that S-Char go!}} +#if __cplusplus >= 201703L +// expected-warning@+4 {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw me away!}} +#else +// expected-warning@+2 {{ignoring temporary created by a constructor declared with 'nodiscard' attribute: Don't throw me away!}} +#endif + static_cast<ConvertTo>(s); + static_cast<int>(s); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +} +}; // namespace p1771 + #ifdef EXT // expected-warning@5 {{use of the 'nodiscard' attribute is a C++17 extension}} // expected-warning@9 {{use of the 'nodiscard' attribute is a C++17 extension}} @@ -91,4 +133,9 @@ // expected-warning@71 {{use of the 'nodiscard' attribute is a C++2a extension}} // expected-warning@73 {{use of the 'nodiscard' attribute is a C++2a extension}} // expected-warning@74 {{use of the 'nodiscard' attribute is a C++2a extension}} +// expected-warning@84 {{use of the 'nodiscard' attribute is a C++2a extension}} +// expected-warning@86 {{use of the 'nodiscard' attribute is a C++17 extension}} +// expected-warning@87 {{use of the 'nodiscard' attribute is a C++2a extension}} +// expected-warning@91 {{use of the 'nodiscard' attribute is a C++17 extension}} +// expected-warning@94 {{use of the 'nodiscard' attribute is a C++2a extension}} #endif Index: clang/lib/Sema/SemaStmt.cpp =================================================================== --- clang/lib/Sema/SemaStmt.cpp +++ clang/lib/Sema/SemaStmt.cpp @@ -284,6 +284,30 @@ return; } } + } else if (const auto *CE = dyn_cast<CXXConstructExpr>(E)) { + if (const CXXConstructorDecl *Ctor = CE->getConstructor()) { + const auto *A = Ctor->getAttr<WarnUnusedResultAttr>(); + A = A ? A : Ctor->getParent()->getAttr<WarnUnusedResultAttr>(); + if (A) { + StringRef Msg = A->getMessage(); + if (!Msg.empty()) + Diag(Loc, diag::warn_unused_constructor_msg) << A << Msg << R1 << R2; + else + Diag(Loc, diag::warn_unused_constructor) << A << R1 << R2; + return; + } + } + } else if (const auto *ILE = dyn_cast<InitListExpr>(E)) { + if (const TagDecl *TD = ILE->getType()->getAsTagDecl()) { + if (const auto *A = TD->getAttr<WarnUnusedResultAttr>()) { + StringRef Msg = A->getMessage(); + if (!Msg.empty()) + Diag(Loc, diag::warn_unused_result_msg) << A << Msg << R1 << R2; + else + Diag(Loc, diag::warn_unused_result) << A << R1 << R2; + return; + } + } } else if (ShouldSuppress) return; Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -2831,7 +2831,8 @@ static void handleWarnUnusedResult(Sema &S, Decl *D, const ParsedAttr &AL) { if (D->getFunctionType() && - D->getFunctionType()->getReturnType()->isVoidType()) { + D->getFunctionType()->getReturnType()->isVoidType() && + !isa<CXXConstructorDecl>(D)) { S.Diag(AL.getLoc(), diag::warn_attribute_void_function_method) << AL << 0; return; } Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -2563,13 +2563,31 @@ case CXXTemporaryObjectExprClass: case CXXConstructExprClass: { if (const CXXRecordDecl *Type = getType()->getAsCXXRecordDecl()) { - if (Type->hasAttr<WarnUnusedAttr>()) { + const auto *WarnURAttr = Type->getAttr<WarnUnusedResultAttr>(); + if (Type->hasAttr<WarnUnusedAttr>() || + (WarnURAttr && WarnURAttr->IsCXX11NoDiscard())) { WarnE = this; Loc = getBeginLoc(); R1 = getSourceRange(); return true; } } + + const auto *CE = cast<CXXConstructExpr>(this); + if (const CXXConstructorDecl *Ctor = CE->getConstructor()) { + const auto *WarnURAttr = Ctor->getAttr<WarnUnusedResultAttr>(); + if (WarnURAttr && WarnURAttr->IsCXX11NoDiscard()) { + WarnE = this; + Loc = getBeginLoc(); + R1 = getSourceRange(); + + if (unsigned NumArgs = CE->getNumArgs()) + R2 = SourceRange(CE->getArg(0)->getBeginLoc(), + CE->getArg(NumArgs - 1)->getEndLoc()); + return true; + } + } + return false; } @@ -2637,6 +2655,7 @@ R1 = getSourceRange(); return true; } + case CXXStaticCastExprClass: case CXXFunctionalCastExprClass: case CStyleCastExprClass: { // Ignore an explicit cast to void unless the operand is a non-trivial @@ -2662,6 +2681,9 @@ if (CE->getCastKind() == CK_ConstructorConversion) return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + if (CE->getCastKind() == CK_NoOp) + return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + WarnE = this; if (const CXXFunctionalCastExpr *CXXCE = dyn_cast<CXXFunctionalCastExpr>(this)) { Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7430,6 +7430,12 @@ def warn_unused_call : Warning< "ignoring return value of function declared with %0 attribute">, InGroup<UnusedValue>; +def warn_unused_constructor : Warning< + "ignoring temporary created by a constructor declared with %0 attribute">, + InGroup<UnusedValue>; +def warn_unused_constructor_msg : Warning< + "ignoring temporary created by a constructor declared with %0 attribute: %1">, + InGroup<UnusedValue>; def warn_side_effects_unevaluated_context : Warning< "expression with side effects has no effect in an unevaluated context">, InGroup<UnevaluatedExpression>; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -1500,6 +1500,33 @@ } error_info &foo(); void f() { foo(); } // Does not diagnose, error_info is a reference. + +Additionally, discarded temporaries resulting from a call to a constructor +marked with ``[[nodiscard]]`` or a constructor of a type marked +``[[nodiscard]]`` will also diagnose. This also applies to type conversions that +use the annotated ``[[nodiscard]]`` constructor or result in an annotated type. + +.. code-block: c++ + struct [[nodiscard]] marked_type {/*..*/ }; + struct marked_ctor { + [[nodiscard]] marked_ctor(); + marked_ctor(int); + }; + + struct S { + operator marked_type() const; + [[nodiscard]] operator int() const; + }; + + void usages() { + marked_type(); // diagnoses. + marked_ctor(); // diagnoses. + marked_ctor(3); // Does not diagnose, int constructor isn't marked nodiscard. + + S s; + static_cast<marked_type>(s); // diagnoses + (int)s; // diagnoses + } }]; } @@ -4201,4 +4228,4 @@ not initialized on device side. It has internal linkage and is initialized by the initializer on host side. }]; -} \ No newline at end of file +} Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -2335,12 +2335,19 @@ } def WarnUnusedResult : InheritableAttr { - let Spellings = [CXX11<"", "nodiscard", 201603>, C2x<"", "nodiscard">, + let Spellings = [CXX11<"", "nodiscard", 201907>, C2x<"", "nodiscard">, CXX11<"clang", "warn_unused_result">, GCC<"warn_unused_result">]; let Subjects = SubjectList<[ObjCMethod, Enum, Record, FunctionLike]>; let Args = [StringArgument<"Message", 1>]; let Documentation = [WarnUnusedResultsDocs]; + let AdditionalMembers = [{ + // Check whether this the C++11 nodiscard version, even in non C++11 + // spellings. + bool IsCXX11NoDiscard() const { + return this->getSemanticSpelling() == CXX11_nodiscard; + } + }]; } def Weak : InheritableAttr {
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits