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 { 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(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/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; +def warn_unused_constructor : Warning< + "ignoring temporary created by a constructor declared with %0 attribute">, + InGroup; +def warn_unused_constructor_msg : Warning< + "ignoring temporary created by a constructor declared with %0 attribute: %1">, + InGroup; def warn_side_effects_unevaluated_context : Warning< "expression with side effects has no effect in an unevaluated context">, InGroup; 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()) { + const auto *WarnURAttr = Type->getAttr(); + if (Type->hasAttr() || + (WarnURAttr && WarnURAttr->IsCXX11NoDiscard())) { WarnE = this; Loc = getBeginLoc(); R1 = getSourceRange(); return true; } } + + const auto *CE = cast(this); + if (const CXXConstructorDecl *Ctor = CE->getConstructor()) { + const auto *WarnURAttr = Ctor->getAttr(); + 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; } 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(D)) { S.Diag(AL.getLoc(), diag::warn_attribute_void_function_method) << AL << 0; return; } Index: clang/lib/Sema/SemaStmt.cpp =================================================================== --- clang/lib/Sema/SemaStmt.cpp +++ clang/lib/Sema/SemaStmt.cpp @@ -254,6 +254,11 @@ return; E = WarnExpr; + if (const auto *Cast = dyn_cast(E)) + if (Cast->getCastKind() == CK_NoOp || + Cast->getCastKind() == CK_ConstructorConversion) + E = Cast->getSubExpr()->IgnoreImpCasts(); + if (const CallExpr *CE = dyn_cast(E)) { if (E->getType()->isVoidType()) return; @@ -284,9 +289,34 @@ return; } } + } else if (const auto *CE = dyn_cast(E)) { + if (const CXXConstructorDecl *Ctor = CE->getConstructor()) { + const auto *A = Ctor->getAttr(); + A = A ? A : Ctor->getParent()->getAttr(); + 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(E)) { + if (const TagDecl *TD = ILE->getType()->getAsTagDecl()) { + if (const auto *A = TD->getAttr()) { + 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; + E = WarnExpr; if (const ObjCMessageExpr *ME = dyn_cast(E)) { if (getLangOpts().ObjCAutoRefCount && ME->isDelegateInitCall()) { Diag(Loc, diag::err_arc_unused_init_message) << R1; 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(s); + static_cast(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/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/www/cxx_status.html =================================================================== --- clang/www/cxx_status.html +++ clang/www/cxx_status.html @@ -664,7 +664,7 @@ P1771R1 (DR) - No + SVN [[maybe_unused]] attribute