LoginSignup
67
56

More than 1 year has passed since last update.

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

Last updated at Posted at 2017-01-06

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

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

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

tidy(タイディ) とは

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

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

準備する.

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

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

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

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

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

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

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

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

特定のチェックにフォーカスするために, -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++ コーディングを日々切磋琢磨し極めてくれるスキームを確立したい.
67
56
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
67
56