NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, 
rnkovacs.
Herald added subscribers: cfe-commits, Szelethus, mikhail.ramalho, 
baloghadamsoftware.

When a checker maintains a program state trait that isn't a simple 
list/set/map, but is a combination of multiple lists/sets/maps (eg., a multimap 
- which may be implemented as a map from something to set of something), 
`ProgramStateManager` only contains the factory for the trait itself. All 
auxiliary lists/sets/maps need a factory to be provided by the checker, which 
is annoying.

So far two checkers wanted a multimap, and both decided to trick the 
`ProgramStateManager` into keeping the auxiliary factory within itself by 
pretending that it's some sort of trait they're interested in, but then never 
using this trait but only using the factory.

I decided to make it legal because this functionality seems useful and i didn't 
want to dig deeply into the template magic around state traits when there's 
already a one-liner solution, though i agree that it should be possible to 
implement this in a more straightforward manner.

One thing that becomes apparent once all pieces are put together is that these 
two checkers are in fact //using the same factory//, because the type that 
identifies it, `ImmutableMap<const MemRegion *, ImmutableSet<SymbolRef>>`, is 
the same. This situation is different from two checkers registering similar 
traits.

I also moved stuff around a bit because it always bothered me.


Repository:
  rC Clang

https://reviews.llvm.org/D51388

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
  include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h
  lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
  lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
  lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -406,9 +406,8 @@
   };
 } // end anonymous namespace
 
-REGISTER_TRAIT_WITH_PROGRAMSTATE(DynamicDispatchBifurcationMap,
-                                 CLANG_ENTO_PROGRAMSTATE_MAP(const MemRegion *,
-                                                             unsigned))
+REGISTER_MAP_WITH_PROGRAMSTATE(DynamicDispatchBifurcationMap,
+                               const MemRegion *, unsigned)
 
 bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D,
                             NodeBuilder &Bldr, ExplodedNode *Pred,
Index: lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -25,23 +25,10 @@
 using namespace clang;
 using namespace ento;
 
-using PtrSet = llvm::ImmutableSet<SymbolRef>;
-
 // Associate container objects with a set of raw pointer symbols.
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(PtrSet, SymbolRef);
 REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet)
 
-// This is a trick to gain access to PtrSet's Factory.
-namespace clang {
-namespace ento {
-template <>
-struct ProgramStateTrait<PtrSet> : public ProgramStatePartialTrait<PtrSet> {
-  static void *GDMIndex() {
-    static int Index = 0;
-    return &Index;
-  }
-};
-} // end namespace ento
-} // end namespace clang
 
 namespace {
 
Index: lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -178,20 +178,12 @@
 };
 } // End anonymous namespace.
 
-typedef llvm::ImmutableSet<SymbolRef> SymbolSet;
 
 /// Maps from the symbol for a class instance to the set of
 /// symbols remaining that must be released in -dealloc.
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(SymbolSet, SymbolRef)
 REGISTER_MAP_WITH_PROGRAMSTATE(UnreleasedIvarMap, SymbolRef, SymbolSet)
 
-namespace clang {
-namespace ento {
-template<> struct ProgramStateTrait<SymbolSet>
-:  public ProgramStatePartialTrait<SymbolSet> {
-  static void *GDMIndex() { static int index = 0; return &index; }
-};
-}
-}
 
 /// An AST check that diagnose when the class requires a -dealloc method and
 /// is missing one.
Index: include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h
@@ -30,8 +30,7 @@
 
   /// Declares a program state trait for type \p Type called \p Name, and
   /// introduce a type named \c NameTy.
-  /// The macro should not be used inside namespaces, or for traits that must
-  /// be accessible from more than one translation unit.
+  /// The macro should not be used inside namespaces.
   #define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type) \
     namespace { \
       class Name {}; \
@@ -47,6 +46,102 @@
     } \
     }
 
+  /// Declares a factory for objects of type \p Type in the program state
+  /// manager. The type must provide a ::Factory sub-class. Commonly used for
+  /// ImmutableMap, ImmutableSet, ImmutableList. The macro should not be used
+  /// inside namespaces.
+  #define REGISTER_FACTORY_WITH_PROGRAMSTATE(Type) \
+    namespace clang { \
+    namespace ento { \
+      template <> \
+      struct ProgramStateTrait<Type> \
+        : public ProgramStatePartialTrait<Type> { \
+        static void *GDMIndex() { static int Index; return &Index; } \
+      }; \
+    } \
+    }
+
+  /// Helper for registering a map trait.
+  ///
+  /// If the map type were written directly in the invocation of
+  /// REGISTER_TRAIT_WITH_PROGRAMSTATE, the comma in the template arguments
+  /// would be treated as a macro argument separator, which is wrong.
+  /// This allows the user to specify a map type in a way that the preprocessor
+  /// can deal with.
+  #define CLANG_ENTO_PROGRAMSTATE_MAP(Key, Value) llvm::ImmutableMap<Key, Value>
+
+  /// Declares an immutable map of type \p NameTy, suitable for placement into
+  /// the ProgramState. This is implementing using llvm::ImmutableMap.
+  ///
+  /// \code
+  /// State = State->set<Name>(K, V);
+  /// const Value *V = State->get<Name>(K); // Returns NULL if not in the map.
+  /// State = State->remove<Name>(K);
+  /// NameTy Map = State->get<Name>();
+  /// \endcode
+  ///
+  /// The macro should not be used inside namespaces, or for traits that must
+  /// be accessible from more than one translation unit.
+  #define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value) \
+    REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, \
+                                     CLANG_ENTO_PROGRAMSTATE_MAP(Key, Value))
+
+  /// Declares an immutable map type \p Name and registers the factory
+  /// for such maps in the program state, but does not add the map itself
+  /// to the program state. Useful for managing lifetime of maps that are used
+  /// as elements of other program state data structures.
+  #define REGISTER_MAP_FACTORY_WITH_PROGRAMSTATE(Name, Key, Value) \
+    using Name = llvm::ImmutableMap<Key, Value>; \
+    REGISTER_FACTORY_WITH_PROGRAMSTATE(Name)
+
+
+  /// Declares an immutable set of type \p NameTy, suitable for placement into
+  /// the ProgramState. This is implementing using llvm::ImmutableSet.
+  ///
+  /// \code
+  /// State = State->add<Name>(E);
+  /// State = State->remove<Name>(E);
+  /// bool Present = State->contains<Name>(E);
+  /// NameTy Set = State->get<Name>();
+  /// \endcode
+  ///
+  /// The macro should not be used inside namespaces, or for traits that must
+  /// be accessible from more than one translation unit.
+  #define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem) \
+    REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, llvm::ImmutableSet<Elem>)
+
+  /// Declares an immutable set type \p Name and registers the factory
+  /// for such sets in the program state, but does not add the set itself
+  /// to the program state. Useful for managing lifetime of sets that are used
+  /// as elements of other program state data structures.
+  #define REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(Name, Elem) \
+    using Name = llvm::ImmutableSet<Elem>; \
+    REGISTER_FACTORY_WITH_PROGRAMSTATE(Name)
+
+
+  /// Declares an immutable list type \p NameTy, suitable for placement into
+  /// the ProgramState. This is implementing using llvm::ImmutableList.
+  ///
+  /// \code
+  /// State = State->add<Name>(E); // Adds to the /end/ of the list.
+  /// bool Present = State->contains<Name>(E);
+  /// NameTy List = State->get<Name>();
+  /// \endcode
+  ///
+  /// The macro should not be used inside namespaces, or for traits that must
+  /// be accessible from more than one translation unit.
+  #define REGISTER_LIST_WITH_PROGRAMSTATE(Name, Elem) \
+    REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, llvm::ImmutableList<Elem>)
+
+  /// Declares an immutable list of type \p Name and registers the factory
+  /// for such lists in the program state, but does not add the list itself
+  /// to the program state. Useful for managing lifetime of lists that are used
+  /// as elements of other program state data structures.
+  #define REGISTER_LIST_FACTORY_WITH_PROGRAMSTATE(Name, Elem) \
+    using Name = llvm::ImmutableList<Elem>; \
+    REGISTER_FACTORY_WITH_PROGRAMSTATE(Name)
+
+
   // Partial-specialization for ImmutableMap.
   template <typename Key, typename Data, typename Info>
   struct ProgramStatePartialTrait<llvm::ImmutableMap<Key, Data, Info>> {
@@ -95,15 +190,6 @@
     }
   };
 
-  /// Helper for registering a map trait.
-  ///
-  /// If the map type were written directly in the invocation of
-  /// REGISTER_TRAIT_WITH_PROGRAMSTATE, the comma in the template arguments
-  /// would be treated as a macro argument separator, which is wrong.
-  /// This allows the user to specify a map type in a way that the preprocessor
-  /// can deal with.
-  #define CLANG_ENTO_PROGRAMSTATE_MAP(Key, Value) llvm::ImmutableMap<Key, Value>
-
   // Partial-specialization for ImmutableSet.
   template <typename Key, typename Info>
   struct ProgramStatePartialTrait<llvm::ImmutableSet<Key, Info>> {
Index: include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
@@ -21,52 +21,6 @@
 namespace clang {
 namespace ento {
 
-  /// Declares an immutable map of type \p NameTy, suitable for placement into
-  /// the ProgramState. This is implementing using llvm::ImmutableMap.
-  ///
-  /// \code
-  /// State = State->set<Name>(K, V);
-  /// const Value *V = State->get<Name>(K); // Returns NULL if not in the map.
-  /// State = State->remove<Name>(K);
-  /// NameTy Map = State->get<Name>();
-  /// \endcode
-  ///
-  /// The macro should not be used inside namespaces, or for traits that must
-  /// be accessible from more than one translation unit.
-  #define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value) \
-    REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, \
-                                     CLANG_ENTO_PROGRAMSTATE_MAP(Key, Value))
-
-  /// Declares an immutable set of type \p NameTy, suitable for placement into
-  /// the ProgramState. This is implementing using llvm::ImmutableSet.
-  ///
-  /// \code
-  /// State = State->add<Name>(E);
-  /// State = State->remove<Name>(E);
-  /// bool Present = State->contains<Name>(E);
-  /// NameTy Set = State->get<Name>();
-  /// \endcode
-  ///
-  /// The macro should not be used inside namespaces, or for traits that must
-  /// be accessible from more than one translation unit.
-  #define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem) \
-    REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, llvm::ImmutableSet<Elem>)
-
-  /// Declares an immutable list of type \p NameTy, suitable for placement into
-  /// the ProgramState. This is implementing using llvm::ImmutableList.
-  ///
-  /// \code
-  /// State = State->add<Name>(E); // Adds to the /end/ of the list.
-  /// bool Present = State->contains<Name>(E);
-  /// NameTy List = State->get<Name>();
-  /// \endcode
-  ///
-  /// The macro should not be used inside namespaces, or for traits that must
-  /// be accessible from more than one translation unit.
-  #define REGISTER_LIST_WITH_PROGRAMSTATE(Name, Elem) \
-    REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, llvm::ImmutableList<Elem>)
-
-
 class CheckerContext {
   ExprEngine &Eng;
   /// The current exploded(symbolic execution) graph node.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D51388: [analyzer... Artem Dergachev via Phabricator via cfe-commits

Reply via email to