sberg created this revision.
sberg added a reviewer: erichkeane.
Herald added a reviewer: aaron.ballman.
Herald added a subscriber: jdoerfert.
Herald added a project: All.
sberg requested review of this revision.
Herald added a project: clang.

This takes inspiration from the standard `nodiscard` attribute, which can also 
be declared on individual constructors.  (I have used this change locally for 
about a year on the LibreOffice code base, marking various compilers as 
`warn_unused`, without any positive or negative effects.  Until I happened to 
notice an additional constructor that would benefit from `warn_unused`, and 
which found about 20 unused variables across the LibreOffice code base, see 
e.g., 
https://git.libreoffice.org/core/+/7cdbe504ff3b59d3aec1b1e86caf7f24223dce72%5E! 
"Fix what looks like copy/paste typos".)

(The changes in `clang/test/Misc/pragma-attribute-strict-subjects.c`, 
`clang/test/Parser/pragma-attribute.cpp`, and 
`clang/test/Sema/pragma-attribute-strict-subjects.c` are necessary to make 
those tests not fail after adding `SubRuleForCXXConstructor` to 
`SubjectMatcherForFunction` in `clang/include/clang/Basic/Attr.td`.  It appears 
that the exact diagnostic output that is generated is somewhat brittle, 
depending on implementation details.)


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D148505

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/DeclNodes.td
  clang/lib/AST/Expr.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/test/Misc/pragma-attribute-strict-subjects.c
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/Parser/pragma-attribute.cpp
  clang/test/Sema/pragma-attribute-strict-subjects.c
  clang/test/SemaCXX/warn-unused-attribute.cpp

Index: clang/test/SemaCXX/warn-unused-attribute.cpp
===================================================================
--- clang/test/SemaCXX/warn-unused-attribute.cpp
+++ clang/test/SemaCXX/warn-unused-attribute.cpp
@@ -9,12 +9,21 @@
   TestNormal();
 };
 
+struct TestCtor {
+  __attribute__((warn_unused)) TestCtor();
+  TestCtor(int);
+  void __attribute__((warn_unused)) f(); // expected-warning {{'warn_unused' attribute only applies to structs, unions, classes, and constructors}}
+};
+
 int main(void) {
   Test unused;         // expected-warning {{unused variable 'unused'}}
   Test used;
   TestNormal normal;
   used.use();
 
-  int i __attribute__((warn_unused)) = 12; // expected-warning {{'warn_unused' attribute only applies to structs, unions, and classes}}
+  TestCtor ctorUnused; // expected-warning {{unused variable 'ctorUnused'}}
+  TestCtor ctorUsed(0);
+
+  int i __attribute__((warn_unused)) = 12; // expected-warning {{'warn_unused' attribute only applies to structs, unions, classes, and constructors}}
   return i;
 }
Index: clang/test/Sema/pragma-attribute-strict-subjects.c
===================================================================
--- clang/test/Sema/pragma-attribute-strict-subjects.c
+++ clang/test/Sema/pragma-attribute-strict-subjects.c
@@ -28,7 +28,7 @@
 #pragma clang attribute pop
 
 #pragma clang attribute push (__attribute__((annotate("negatedSubRuleContradictions2"))), apply_to = any(variable(unless(is_parameter)), variable(is_thread_local), function, variable(is_global)))
-// expected-error@-1 {{negated attribute subject matcher sub-rule 'variable(unless(is_parameter))' contradicts sub-rule 'variable(is_global)'}}
+// expected-error@-1 {{negated attribute subject matcher sub-rule 'variable(unless(is_parameter))' contradicts sub-rule 'variable(is_thread_local)'}}
 // We have just one error, don't error on 'variable(is_global)'
 
 #pragma clang attribute pop
Index: clang/test/Parser/pragma-attribute.cpp
===================================================================
--- clang/test/Parser/pragma-attribute.cpp
+++ clang/test/Parser/pragma-attribute.cpp
@@ -190,7 +190,7 @@
 #pragma clang attribute pop
 #pragma clang attribute push([[clang::uninitialized]], apply_to = variable(is_global)) // expected-error {{attribute 'uninitialized' can't be applied to 'variable(is_global)'}}
 #pragma clang attribute pop
-#pragma clang attribute push([[clang::uninitialized]], apply_to = any(variable(is_parameter), variable(unless(is_parameter)))) // expected-error {{attribute 'uninitialized' can't be applied to 'variable(is_parameter)', and 'variable(unless(is_parameter))'}}
+#pragma clang attribute push([[clang::uninitialized]], apply_to = any(variable(is_parameter), variable(unless(is_parameter)))) // expected-error {{attribute 'uninitialized' can't be applied to 'variable(unless(is_parameter))', and 'variable(is_parameter)'}}
 #pragma clang attribute pop
 // We're allowed to apply attributes to subsets of allowed subjects.
 #pragma clang attribute push([[clang::no_destroy]], apply_to = variable)
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -192,7 +192,7 @@
 // CHECK-NEXT: UseHandle (SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: VecReturn (SubjectMatchRule_record)
 // CHECK-NEXT: VecTypeHint (SubjectMatchRule_function)
-// CHECK-NEXT: WarnUnused (SubjectMatchRule_record)
+// CHECK-NEXT: WarnUnused (SubjectMatchRule_record, SubjectMatchRule_function_constructor)
 // CHECK-NEXT: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType, SubjectMatchRule_type_alias)
 // CHECK-NEXT: Weak (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
 // CHECK-NEXT: WeakRef (SubjectMatchRule_variable, SubjectMatchRule_function)
Index: clang/test/Misc/pragma-attribute-strict-subjects.c
===================================================================
--- clang/test/Misc/pragma-attribute-strict-subjects.c
+++ clang/test/Misc/pragma-attribute-strict-subjects.c
@@ -16,7 +16,7 @@
 #pragma clang attribute pop
 
 #pragma clang attribute push (__attribute__((annotate("negatedSubRuleContradictions2"))), apply_to = any(variable(unless(is_parameter)), variable(is_thread_local), function, variable(is_global)))
-// expected-error@-1 {{negated attribute subject matcher sub-rule 'variable(unless(is_parameter))' contradicts sub-rule 'variable(is_global)'}}
+// expected-error@-1 {{negated attribute subject matcher sub-rule 'variable(unless(is_parameter))' contradicts sub-rule 'variable(is_thread_local)'}}
 // We have just one error, don't error on 'variable(is_global)'
 
 // Ensure that we've recovered from the error:
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -2051,15 +2051,15 @@
         return false;
 
       if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Tag)) {
-        if (!RD->hasTrivialDestructor() && !RD->hasAttr<WarnUnusedAttr>())
-          return false;
-
+        bool ConstructorWarnUnused = false;
         if (Init) {
           const CXXConstructExpr *Construct =
             dyn_cast<CXXConstructExpr>(Init);
-          if (Construct && !Construct->isElidable()) {
+          if (Construct) {
             CXXConstructorDecl *CD = Construct->getConstructor();
-            if (!CD->isTrivial() && !RD->hasAttr<WarnUnusedAttr>() &&
+            ConstructorWarnUnused = CD->hasAttr<WarnUnusedAttr>();
+            if (!Construct->isElidable() && !CD->isTrivial() &&
+                !RD->hasAttr<WarnUnusedAttr>() && !ConstructorWarnUnused &&
                 (VD->getInit()->isValueDependent() || !VD->evaluateValue()))
               return false;
           }
@@ -2077,6 +2077,10 @@
           if (isa<CXXUnresolvedConstructExpr>(Init))
             return false;
         }
+
+        if (!RD->hasTrivialDestructor() && !RD->hasAttr<WarnUnusedAttr>()
+            && !ConstructorWarnUnused)
+          return false;
       }
     }
 
Index: clang/lib/AST/Expr.cpp
===================================================================
--- clang/lib/AST/Expr.cpp
+++ clang/lib/AST/Expr.cpp
@@ -2769,7 +2769,8 @@
     if (const CXXRecordDecl *Type = getType()->getAsCXXRecordDecl()) {
       const auto *WarnURAttr = Type->getAttr<WarnUnusedResultAttr>();
       if (Type->hasAttr<WarnUnusedAttr>() ||
-          (WarnURAttr && WarnURAttr->IsCXX11NoDiscard())) {
+          (WarnURAttr && WarnURAttr->IsCXX11NoDiscard()) ||
+          cast<CXXConstructExpr>(this)->getConstructor()->hasAttr<WarnUnusedAttr>()) {
         WarnE = this;
         Loc = getBeginLoc();
         R1 = getSourceRange();
Index: clang/include/clang/Basic/DeclNodes.td
===================================================================
--- clang/include/clang/Basic/DeclNodes.td
+++ clang/include/clang/Basic/DeclNodes.td
@@ -51,7 +51,7 @@
       def Function : DeclNode<Declarator, "functions">, DeclContext;
         def CXXDeductionGuide : DeclNode<Function>;
         def CXXMethod : DeclNode<Function>;
-          def CXXConstructor : DeclNode<CXXMethod>;
+          def CXXConstructor : DeclNode<CXXMethod, "constructors">;
           def CXXDestructor : DeclNode<CXXMethod>;
           def CXXConversion : DeclNode<CXXMethod>;
       def Var : DeclNode<Declarator, "variables">;
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -471,8 +471,12 @@
 def SubRuleForCXXMethod : AttrSubjectMatcherSubRule<"is_member", [CXXMethod]> {
   let LangOpts = [CPlusPlus];
 }
+def SubRuleForCXXConstructor :
+    AttrSubjectMatcherSubRule<"constructor", [CXXConstructor]> {
+  let LangOpts = [CPlusPlus];
+}
 def SubjectMatcherForFunction : AttrSubjectMatcherRule<"function", [Function], [
-  SubRuleForCXXMethod
+  SubRuleForCXXMethod, SubRuleForCXXConstructor
 ]>;
 // hasType is abstract, it should be used with one of the sub-rules.
 def SubjectMatcherForType : AttrSubjectMatcherRule<"hasType", [], [
@@ -2989,7 +2993,7 @@
 
 def WarnUnused : InheritableAttr {
   let Spellings = [GCC<"warn_unused">];
-  let Subjects = SubjectList<[Record]>;
+  let Subjects = SubjectList<[Record, CXXConstructor]>;
   let Documentation = [Undocumented];
   let SimpleHandler = 1;
 }
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -186,6 +186,8 @@
   the compilation of the foreign language sources (e.g. Swift).
 - The ``__has_attribute``, ``__has_c_attribute`` and ``__has_cpp_attribute``
   preprocessor operators now return 1 also for attributes defined by plugins.
+- In C++, the ``__attribute__((warn_unused))`` can now also be used on individual
+  constructors,
 
 Improvements to Clang's diagnostics
 -----------------------------------
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to