静的解析ツールのCppcheckの検出項目と修正方法についてメモを記載します。
Table of Contents
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 */