Edited at

冬休み到来! clang-tidy で安心安全な C/C++ コーディングを極めよう!

漢なら C/C++ ですね! clang-tidy で安心安全な C/C++ コーディングを極めましょう!

http://clang.llvm.org/extra/clang-tidy/index.html

clang-tidy は, LibTooling をベースとした, コーディングをチェックしたりするツールです. ちょっとしたワーニングなら自動でリライトする機能もあって便利だよ.

clang 単体でも, -Weverything で結構見つかるけど(C++コードのデトックス 参照), clang-tidy だと MPI 関数のチェックとか(なんという俺得機能)あって, もっと色々解析してくれるよ.


tidy(タイディ) とは

テディベアの Teddy とはスペルが違います.

片付けるとか, 整理するという意味合いです. つまりは konmari(こんまり)ですね.


準備する.

clang-tidy では, C/C++ コードがどのようにコンパイルされたかの情報が必要になります.

http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html

cmake とか ninja とかだと, 自動で生成する機能があるようです.

とりあえず今回は手動で作ります.

ちなみに, こういう時, ヘッダオンリーだったりシングルファイルな C/C++ コードだとセットアップが楽で新しいツールをすぐ試せて便利ですね! シングルファイル C/C++ ライブラリが便利すぎてやばい

以下のようなコンパイルデータベースを用意します.

ファイル名は compile_commands.json でないとダメのようです.

[

{ "directory": "/Users/syoyo/work/tidy-test/",
"command": "/usr/bin/clang++ -Weverything file.cc",
"file": "file.cc"
}
]

適当に file.cc をでっちあげます.

int main(int argc, char **argv)

{
const char *cpc;
const char *cpc2 = (const char*)cpc;
short b = argc;
return b;
}

以下のようにして file.cc をチェックします. compile_commands.json は同じディレクトリにあると仮定していますが, 別ディレクトリにある場合は -p コマンドでディレクトリの場所を指定できます.

$ clang-tidy file.cc

7 warnings generated.
file.cc:1:27: warning: unused parameter 'argv' [clang-diagnostic-unused-parameter]
int main(int argc, char **argv)
^
file.cc:4:3: warning: Assigned value is garbage or undefined [clang-analyzer-core.uninitialized.Assign]
const char *cpc2 = (const char*)cpc;
^
file.cc:3:3: note: 'cpc' declared without an initial value
const char *cpc;
^
file.cc:4:3: note: Assigned value is garbage or undefined
const char *cpc2 = (const char*)cpc;
^
file.cc:4:15: warning: Value stored to 'cpc2' during its initialization is never read [clang-analyzer-deadcode.DeadStores]
const char *cpc2 = (const char*)cpc;
^
file.cc:4:15: note: Value stored to 'cpc2' during its initialization is never read
const char *cpc2 = (const char*)cpc;
^
file.cc:4:15: warning: unused variable 'cpc2' [clang-diagnostic-unused-variable]
const char *cpc2 = (const char*)cpc;
^
file.cc:4:22: warning: use of old-style cast [clang-diagnostic-old-style-cast]
const char *cpc2 = (const char*)cpc;
^
/Users/syoyo/work/tidy-test/file.cc:4:35: warning: variable 'cpc' is uninitialized when used here [clang-diagnostic-uninitialized]
const char *cpc2 = (const char*)cpc;
^
/Users/syoyo/work/tidy-test/file.cc:3:18: note: initialize the variable 'cpc' to silence this warning
const char *cpc;
^
/Users/syoyo/work/tidy-test/file.cc:5:13: warning: implicit conversion loses integer precision: 'int' to 'short' [clang-diagnostic-conversion]
short b = argc;
^

おお〜!

-checks でどのチェックを行うか指定できます.

-checks=* -list-checks で, どのチェックが利用可能かわかります.

$ clang-tidy -checks=* -list-checks

Enabled checks:
boost-use-to-string
cert-dcl03-c
cert-dcl50-cpp
cert-dcl54-cpp
cert-dcl59-cpp
cert-env33-c
cert-err34-c
cert-err52-cpp
cert-err58-cpp
cert-err60-cpp
cert-err61-cpp
cert-fio38-c
cert-flp30-c
cert-oop11-cpp
clang-analyzer-alpha.core.BoolAssignment
clang-analyzer-alpha.core.CallAndMessageUnInitRefArg
clang-analyzer-alpha.core.CastSize
clang-analyzer-alpha.core.CastToStruct
clang-analyzer-alpha.core.DynamicTypeChecker
clang-analyzer-alpha.core.FixedAddr
clang-analyzer-alpha.core.IdenticalExpr
clang-analyzer-alpha.core.PointerArithm
clang-analyzer-alpha.core.PointerSub
clang-analyzer-alpha.core.SizeofPtr
clang-analyzer-alpha.core.TestAfterDivZero
clang-analyzer-alpha.cplusplus.VirtualCall
clang-analyzer-alpha.deadcode.UnreachableCode
clang-analyzer-alpha.osx.cocoa.DirectIvarAssignment
clang-analyzer-alpha.osx.cocoa.DirectIvarAssignmentForAnnotatedFunctions
clang-analyzer-alpha.osx.cocoa.InstanceVariableInvalidation
clang-analyzer-alpha.osx.cocoa.MissingInvalidationMethod
clang-analyzer-alpha.osx.cocoa.localizability.PluralMisuseChecker
clang-analyzer-alpha.security.ArrayBound
clang-analyzer-alpha.security.ArrayBoundV2
clang-analyzer-alpha.security.MallocOverflow
clang-analyzer-alpha.security.ReturnPtrRange
clang-analyzer-alpha.security.taint.TaintPropagation
clang-analyzer-alpha.unix.Chroot
clang-analyzer-alpha.unix.PthreadLock
clang-analyzer-alpha.unix.SimpleStream
clang-analyzer-alpha.unix.Stream
clang-analyzer-alpha.unix.cstring.BufferOverlap
clang-analyzer-alpha.unix.cstring.NotNullTerminated
clang-analyzer-alpha.unix.cstring.OutOfBounds
clang-analyzer-core.CallAndMessage
clang-analyzer-core.DivideZero
clang-analyzer-core.DynamicTypePropagation
clang-analyzer-core.NonNullParamChecker
clang-analyzer-core.NullDereference
clang-analyzer-core.StackAddressEscape
clang-analyzer-core.UndefinedBinaryOperatorResult
clang-analyzer-core.VLASize
clang-analyzer-core.builtin.BuiltinFunctions
clang-analyzer-core.builtin.NoReturnFunctions
clang-analyzer-core.uninitialized.ArraySubscript
clang-analyzer-core.uninitialized.Assign
clang-analyzer-core.uninitialized.Branch
clang-analyzer-core.uninitialized.CapturedBlockVariable
clang-analyzer-core.uninitialized.UndefReturn
clang-analyzer-cplusplus.NewDelete
clang-analyzer-cplusplus.NewDeleteLeaks
clang-analyzer-deadcode.DeadStores
clang-analyzer-llvm.Conventions
clang-analyzer-nullability.NullPassedToNonnull
clang-analyzer-nullability.NullReturnedFromNonnull
clang-analyzer-nullability.NullableDereferenced
clang-analyzer-nullability.NullablePassedToNonnull
clang-analyzer-nullability.NullablePassedToNonnull
clang-analyzer-optin.mpi.MPI-Checker
clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker
clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker
clang-analyzer-optin.performance.Padding
clang-analyzer-osx.API
clang-analyzer-osx.SecKeychainAPI
clang-analyzer-osx.cocoa.AtSync
clang-analyzer-osx.cocoa.ClassRelease
clang-analyzer-osx.cocoa.Dealloc
clang-analyzer-osx.cocoa.IncompatibleMethodTypes
clang-analyzer-osx.cocoa.Loops
clang-analyzer-osx.cocoa.MissingSuperCall
clang-analyzer-osx.cocoa.NSAutoreleasePool
clang-analyzer-osx.cocoa.NSError
clang-analyzer-osx.cocoa.NilArg
clang-analyzer-osx.cocoa.NonNilReturnValue
clang-analyzer-osx.cocoa.ObjCGenerics
clang-analyzer-osx.cocoa.RetainCount
clang-analyzer-osx.cocoa.SelfInit
clang-analyzer-osx.cocoa.SuperDealloc
clang-analyzer-osx.cocoa.UnusedIvars
clang-analyzer-osx.cocoa.VariadicMethodTypes
clang-analyzer-osx.coreFoundation.CFError
clang-analyzer-osx.coreFoundation.CFNumber
clang-analyzer-osx.coreFoundation.CFRetainRelease
clang-analyzer-osx.coreFoundation.containers.OutOfBounds
clang-analyzer-osx.coreFoundation.containers.PointerSizedValues
clang-analyzer-security.FloatLoopCounter
clang-analyzer-security.insecureAPI.UncheckedReturn
clang-analyzer-security.insecureAPI.getpw
clang-analyzer-security.insecureAPI.gets
clang-analyzer-security.insecureAPI.mkstemp
clang-analyzer-security.insecureAPI.mktemp
clang-analyzer-security.insecureAPI.rand
clang-analyzer-security.insecureAPI.strcpy
clang-analyzer-security.insecureAPI.vfork
clang-analyzer-unix.API
clang-analyzer-unix.Malloc
clang-analyzer-unix.MallocSizeof
clang-analyzer-unix.MismatchedDeallocator
clang-analyzer-unix.Vfork
clang-analyzer-unix.cstring.BadSizeArg
clang-analyzer-unix.cstring.NullArg
cppcoreguidelines-c-copy-assignment-signature
cppcoreguidelines-interfaces-global-init
cppcoreguidelines-pro-bounds-array-to-pointer-decay
cppcoreguidelines-pro-bounds-constant-array-index
cppcoreguidelines-pro-bounds-pointer-arithmetic
cppcoreguidelines-pro-type-const-cast
cppcoreguidelines-pro-type-cstyle-cast
cppcoreguidelines-pro-type-member-init
cppcoreguidelines-pro-type-reinterpret-cast
cppcoreguidelines-pro-type-static-cast-downcast
cppcoreguidelines-pro-type-union-access
cppcoreguidelines-pro-type-vararg
google-build-explicit-make-pair
google-build-namespaces
google-build-using-namespace
google-default-arguments
google-explicit-constructor
google-global-names-in-headers
google-readability-braces-around-statements
google-readability-casting
google-readability-function-size
google-readability-namespace-comments
google-readability-redundant-smartptr-get
google-readability-todo
google-runtime-int
google-runtime-member-string-references
google-runtime-memset
google-runtime-operator
google-runtime-references
llvm-header-guard
llvm-include-order
llvm-namespace-comment
llvm-twine-local
misc-argument-comment
misc-assert-side-effect
misc-bool-pointer-implicit-conversion
misc-dangling-handle
misc-definitions-in-headers
misc-fold-init-type
misc-forward-declaration-namespace
misc-inaccurate-erase
misc-incorrect-roundings
misc-inefficient-algorithm
misc-macro-parentheses
misc-macro-repeated-side-effects
misc-misplaced-const
misc-misplaced-widening-cast
misc-move-const-arg
misc-move-constructor-init
misc-multiple-statement-macro
misc-new-delete-overloads
misc-noexcept-move-constructor
misc-non-copyable-objects
misc-pointer-and-integral-operation
misc-redundant-expression
misc-sizeof-container
misc-sizeof-expression
misc-static-assert
misc-string-constructor
misc-string-integer-assignment
misc-string-literal-with-embedded-nul
misc-suspicious-missing-comma
misc-suspicious-semicolon
misc-suspicious-string-compare
misc-swapped-arguments
misc-throw-by-value-catch-by-reference
misc-unconventional-assign-operator
misc-undelegated-constructor
misc-uniqueptr-reset-release
misc-unused-alias-decls
misc-unused-parameters
misc-unused-raii
misc-unused-using-decls
misc-virtual-near-miss
modernize-avoid-bind
modernize-deprecated-headers
modernize-loop-convert
modernize-make-shared
modernize-make-unique
modernize-pass-by-value
modernize-raw-string-literal
modernize-redundant-void-arg
modernize-replace-auto-ptr
modernize-shrink-to-fit
modernize-use-auto
modernize-use-bool-literals
modernize-use-default
modernize-use-emplace
modernize-use-nullptr
modernize-use-override
modernize-use-using
performance-faster-string-find
performance-for-range-copy
performance-implicit-cast-in-loop
performance-unnecessary-copy-initialization
performance-unnecessary-value-param
readability-avoid-const-params-in-decls
readability-braces-around-statements
readability-container-size-empty
readability-deleted-default
readability-else-after-return
readability-function-size
readability-identifier-naming
readability-implicit-bool-cast
readability-inconsistent-declaration-parameter-name
readability-named-parameter
readability-redundant-control-flow
readability-redundant-smartptr-get
readability-redundant-string-cstr
readability-redundant-string-init
readability-simplify-boolean-expr
readability-static-definition-in-anonymous-namespace
readability-uniqueptr-delete-release


チェックの例

readability-braces-around-statements

http://clang.llvm.org/extra/clang-tidy/checks/readability-braces-around-statements.html

括弧のない if 文などをチェックしてくれます.

筆者は常に括弧つけたい派なので, このオプションを活用していきたいですね.


clang-tidy で自動でソースコードを fix する.

clang-tidy には -fix(及び -fix-error) で, 自動で修正してくれる機能があります.

ただ, どのチェックでも利用可能なわけでないようです.

少なくとも, clang-tidy(clang-tools)のテストコードを見て, CHECK-FIXES があるのは自動で fix してくれると想像できます. 今回は google-readability-casting に注目してみましょう.

https://github.com/llvm-mirror/clang-tools-extra/blob/master/test/clang-tidy/google-readability-casting.cpp

特定のチェックにフォーカスするために, -checks=-*,google-readability-casting とすることで, -* でまずはすべてのチェックを無効にし, その後 google-readability-casting だけを有効にすることができます. カンマで区切ることで複数指定できます. * でワイルドカードも使えます.

$ clang-tidy file.cc -checks=-*,google-readability-casting 

6 warnings generated.
/Users/syoyo/work/tidy-test/file.cc:4:22: warning: redundant cast to the same type [google-readability-casting]
const char *cpc2 = (const char*)cpc;
^
Suppressed 5 warnings (5 with check filters).

$ clang-tidy file.cc -checks=-*,google-readability-casting -fix

6 warnings generated.
/Users/syoyo/work/tidy-test/file.cc:4:22: warning: redundant cast to the same type [google-readability-casting]
const char *cpc2 = (const char*)cpc;
^
/Users/syoyo/work/tidy-test/file.cc:4:22: note: FIX-IT applied suggested code changes
const char *cpc2 = (const char*)cpc;
^
clang-tidy applied 1 of 1 suggested fixes.
Suppressed 5 warnings (5 with check filters).

$ cat file.cc
int main(int argc, char **argv)
{
const char *cpc;
const char *cpc2 = cpc;
short b = argc;
return b;
}

Voala! 冗長なキャストのコードが消えました!

clang-tidy は, ソースコードのフォーマットはしないので, あとで clang-format で整形しておきましょう.

clang-tidy で Happy C/C++ コーディング!


TODO



  • (type)astatic_cast<type>(a) に自動で fix してくれるプラグインを作りたい.

  • 優秀な C/C++ 若人が, clang-tidy(と clang-tools)を極め, 安心安全で平和な C/C++ コーディングを日々切磋琢磨し極めてくれるスキームを確立したい.