Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -2335,11 +2335,18 @@ } 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 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 @@ -1493,6 +1493,22 @@ } 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. + +.. code-block: c++ + struct [[nodiscard]] marked_type {/*..*/ }; + struct marked_ctor { + [[nodiscard]] marked_ctor(); + marked_ctor(int); + }; + void usages() { + marked_type(); // diagnoses. + marked_ctor(); // diagnoses. + marked_ctor(3); // Does not diagnose, int constructor isn't marked nodiscard. + } }]; } @@ -4194,4 +4210,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 @@ -7426,6 +7426,9 @@ 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_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 @@ -279,6 +279,13 @@ return; } } + } else if (const auto *CE = dyn_cast(E)) { + if (const CXXConstructorDecl *Ctor = CE->getConstructor()) { + if (const auto *A = Ctor->getAttr()) { + Diag(Loc, diag::warn_unused_constructor) << A << R1 << R2; + return; + } + } } else if (ShouldSuppress) return; 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 @@ -61,10 +61,29 @@ } } // namespace PR31526 +namespace p1771 { + struct S { + [[nodiscard]] S(); + S(int); + [[gnu::warn_unused_result]] S(double); + }; + + struct[[nodiscard]] Y{}; + + void usage() { + S(); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute}} + S(1); + S(2.2); + Y(); // expected-warning {{expression result unused}} + } +}; // namespace p1771 + #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}} // expected-warning@11 {{use of the 'nodiscard' attribute is a C++17 extension}} // expected-warning@12 {{use of the 'nodiscard' attribute is a C++17 extension}} // expected-warning@28 {{use of the 'nodiscard' attribute is a C++17 extension}} +// expected-warning@66 {{use of the 'nodiscard' attribute is a C++17 extension}} +// expected-warning@71 {{use of the 'nodiscard' attribute is a C++17 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