]> granicus.if.org Git - clang/commitdiff
Consumed analysis: add 'consumable' class attribute.
authorDeLesley Hutchins <delesley@google.com>
Fri, 30 Aug 2013 22:56:34 +0000 (22:56 +0000)
committerDeLesley Hutchins <delesley@google.com>
Fri, 30 Aug 2013 22:56:34 +0000 (22:56 +0000)
Patch by chris.wailes@gmail.com

Adds the 'consumable' attribute that can be attached to classes.  This replaces
the previous method of scanning a class's methods to see if any of them have
consumed analysis attributes attached to them.  If consumed analysis attributes
are attached to methods of a class that isn't marked 'consumable' a warning
is generated.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@189702 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Analysis/Analyses/Consumed.h
include/clang/Basic/Attr.td
include/clang/Basic/DiagnosticSemaKinds.td
lib/Analysis/Consumed.cpp
lib/Sema/SemaDeclAttr.cpp
test/SemaCXX/warn-consumed-analysis-strict.cpp
test/SemaCXX/warn-consumed-analysis.cpp
test/SemaCXX/warn-consumed-parsing.cpp

index bab697a9b51e9ced75abbf2b403f4f9ce8d6d8be..dfc4f5d2fe51103ed280988dca0f8bc1a7d27112 100644 (file)
@@ -167,14 +167,9 @@ namespace consumed {
   /// A class that handles the analysis of uniqueness violations.
   class ConsumedAnalyzer {
     
-    typedef llvm::DenseMap<const CXXRecordDecl *, bool> CacheMapType;
-    typedef std::pair<const CXXRecordDecl *, bool> CachePairType;
-    
     ConsumedBlockInfo BlockInfo;
     ConsumedStateMap *CurrStates;
     
-    CacheMapType ConsumableTypeCache;
-    
     bool hasConsumableAttributes(const CXXRecordDecl *RD);
     bool splitState(const CFGBlock *CurrBlock,
                     const ConsumedStmtVisitor &Visitor);
@@ -186,9 +181,6 @@ namespace consumed {
     ConsumedAnalyzer(ConsumedWarningsHandlerBase &WarningsHandler)
         : WarningsHandler(WarningsHandler) {}
 
-    /// \brief Check to see if the type is a consumable type.
-    bool isConsumableType(QualType Type);
-    
     /// \brief Check a function's CFG for consumed violations.
     ///
     /// We traverse the blocks in the CFG, keeping track of the state of each
index d38ffa60e1e55704830c814fbaaca01c2ca99d5f..e244d4b9c0311b641b24432f78f22e0a0a12a676 100644 (file)
@@ -928,6 +928,11 @@ def SharedLocksRequired : InheritableAttr {
 
 // C/C++ consumed attributes.
 
+def Consumable : InheritableAttr {
+  let Spellings = [GNU<"consumable">];
+  let Subjects = [CXXRecord];
+}
+
 def CallableWhenUnconsumed : InheritableAttr {
   let Spellings = [GNU<"callable_when_unconsumed">];
   let Subjects = [CXXMethod];
@@ -938,16 +943,16 @@ def TestsUnconsumed : InheritableAttr {
   let Subjects = [CXXMethod];
 }
 
-def Consumes : InheritableAttr {
-  let Spellings = [GNU<"consumes">];
-  let Subjects = [CXXMethod, CXXConstructor];
-}
-
 def TestsConsumed : InheritableAttr {
   let Spellings = [GNU<"tests_consumed">];
   let Subjects = [CXXMethod];
 }
 
+def Consumes : InheritableAttr {
+  let Spellings = [GNU<"consumes">];
+  let Subjects = [CXXMethod];
+}
+
 // Type safety attributes for `void *' pointers and type tags.
 
 def ArgumentWithTypeTag : InheritableAttr {
index de437589ccc6b21155e30883718a2cb3236fac39..af3427448ac0fe28f280af5d3186331034814800 100644 (file)
@@ -2189,6 +2189,9 @@ def warn_use_while_consumed : Warning<
 def warn_use_of_temp_while_consumed : Warning<
   "invocation of method '%0' on a temporary object while it is in the "
   "'consumed' state">, InGroup<Consumed>, DefaultIgnore;
+def warn_attr_on_unconsumable_class : Warning<
+  "consumed analysis attribute is attached to class '%0' which isn't marked "
+  "as consumable">, InGroup<Consumed>, DefaultIgnore;
 
 // ConsumedStrict warnings
 def warn_use_in_unknown_state : Warning<
index 09676a8c23f02c02d04f763b0f80be72e2bf7ef4..59ebbb2fc05046757eca0e5bf44fc0d2deff73b2 100644 (file)
@@ -65,6 +65,13 @@ static ConsumedState invertConsumedUnconsumed(ConsumedState State) {
   llvm_unreachable("invalid enum");
 }
 
+static bool isConsumableType(const QualType &QT) {
+  if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())
+    return RD->hasAttr<ConsumableAttr>();
+  else
+    return false;
+}
+
 static bool isKnownState(ConsumedState State) {
   switch (State) {
   case CS_Unconsumed:
@@ -475,7 +482,7 @@ void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
   ASTContext &CurrContext = AC.getASTContext();
   QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
   
-  if (Analyzer.isConsumableType(ThisType)) {
+  if (isConsumableType(ThisType)) {
     if (Constructor->hasAttr<ConsumesAttr>() ||
         Constructor->isDefaultConstructor()) {
       
@@ -666,7 +673,7 @@ void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) {
 
 
 void ConsumedStmtVisitor::VisitParmVarDecl(const ParmVarDecl *Param) {
-  if (Analyzer.isConsumableType(Param->getType()))
+  if (isConsumableType(Param->getType()))
     StateMap->setState(Param, consumed::CS_Unknown);
 }
 
@@ -690,7 +697,7 @@ void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
 }
 
 void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
-  if (Analyzer.isConsumableType(Var->getType())) {
+  if (isConsumableType(Var->getType())) {
     if (Var->hasInit()) {
       PropagationInfo PInfo =
         PropagationMap.find(Var->getInit())->second;
@@ -891,44 +898,6 @@ void ConsumedStateMap::remove(const VarDecl *Var) {
   Map.erase(Var);
 }
 
-bool ConsumedAnalyzer::isConsumableType(QualType Type) {
-  const CXXRecordDecl *RD =
-    dyn_cast_or_null<CXXRecordDecl>(Type->getAsCXXRecordDecl());
-  
-  if (!RD) return false;
-  
-  std::pair<CacheMapType::iterator, bool> Entry =
-    ConsumableTypeCache.insert(std::make_pair(RD, false));
-  
-  if (Entry.second)
-    Entry.first->second = hasConsumableAttributes(RD);
-  
-  return Entry.first->second;
-}
-
-// TODO: Walk the base classes to see if any of them are unique types.
-//       (Deferred)
-bool ConsumedAnalyzer::hasConsumableAttributes(const CXXRecordDecl *RD) {
-  for (CXXRecordDecl::method_iterator MI = RD->method_begin(),
-       ME = RD->method_end(); MI != ME; ++MI) {
-    
-    for (Decl::attr_iterator AI = (*MI)->attr_begin(), AE = (*MI)->attr_end();
-         AI != AE; ++AI) {
-      
-      switch ((*AI)->getKind()) {
-      case attr::CallableWhenUnconsumed:
-      case attr::TestsUnconsumed:
-        return true;
-      
-      default:
-        break;
-      }
-    }
-  }
-  
-  return false;
-}
-
 bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
                                   const ConsumedStmtVisitor &Visitor) {
   
index 2bf56c737a2f46dbd5f44a2dba70e9c9d2c4b3f1..b9e4ef06330819eb6ab907f22dc09d3313951123 100644 (file)
@@ -969,19 +969,52 @@ static void handleLocksExcludedAttr(Sema &S, Decl *D,
                                Attr.getAttributeSpellingListIndex()));
 }
 
-static void handleConsumesAttr(Sema &S, Decl *D,
-                               const AttributeList &Attr) {
+static void handleConsumableAttr(Sema &S, Decl *D, const AttributeList &Attr) {
   if (!checkAttributeNumArgs(S, Attr, 0)) return;
 
-  if (!(isa<CXXMethodDecl>(D) || isa<CXXConstructorDecl>(D))) {
-    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
-      << Attr.getName() << ExpectedMethod;
+  if (!isa<CXXRecordDecl>(D)) {
+    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
+      Attr.getName() << ExpectedClass;
     return;
   }
   
+  D->addAttr(::new (S.Context)
+             ConsumableAttr(Attr.getRange(), S.Context,
+                            Attr.getAttributeSpellingListIndex()));
+}
+
+static bool checkForConsumableClass(Sema &S, const CXXMethodDecl *MD,
+                                        const AttributeList &Attr) {
+  ASTContext &CurrContext = S.getASTContext();
+  QualType ThisType = MD->getThisType(CurrContext)->getPointeeType();
+  
+  if (const CXXRecordDecl *RD = ThisType->getAsCXXRecordDecl()) {
+    if (!RD->hasAttr<ConsumableAttr>()) {
+      S.Diag(Attr.getLoc(), diag::warn_attr_on_unconsumable_class) <<
+        RD->getNameAsString();
+      
+      return false;
+    }
+  }
+  
+  return true;
+}
+
+static void handleConsumesAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+  if (!checkAttributeNumArgs(S, Attr, 0)) return;
+
+  if (!isa<CXXMethodDecl>(D)) {
+    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
+      Attr.getName() << ExpectedMethod;
+    return;
+  }
+  
+  if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr))
+    return;
+  
   D->addAttr(::new (S.Context)
              ConsumesAttr(Attr.getRange(), S.Context,
-              Attr.getAttributeSpellingListIndex()));
+                          Attr.getAttributeSpellingListIndex()));
 }
 
 static void handleCallableWhenUnconsumedAttr(Sema &S, Decl *D,
@@ -989,14 +1022,17 @@ static void handleCallableWhenUnconsumedAttr(Sema &S, Decl *D,
   if (!checkAttributeNumArgs(S, Attr, 0)) return;
 
   if (!isa<CXXMethodDecl>(D)) {
-    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
-      << Attr.getName() << ExpectedMethod;
+    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
+      Attr.getName() << ExpectedMethod;
     return;
   }
   
+  if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr))
+    return;
+  
   D->addAttr(::new (S.Context)
              CallableWhenUnconsumedAttr(Attr.getRange(), S.Context,
-              Attr.getAttributeSpellingListIndex()));
+                                        Attr.getAttributeSpellingListIndex()));
 }
 
 static void handleTestsConsumedAttr(Sema &S, Decl *D,
@@ -1004,14 +1040,17 @@ static void handleTestsConsumedAttr(Sema &S, Decl *D,
   if (!checkAttributeNumArgs(S, Attr, 0)) return;
 
   if (!isa<CXXMethodDecl>(D)) {
-    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
-      << Attr.getName() << ExpectedMethod;
+    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
+      Attr.getName() << ExpectedMethod;
     return;
   }
   
+  if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr))
+    return;
+  
   D->addAttr(::new (S.Context)
              TestsConsumedAttr(Attr.getRange(), S.Context,
-              Attr.getAttributeSpellingListIndex()));
+                               Attr.getAttributeSpellingListIndex()));
 }
 
 static void handleTestsUnconsumedAttr(Sema &S, Decl *D,
@@ -1019,14 +1058,17 @@ static void handleTestsUnconsumedAttr(Sema &S, Decl *D,
   if (!checkAttributeNumArgs(S, Attr, 0)) return;
 
   if (!isa<CXXMethodDecl>(D)) {
-    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
-      << Attr.getName() << ExpectedMethod;
+    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
+      Attr.getName() << ExpectedMethod;
     return;
   }
   
+  if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr))
+    return;
+  
   D->addAttr(::new (S.Context)
              TestsUnconsumedAttr(Attr.getRange(), S.Context,
-              Attr.getAttributeSpellingListIndex()));
+                                 Attr.getAttributeSpellingListIndex()));
 }
 
 static void handleExtVectorTypeAttr(Sema &S, Scope *scope, Decl *D,
@@ -4995,6 +5037,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
     break;
 
   // Uniqueness analysis attributes.
+  case AttributeList::AT_Consumable:
+    handleConsumableAttr(S, D, Attr);
+    break;
   case AttributeList::AT_Consumes:
     handleConsumesAttr(S, D, Attr);
     break;
index b8a2982fc5b89116740fb170633711b442a95c66..499ba009bdd7a813edca3f48e1a2e9ae476fcee0 100644 (file)
@@ -1,15 +1,16 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed-strict -std=c++11 %s
 
 #define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
-#define CONSUMES __attribute__ ((consumes))
-#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed))
+#define CONSUMABLE               __attribute__ ((consumable))
+#define CONSUMES                 __attribute__ ((consumes))
+#define TESTS_UNCONSUMED         __attribute__ ((tests_unconsumed))
 
 #define TEST_VAR(Var) Var.isValid()
 
 typedef decltype(nullptr) nullptr_t;
 
 template <typename T>
-class ConsumableClass {
+class CONSUMABLE ConsumableClass {
   T var;
   
   public:
index adaab647e1569da401a8096b5fe8386f50b0de06..d4387fc29deecb5e0bac499288859091827f91c7 100644 (file)
@@ -1,13 +1,14 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
 
 #define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
-#define CONSUMES __attribute__ ((consumes))
-#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed))
+#define CONSUMABLE               __attribute__ ((consumable))
+#define CONSUMES                 __attribute__ ((consumes))
+#define TESTS_UNCONSUMED         __attribute__ ((tests_unconsumed))
 
 typedef decltype(nullptr) nullptr_t;
 
 template <typename T>
-class ConsumableClass {
+class CONSUMABLE ConsumableClass {
   T var;
   
   public:
index 23df1d19dfd883479551aef9d88a9008d5c4bdfd..ae71e29dc7943b0747aac3d28588990bb27e15c0 100644 (file)
@@ -1,29 +1,35 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
 
+#define CONSUMABLE                __attribute__ ((consumable))
 #define CONSUMES                  __attribute__ ((consumes))
 #define TESTS_UNCONSUMED          __attribute__ ((tests_unconsumed))
 #define CALLABLE_WHEN_UNCONSUMED  __attribute__ ((callable_when_unconsumed))
 
 class AttrTester0 {
-  void Consumes(void)        __attribute__ ((consumes(42))); // expected-error {{attribute takes no arguments}}
-  bool TestsUnconsumed(void) __attribute__ ((tests_unconsumed(42))); // expected-error {{attribute takes no arguments}}
-  void CallableWhenUnconsumed(void
+  void Consumes()        __attribute__ ((consumes(42))); // expected-error {{attribute takes no arguments}}
+  bool TestsUnconsumed() __attribute__ ((tests_unconsumed(42))); // expected-error {{attribute takes no arguments}}
+  void CallableWhenUnconsumed() 
     __attribute__ ((callable_when_unconsumed(42))); // expected-error {{attribute takes no arguments}}
 };
 
 int var0 CONSUMES; // expected-warning {{'consumes' attribute only applies to methods}}
 int var1 TESTS_UNCONSUMED; // expected-warning {{'tests_unconsumed' attribute only applies to methods}}
 int var2 CALLABLE_WHEN_UNCONSUMED; // expected-warning {{'callable_when_unconsumed' attribute only applies to methods}}
+int var3 CONSUMABLE; // expected-warning {{'consumable' attribute only applies to classes}}
 
-void function0(void) CONSUMES; // expected-warning {{'consumes' attribute only applies to methods}}
-void function1(void) TESTS_UNCONSUMED; // expected-warning {{'tests_unconsumed' attribute only applies to methods}}
-void function2(void) CALLABLE_WHEN_UNCONSUMED; // expected-warning {{'callable_when_unconsumed' attribute only applies to methods}}
+void function0() CONSUMES; // expected-warning {{'consumes' attribute only applies to methods}}
+void function1() TESTS_UNCONSUMED; // expected-warning {{'tests_unconsumed' attribute only applies to methods}}
+void function2() CALLABLE_WHEN_UNCONSUMED; // expected-warning {{'callable_when_unconsumed' attribute only applies to methods}}
+void function3() CONSUMABLE; // expected-warning {{'consumable' attribute only applies to classes}}
 
-class AttrTester1 {
-  void consumes(void)        CONSUMES;
-  bool testsUnconsumed(void) TESTS_UNCONSUMED;
+class CONSUMABLE AttrTester1 {
+  void callableWhenUnconsumed() CALLABLE_WHEN_UNCONSUMED;
+  void consumes()               CONSUMES;
+  bool testsUnconsumed()        TESTS_UNCONSUMED;
 };
 
 class AttrTester2 {
-  void callableWhenUnconsumed(void) CALLABLE_WHEN_UNCONSUMED;
+  void callableWhenUnconsumed() CALLABLE_WHEN_UNCONSUMED; // expected-warning {{consumed analysis attribute is attached to class 'AttrTester2' which isn't marked as consumable}}
+  void consumes()               CONSUMES; // expected-warning {{consumed analysis attribute is attached to class 'AttrTester2' which isn't marked as consumable}}
+  bool testsUnconsumed()        TESTS_UNCONSUMED; // expected-warning {{consumed analysis attribute is attached to class 'AttrTester2' which isn't marked as consumable}}
 };