erichkeane updated this revision to Diff 211094. erichkeane marked an inline comment as done. erichkeane added a comment.
Rebased and did all the comments (including the www_status). @aaron.ballman : Wasn't positive what you meant about the conversion functions, but I think I got one? I would also love some bikeshedding on the documentation. 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,27 @@ conflicting_reason(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: special reason}} } +namespace p1771 { + struct [[nodiscard]] ConvertTo{}; + struct S { + [[nodiscard]] S(); + S(int); + [[gnu::warn_unused_result]] S(double); + operator ConvertTo(); + }; + + 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}} + S s; + (ConvertTo)s; // expected-warning {{expression result unused}} + } +}; // 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 +112,7 @@ // 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++17 extension}} +// expected-warning@86 {{use of the 'nodiscard' attribute is a C++17 extension}} +// expected-warning@92 {{use of the 'nodiscard' attribute is a C++17 extension}} #endif Index: clang/lib/Sema/SemaStmt.cpp =================================================================== --- clang/lib/Sema/SemaStmt.cpp +++ clang/lib/Sema/SemaStmt.cpp @@ -284,6 +284,13 @@ return; } } + } else if (const auto *CE = dyn_cast<CXXConstructExpr>(E)) { + if (const CXXConstructorDecl *Ctor = CE->getConstructor()) { + if (const auto *A = Ctor->getAttr<WarnUnusedResultAttr>()) { + Diag(Loc, diag::warn_unused_constructor) << 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; } Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7430,6 +7430,9 @@ 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_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,23 @@ } 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); + }; + void usages() { + marked_type(); // diagnoses. + marked_ctor(); // diagnoses. + marked_ctor(3); // Does not diagnose, int constructor isn't marked nodiscard. + } }]; } @@ -4201,4 +4218,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