Можно ли заставить Clang Static Analyzer понимать подсчет ссылок? - PullRequest
0 голосов
/ 12 января 2019

Ситуация: мой код использует класс указателя подсчета ссылок (по духу похожий на boost :: intrusive_ptr ) для управления его динамическими распределениями и предотвращения утечек памяти.

AFAICT это прекрасно работает и не пропускает память; однако, когда я запускаю Clang Static Analyzer для своего кода, использующего этот класс, Clang, похоже, не понимает логику подсчета ссылок и поэтому выдает предупреждения об утечках памяти и ошибках использования после освобождения.

Приведенный ниже игрушечный код (который значительно упрощен для ясности) демонстрирует проблему - он компилируется и запускается без утечки памяти (как указано при запуске alloc-object-count, он печатает до stdout, как и прежде), но если Я запускаю scan-build на нем, я получаю этот вывод:

$ scan-build g++ testrefcount.cpp -o testrefcount
scan-build: Using '/Users/jaf/checker-279/bin/clang' for static analysis
testrefcount.cpp:43:54: warning: Use of memory after it is freed
   const MyRefCountedObject * GetObject() const {return _obj;}
                                                 ^~~~~~~~~~~
testrefcount.cpp:52:8: warning: Potential memory leak
   }
   ^
2 warnings generated.
scan-build: 2 bugs found.
scan-build: Run 'scan-view /var/folders/q9/9w3p5x650wgfpbcws3zhsc6h0000gn/T/scan-build-2019-01-12-122209-5219-1' to examine bug reports.

У меня вопрос: есть ли способ изменить мой код так, чтобы Статический анализатор Clang не генерировал здесь ложных срабатываний? Я полагаю, что я могу просто обернуть методы подсчета рефенсов в #ifndef __clang_analyzer__, но это кажется уродливым решением (аналогично наложению ленты на индикатор «механизм проверки»), которое не позволило бы CSA обнаруживать какие-либо реальные проблемы с этим кодом, поэтому Я предпочел бы избежать этого, если есть лучший подход. (Да, я знаю о shared_ptr и intrusive_ptr и так далее - интересно, как они справляются с этой проблемой?)

Ниже приведен автономный пример кода:

#include <stdio.h>

// This value will keep track of the number of MyRefCountedObject objects alive in
// the process -- just to verify that this program doesn't actually have a memory leak
static int _objectCounter = 0;

// Objects of this class will be reference-counted by the MyRef class
class MyRefCountedObject
{
public:
   MyRefCountedObject(int value) : _value(value), _refCount(0)
   {  
      printf("MyRefCountedObject %p with value %i created.  Current number of MyRefCountedObjects is now %i\n", this, _value, ++_objectCounter);
   }

   ~MyRefCountedObject()
   {  
      printf("MyRefCountedObject %p with value %i destroyed.  Current number of MyRefCountedObjects is now %i\n", this, _value, --_objectCounter);
   }

   int GetValue() const {return _value;}

   inline void IncrementRefCount() const {_refCount++;}
   inline bool DecrementRefCount() const {return (--_refCount == 0);}

private:
   inline MyRefCountedObject &operator=(const MyRefCountedObject &); // deliberately unimplemented

   const int _value;       // An arbitrary payload/data value
   mutable int _refCount;  // how my MyRef's are currently pointing at this object
};

// Represents one reference to a MyRefCountedObject (like a toy shared_ptr)
class MyRef
{
public:
   MyRef(const MyRefCountedObject * item = NULL) : _obj(item) {RefObject();}
   MyRef(const MyRef & rhs) : _obj(NULL) {*this = rhs;}
   ~MyRef() {UnrefObject();}

   inline MyRef &operator=(const MyRef & rhs) {this->SetRef(rhs._obj); return *this;}

   const MyRefCountedObject * GetObject() const {return _obj;}

private:
   void RefObject() {if (_obj) _obj->IncrementRefCount();}

   void UnrefObject()
   {
      if ((_obj)&&(_obj->DecrementRefCount())) delete _obj;
      _obj = NULL;
   }

   void SetRef(const MyRefCountedObject * newObj)
   {
      if (newObj != _obj)
      {
         UnrefObject();
         _obj = newObj;
         RefObject();
      }
   }

   const MyRefCountedObject * _obj;
};

int main(void)
{
   MyRef ref;
   {
      MyRef ref2(new MyRefCountedObject(555));
      ref = ref2;
   }

   printf("At end of main, (ref)'s value is: %i\n", ref.GetObject()->GetValue());
   return 0;
}
...