Implement support for [[nodiscard]] in C++1z that is based off existing support for...
authorAaron Ballman <aaron@aaronballman.com>
Mon, 7 Mar 2016 22:44:55 +0000 (22:44 +0000)
committerAaron Ballman <aaron@aaronballman.com>
Mon, 7 Mar 2016 22:44:55 +0000 (22:44 +0000)
llvm-svn: 262872

13 files changed:
clang/include/clang/AST/Decl.h
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/AttributeList.h
clang/lib/AST/Decl.cpp
clang/lib/Parse/ParseDeclCXX.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/lib/Sema/SemaStmt.cpp
clang/test/Sema/unused-expr.c
clang/test/SemaCXX/nodiscard.cpp [new file with mode: 0644]
clang/test/SemaObjC/method-warn-unused-attribute.m
clang/www/cxx_status.html

index 314e8f9f04046df4637602c3af53a721112a142c..04c4b46c9f9bf4c289b1e5272380518970921748 100644 (file)
@@ -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.
index 221b50040f558212a8797d9749b3fbbb8bc0a4df..4ab7c0294b73d08e1acfa02797212d44dc05cf01 100644 (file)
@@ -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 {
index 1808cea4c8bdc1ba9091c120c4452f04a9015df4..5de6b192715f0a3bb653bef02bff5a6d4239cf3c 100644 (file)
@@ -729,6 +729,31 @@ When one method overrides another, the overriding method can be more widely avai
   }];
 }
 
+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 { /*...*/ };\r
+  error_info enable_missile_safety_mode();\r
+  \r
+  void launch_missiles();\r
+  void test_missiles() {\r
+    enable_missile_safety_mode(); // diagnoses\r
+    launch_missiles();\r
+  }\r
+  error_info &foo();\r
+  void f() { foo(); } // Does not diagnose, error_info is a reference.  
+  }];
+}
+
 def FallthroughDocs : Documentation {
   let Category = DocCatStmt;
   let Content = [{
index c5faa6eb74b73466cf63d9029ea5474e713568a4..e76be07ff51d549b86a772ed4d550105bbcbfd4b 100644 (file)
@@ -2475,7 +2475,7 @@ def warn_attribute_wrong_decl_type : Warning<
   "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">,
index ddcc3e90907642e65ebc0d1c40a747b6d63e82cf..cfa3c0d170340d015a2278012b59288d6450a87f 100644 (file)
@@ -893,7 +893,8 @@ enum AttributeDeclKind {
   ExpectedObjectiveCInterfaceOrProtocol,
   ExpectedKernelFunction,
   ExpectedFunctionWithProtoType,
-  ExpectedVariableEnumFieldOrTypedef
+  ExpectedVariableEnumFieldOrTypedef,
+  ExpectedFunctionMethodEnumOrClass
 };
 
 }  // end namespace clang
index 041b530bf07d5937f7cd05c4805e81b1279a9c6a..abaec2d7e10a36c5d46c9ddd628442fb650dc7cc 100644 (file)
@@ -2935,16 +2935,22 @@ SourceRange FunctionDecl::getReturnTypeSourceRange() const {
   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
index 798284b3d39ae81abfccd026b7d619ec362a4121..15aded52b45e66d4c6892a4fca6053124c4c1112 100644 (file)
@@ -3635,7 +3635,8 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
   case AttributeList::AT_FallThrough:
   case AttributeList::AT_CXX11NoReturn:
     return true;
-
+  case AttributeList::AT_WarnUnusedResult:
+    return !ScopeName && AttrName->getName().equals("nodiscard");
   default:
     return false;
   }
index bb02e0b59c48358cf61cd856e6c2ad96c3f27b0e..ce78ab009268812ad34f33c0c59aeba68f597f51 100644 (file)
@@ -2463,6 +2463,12 @@ static void handleWarnUnusedResult(Sema &S, Decl *D, const AttributeList &Attr)
       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()));
index c58bf4640049369e0f50d3c4ac9b7dc63445cf04..b73b4f088414ad452fc24eb3b9ab28a7e88055a1 100644 (file)
@@ -249,10 +249,10 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) {
     // 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(const Stmt *S) {
     }
     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;
       }
     }
index 09359687d53243b53c54b0a29e8b1130ed6a02e8..58ad8278f2011a138ce1b38891beea1b2f6bba66 100644 (file)
@@ -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_result)); // expected-warning {{'warn_unused_
 // 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}}
diff --git a/clang/test/SemaCXX/nodiscard.cpp b/clang/test/SemaCXX/nodiscard.cpp
new file mode 100644 (file)
index 0000000..5fa4177
--- /dev/null
@@ -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
index 042f4422f808379b073910731744a03dc3b58817..b83dabf3bbdc225950df691db5b9764ecd22a303 100644 (file)
@@ -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}}
 }
 
 
index f116aee4dcc692b83efb7c490f553e1296403b94..5027ae8174e83cdc141ba21b1315d76e6d3f33a1 100644 (file)
@@ -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>