Cppcheckの使い方

静的解析ツールのCppcheckの検出項目と修正方法についてメモを記載します。


1 Cppcheckの使い方

cppcheck -I./include --inline-suppr \
  --enable=warning,style,performance,portability .

-Iオプションでインクルードパスを設定します。

–enableオプションで有効にする検出項目を設定します。

–enable=allにした場合unusedFunctionも有効になるのですが、unusedFunctionはexternな関数まで未使用と判定されてしまうようなので、上記では無効にしています。

–inline-supprオプションでソースコード上の検出抑制のコメントを読み込むようになります。

-iオプションで検出対象から外すディレクトリを設定します。

2 検出項目の例(一部)

ヘッダファイルはC++のコードでインクルードされている必要があります。

2.1 catchExceptionByValue

try catch文で例外を参照ではなく値で受け取った場合に検出されます。

#ifndef __CATCH_EXCEPTION_BY_VALUE
#define __CATCH_EXCEPTION_BY_VALUE

#include <stdexcept>

namespace cppcheck {

class catchExceptionByValue {
 public:
  catchExceptionByValue()
  {
    try {
      ;
    } catch (std::exception e) {
      ;
    }
  }
};

};

#endif /** __CATCH_EXCEPTION_BY_VALUE */
[include/catchExceptionByValue.h:14]: (style) Exception should be
caught by reference.

2.2 cstyleCast

クラスのキャストに、static_castではなくC言語の型キャストを使った場合に出力されます。

#ifndef __CSTYLE_CAST_H
#define __CSTYLE_CAST_H

#include <cstdlib>

namespace cppcheck {

struct Data {
  int size;
  char data[];
};

class cstyleCast {
 private:
  Data *mData;

 public:
  explicit cstyleCast(void *data)
  {
    mData = (Data *) data;
  }
};

};

#endif /** __CSTYLE_CAST_H */
[include/cstyleCast.h:20]: (style) C-style pointer casting

2.3 duplInheritedMember

親クラスのprotectedあるいはpublicなメンバ変数名と同じ変数名を子クラスで定義した場合に出力されます。

#ifndef __DUPL_INHERITED_MEMBER
#define __DUPL_INHERITED_MEMBER

namespace cppcheck {

class duplInheritedMemberSuper {
 protected:
  int mValue;
  explicit duplInheritedMemberSuper(int value) : mValue(value) {}
};

class duplInheritedMemberInherited : public duplInheritedMemberSuper {
 private:
  int mValue;

 public:
  explicit duplInheritedMemberInherited(int value)
    : duplInheritedMemberSuper(value), mValue(value) {}
};

};

#endif /** __DUPL_INHERITED_MEMBER */
[include/duplInheritedMember.h:14] ->
[include/duplInheritedMember.h:8]: (warning) The class
'duplInheritedMemberInherited' defines member variable with name
'mValue' also defined in its parent class 'duplInheritedMemberSuper'.

2.4 eraseDereference

コンテナのメンバをイテレータでeraseした後にイテレータでメンバにアクセスした場合に出力されます。

#ifndef __ERASE_DEREFERENCE_H
#define __ERASE_DEREFERENCE_H

#include <vector>
#include <algorithm>

namespace cppcheck {

class eraseDereference {
 private:
  std::vector<int *> mValues;

 public:
  void eraseAndDereference(int *value)
  {
    std::vector<int *>::iterator it = mValues.begin();
    if (it != mValues.end()) {
      mValues.erase(it);
      *it = nullptr;
    }
  }
};

};

#endif /** __ERASE_DEREFERENCE_H */
[include/eraseDereference.h:18] -> [include/eraseDereference.h:17]:
(error) Iterator 'it' used after element has been erased.

2.5 noConstructor

privateなメンバ変数が定義されているのにコンストラクタが定義されていない場合に出力されます。

#ifndef __NO_CONSTRUCTOR_H
#define __NO_CONSTRUCTOR_H

namespace cppcheck {

class noConstructor {
 private:
  int mValue;
};

};

#endif /** __NO_CONSTRUCTOR_H */
[include/noConstructor.h:6]: (style) The class 'noConstructor' does
not have a constructor.

2.6 noCopyConstructor

メンバ変数のポインタがコンストラクタでメモリ確保されており、コピーコンストラクタが定義されていない場合に出力されます。

#ifndef __NO_COPY_CONSTRUCTOR_H
#define __NO_COPY_CONSTRUCTOR_H

#include <cstddef>

namespace cppcheck {

class noCopyConstructor {
 private:
  char *mBuffer;

 public:
  explicit noCopyConstructor(size_t size) : mBuffer(new char [size]) {}

  ~noCopyConstructor()
  {
    delete [] mBuffer;
  }
};

};

#endif /** __NO_COPY_CONSTRUCTOR_H */
[include/noCopyConstructor.h:8]: (style) 'class noCopyConstructor'
does not have a copy constructor which is recommended since the class
contains a pointer to allocated memory.

2.7 operatorEqVarError

operator=でコピーされていないメンバ変数がある場合に出力されます。

#ifndef __OPERATOR_EQ_VAR_ERROR
#define __OPERATOR_EQ_VAR_ERROR

namespace cppcheck {

class OperatorEqVarError {
 private:
  int mValue;

 public:
  OperatorEqVarError() : mValue(0) {}

  /** NOTE: Cppcheck will detect operatorEqVarerror. */
  OperatorEqVarError &operator=(const OperatorEqVarError &e)
  {
    return *this;
  }
};

};

#endif /** __OPERATOR_EQ_VAR_ERROR */
[include/operatorEqVarError.h:14]: (warning) Member variable
'OperatorEqVarError::mValue' is not assigned a value in
'OperatorEqVarError::operator='.

2.8 passedByValue

メソッドの引数が値のコピーから参照に変更できる場合に出力されます。

#ifndef __PASSED_BY_VALUE_H
#define __PASSED_BY_VALUE_H

#include <string>

namespace cppcheck {

class passedByValue {
 private:
  std::string mValue;

 public:
  explicit passedByValue(const std::string value) : mValue(value) {}
};

};

#endif /** __PASSED_BY_VALUE_H */
[include/passedByValue.h:13]: (performance) Function parameter 'value'
should be passed by reference.

2.9 postfixOperator

クラスでoperator++を使っているが、その結果を使っていない場合に出力されます。

#ifndef __POSTFIX_OPERATOR_H
#define __POSTFIX_OPERATOR_H

namespace cppcheck {

class Increment {
 public:
  Increment &operator++(int) {
    return *this;
  }
};

class postfixOperator {
 private:
  Increment mIncrement;

 public:
  void postIncrement()
  {
    mIncrement++;
  }
};

};

#endif /** __POSTFIX_OPERATOR_H */
[include/postfixOperator.h:20]: (performance) Prefer prefix ++/--
operators for non-primitive types.

2.10 stlSize

std::mapでemptyメソッドを使える箇所でsizeメソッドを使用している場合に出力されます。

#ifndef __STL_SIZE_H
#define __STL_SIZE_H

#include <map>

namespace cppcheck {

class stlSize {
 private:
  std::map<int, int> mValue;

 public:
  bool isEmpty()
  {
    return mValue.size() == 0;
  }
};

};

#endif /** __STL_SIZE_H */
[include/stlSize.h:15]: (performance) Possible inefficient checking
for 'mValue' emptiness.

2.11 uninitMemberVar

コンストラクタで初期化されていないメンバ変数がある場合に出力されます。

#ifndef __UNINIT_MEMBER_VAR_H
#define __UNINIT_MEMBER_VAR_H

namespace cppcheck {

class uninitMemberVar {
 private:
  int mMemberVar;

 public:
  uninitMemberVar() {}
};

};

#endif /** __UNINIT_MEMBER_VAR_H */
[include/uninitMemberVar.h:11]: (warning) Member variable
'uninitMemberVar::mMemberVar' is not initialized in the constructor.

2.12 unusedPrivateFunction

使用されていないprivateなメソッドがある場合に出力されます。

#ifndef __UNUSED_PRIVATE_FUNCTION_H
#define __UNUSED_PRIVATE_FUNCTION_H

namespace cppcheck {

class unusedPrivateFunction {
 private:
  void unused() {}
};

};

#endif /** __UNUSED_PRIVATE_FUNCTION_H */
[include/unusedPrivateFunction.h:8]: (style) Unused private function:
'unusedPrivateFunction::unused'

2.13 useInitializationList

初期化子リストに移動できるメンバ変数の初期化がある場合に出力されます。

#ifndef __USE_INITIALIZATION_LIST_H
#define __USE_INITIALIZATION_LIST_H

#include <string>

namespace cppcheck {

class useInitializationList {
 private:
  std::string mValue;

 public:
  explicit useInitializationList(const std::string &value)
  {
   mValue = value;
  }
};

};

#endif /** __USE_INITIALIZATION_LIST_H */
[include/useInitializationList.h:15]: (performance) Variable 'mValue'
is assigned in constructor body. Consider performing initialization in
initialization list.

3 –inline-supprで検出抑制

–inline-supprオプションを使用した場合、検出箇所の直前に検出抑制のコメントを記述することで、検出結果が除外されます。

以下はnoCopyConstructorの例です。

/* cppcheck-suppress noCopyConstructor */
// cppcheck-suppress noCopyConstructor

Javadocなコメントでは抑制されないようです。

// Not work.
/** cppcheck-suppress noCopyConstructor */