-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathClangTidy.cmake
363 lines (335 loc) · 15.6 KB
/
ClangTidy.cmake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
#
# Copyright (C) 2021 Swift Navigation Inc.
# Contact: Swift Navigation <[email protected]>
#
# This source is subject to the license found in the file 'LICENSE' which must
# be be distributed together with this source. All other rights reserved.
#
# THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
#
#
# A module to create custom targets to lint source files using clang-tidy
#
# Call swift_create_clang_tidy_targets at the end of the top level
# CMakeLists.txt to create targets for running clang-tidy
#
# clang-tidy version 6 must be available on the system for this module to work
# properly. If an appropriate clang-tidy can't be found no targets will be
# created and a warning will be logged
#
# swift_create_clang_tidy_targets will only have an effect for top level
# projects. If called within a subproject it will return without taking any
# action
#
# For every compilable target defined within the calling repository two targets
# will be created.
#
# The first target 'clang-tidy-${target}' will invoke clang-tidy for all source
# files which make up the given target. It will export fixes to a file called
# 'fixes-${target}.yaml' in the top level project source directory.
#
# The second target 'clang-tidy-${target}-check' will run clang-tidy as the
# target described above and then return an error code if any warning/errors
# were generated
#
# If the variable SWIFT_CLANG_TIDY_RATCHET_FILE is defined, then a
# third target will be created: 'clang-tidy-${target}-ratchet-check'.
# This target pipes the output of clang-tidy through
# scripts/clang_tidy_ratchet.py and compares the output to the check
# counts supplied in the filename in SWIFT_CLANG_TIDY_RATCHET_FILE,
# returning failure only if the ratchet check script fails. This
# feature can be used to (approximately) mask existing clang-tidy
# warnings without fixing them, so that development going forward
# can still benefit from clang-tidy checking
#
# In addition there are two other targets created which lint multiple targets
# at the same time
#
# clang-tidy-all runs clang-tidy on all "core" targets in the repository
# (targets which were added with swift_add_executable or swift_add_library)
#
# clang-tidy-world runs clang-tidy on all compilable targets in the repository
# including all test suites
#
# clang-tidy-all and clang-tidy-world each have a "check" variant which returns
# an error code should any warning/errors be generated
#
# swift_create_clang_tidy_targets will generate a .clang-tidy file in the
# project source directory which contains the Swift master config for
# clang-tidy. There is no need for repositories to maintain their own version
# of .clang-tidy, it should be added to .gitignore in each repository to
# prevent being checked in. Pass the option DONT_GENERATE_CLANG_TIDY_CONFIG to
# disable the autogenerated config
#
# In addition this function sets up a cmake option which can be used to control
# whether the targets are created either on the command line or by a super project.
# The option has the name
#
# ${PROJECT_NAME}_ENABLE_CLANG_TIDY
#
# The default value is ON for top level projects, and OFF for any others.
#
# Running
#
# cmake -D<project>_ENABLE_CLANG_TIDY=OFF ..
#
# will explicitly disable these targets from the command line at configure time
#
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Export compile commands" FORCE)
# Helper function to actually create the targets, not to be used outside this file
function(create_clang_tidy_targets key fixes)
add_custom_target(
clang-tidy-${key}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/scripts/run-clang-tidy.py -clang-tidy-binary ${CLANG_TIDY} -p ${CMAKE_BINARY_DIR} -export-fixes=${CMAKE_SOURCE_DIR}/${fixes}
${ARGN}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_custom_target(
clang-tidy-${key}-check
COMMAND test ! -f ${CMAKE_SOURCE_DIR}/${fixes}
DEPENDS clang-tidy-${key}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
if(DEFINED SWIFT_CLANG_TIDY_RATCHET_FILE)
add_custom_target(
clang-tidy-${key}-ratchet-check
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/scripts/run-clang-tidy.py -clang-tidy-binary ${CLANG_TIDY} -p ${CMAKE_BINARY_DIR}
${ARGN} 2>/dev/null | ${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/scripts/clang_tidy_ratchet.py --reference ${SWIFT_CLANG_TIDY_RATCHET_FILE}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
endif()
endfunction()
macro(early_exit level msg)
message(${level} "${msg}")
return()
endmacro()
function(swift_create_clang_tidy_targets)
if(NOT ${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME})
return()
endif()
set(argOption "DONT_GENERATE_CLANG_TIDY_CONFIG" "STYLE_GUIDE" "WITHOUT_SWIFT_TYPES")
set(argSingle "")
set(argMulti "FLAGS_TO_ENABLE")
cmake_parse_arguments(x "${argOption}" "${argSingle}" "${argMulti}" ${ARGN})
if(x_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unparsed arguments to swift_create_clang_tidy_targets ${ARGN}")
endif()
# Global clang-tidy enable option, influences the default project specific enable option
option(ENABLE_CLANG_TIDY "Enable auto-linting of code using clang-tidy globally" ON)
if(NOT ENABLE_CLANG_TIDY)
early_exit(STATUS "clang-tidy is disabled globally")
endif()
# Create a cmake option to enable linting of this specific project
option(${PROJECT_NAME}_ENABLE_CLANG_TIDY "Enable auto-linting of code using clang-tidy for project" ON)
if(NOT ${PROJECT_NAME}_ENABLE_CLANG_TIDY)
early_exit(STATUS "${PROJECT_NAME} clang-tidy support is DISABLED")
endif()
find_program(CLANG_TIDY NAMES clang-tidy-14 clang-tidy)
if("${CLANG_TIDY}" STREQUAL "CLANG_TIDY-NOTFOUND")
message(WARNING "Could not find clang-tidy, link targets will not be created")
return()
endif()
if(NOT x_DONT_GENERATE_CLANG_TIDY_CONFIG)
# By default all clang-tidy are disabled. The following set of suites will be enabled in their entirety
set(enabled_categories
# bugprone could probably do with being turned on
# bugprone*
cert*
clang-analyzer*
cppcoreguidelines*
google*
misc*
modernize*
performance*
readability*)
# The set of specific tests which will be disabled. All checks in this list should have a reason for being disabled placed in a comment along side. Use wildcards with care, in
# general try to disabled the minimum set of checks required and provide a reason for doing so.
set(disabled_checks
# Don't need OSX checks
-clang-analyzer-osx*
-clang-analyzer-optin.osx.*
# Test suites aren't linted
-clang-analyzer-apiModeling.google.GTest
# Don't care about LLVM conventions
-clang-analyzer-llvm.Conventions
# Function size is not enforced through clang-tidy, sonar cloud has its own check
-readability-function-size
# No using MPI
-clang-analyzer-optin.mpi*
# No ObjC code anywhere
-google-objc*
# clang-format takes care of indentation
-readability-misleading-indentation
# Caught by compiler, -Wunused-parameter
-misc-unused-parameters
# We have a external function blacklist which is much faster, don't need clang to do it
-clang-analyzer-security.insecureAPI*
# All the following checks were disabled when the CI project started. They are left like this to avoid having to make too many code changes. This should not be taken as an
# endorsement of anything.
-cert-dcl03-c
-cert-dcl21-cpp
-cert-err34-c
-cert-err58-cpp
-clang-analyzer-alpha*
-clang-analyzer-core.CallAndMessage
-clang-analyzer-core.UndefinedBinaryOperatorResult
-clang-analyzer-core.uninitialized.Assign
-clang-analyzer-core.uninitialized.UndefReturn
-clang-analyzer-optin.cplusplus.VirtualCall
-clang-analyzer-optin.performance.Padding
-cppcoreguidelines-owning-memory
-cppcoreguidelines-pro-bounds-array-to-pointer-decay
-cppcoreguidelines-pro-bounds-constant-array-index
-cppcoreguidelines-pro-bounds-pointer-arithmetic
-cppcoreguidelines-pro-type-member-init
-cppcoreguidelines-pro-type-static-cast-downcast
-cppcoreguidelines-pro-type-union-access
-cppcoreguidelines-pro-type-vararg
-cppcoreguidelines-special-member-functions
-google-runtime-references
-misc-static-assert
-modernize-deprecated-headers
-modernize-pass-by-value
-modernize-redundant-void-arg
-modernize-return-braced-init-list
-modernize-use-auto
-modernize-use-bool-literals
-modernize-use-default-member-init
-modernize-use-emplace
-modernize-use-equals-default
-modernize-use-equals-delete
-modernize-use-using
-performance-unnecessary-value-param
-readability-avoid-const-params-in-decls
-readability-non-const-parameter
-readability-redundant-declaration
-readability-redundant-member-init
# Disable all new checks introduced between clang-6 and clang-14
-clang-analyzer-core.StackAddressEscape
-clang-analyzer-core.VLASize
-clang-analyzer-cplusplus.NewDeleteLeaks
-clang-analyzer-deadcode.DeadStores
-clang-analyzer-optin.cplusplus.UninitializedObject
-cert-err33-c
-cert-exp42-c
-cert-dcl37-c
-cert-dcl51-cpp
-cert-flp37-c
-cert-oop54-cpp
-cert-str34-c
-cppcoreguidelines-avoid-c-arrays
-cppcoreguidelines-avoid-goto
-cppcoreguidelines-avoid-magic-numbers
-cppcoreguidelines-avoid-non-const-global-variables
-cppcoreguidelines-init-variables
-cppcoreguidelines-macro-usage
-cppcoreguidelines-narrowing-conversions
-cppcoreguidelines-non-private-member-variables-in-classes
-cppcoreguidelines-prefer-member-initializer
-cppcoreguidelines-virtual-class-destructor
-misc-non-private-member-variables-in-classes
-misc-no-recursion
-modernize-avoid-c-arrays
-modernize-use-trailing-return-type
-performance-no-int-to-ptr
-readability-const-return-type
-readability-container-data-pointer
-readability-convert-member-functions-to-static
-readability-duplicate-include
-readability-function-cognitive-complexity
-readability-identifier-length
-readability-isolate-declaration
-readability-make-member-function-const
-readability-magic-numbers
-readability-qualified-auto
-readability-redundant-access-specifiers
-readability-suspicious-call-argument
-readability-uppercase-literal-suffix
-readability-use-anyofallof)
if (NOT x_STYLE_GUIDE)
list(APPEND disabled_checks -readability-identifier-naming)
endif()
foreach(flag IN LISTS x_FLAGS_TO_ENABLE)
list(FILTER disabled_checks EXCLUDE REGEX ${flag})
endforeach()
# Final list of checks to enable/disable
set(all_checks -* ${enabled_categories} ${disabled_checks})
# .clang-tidy needs a comma separated list of checks but cmake uses a semicolon as the list separator
string(REPLACE ";" "," comma_checks "${all_checks}")
# Generate .clang-tidy in project root dir
file(WRITE ${CMAKE_SOURCE_DIR}/.clang-tidy "\
# Automatically generated, do not edit
# Enabled checks are generated from SwiftClangTidy.cmake
Checks: \"${comma_checks}\"
HeaderFilterRegex: '.*'
AnalyzeTemporaryDtors: true
")
if (x_STYLE_GUIDE)
file (APPEND ${CMAKE_SOURCE_DIR}/.clang-tidy "\
CheckOptions:
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
- { key: readability-identifier-naming.ClassMemberCase, value: lower_case }
- { key: readability-identifier-naming.ClassMemberSuffix, value: _ }
- { key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase }
- { key: readability-identifier-naming.ConstexprVariablePrefix, value: c }
- { key: readability-identifier-naming.EnumCase, value: CamelCase }
- { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE }
- { key: readability-identifier-naming.FunctionCase, value: lower_case }
- { key: readability-identifier-naming.GlobalConstantCase, value: CamelCase }
- { key: readability-identifier-naming.GlobalConstantPrefix, value: c }
- { key: readability-identifier-naming.MemberConstantCase, value: CamelCase }
- { key: readability-identifier-naming.MemberConstantPrefix, value: c }
- { key: readability-identifier-naming.NamespaceCase, value: lower_case }
- { key: readability-identifier-naming.PrivateMemberSuffix, value: _ }
- { key: readability-identifier-naming.ProtectedMemberSuffix, value: _ }
- { key: readability-identifier-naming.StaticConstantCase, value: CamelCase }
- { key: readability-identifier-naming.StaticConstantPrefix, value: c }
- { key: readability-identifier-naming.StructCase, value: CamelCase }
- { key: readability-identifier-naming.TemplateParameterCase, value: CamelCase }
- { key: readability-identifier-naming.VariableCase, value: lower_case }
- { key: readability-identifier-naming.TemplateParameterIgnoredRegexp, value: 'expr-type'}
")
endif ()
endif()
# These two lists will ultimately contain the complete set of source files to pass to the clang-tidy-all and clang-tidy-world targets
unset(all_abs_srcs)
unset(world_abs_srcs)
# Only lint targets created in this repository. Later on we will create 2 targets: clang-tidy-all will lint all "core" targets, executables and libraries clang-tidy-world will
# lint everything including test suites
if (x_WITHOUT_SWIFT_TYPES)
swift_list_compilable_targets(all_targets ONLY_THIS_REPO)
else()
swift_list_compilable_targets(all_targets ONLY_THIS_REPO SWIFT_TYPES "executable" "library")
endif()
swift_list_compilable_targets(world_targets ONLY_THIS_REPO)
foreach(target IN LISTS world_targets)
# Build up a list of source files to pass to clang-tidy
get_target_property(target_srcs ${target} SOURCES)
get_target_property(target_dir ${target} SOURCE_DIR)
unset(abs_srcs)
foreach(file ${target_srcs})
get_filename_component(abs_file ${file} ABSOLUTE BASE_DIR ${target_dir})
list(APPEND abs_srcs ${abs_file})
endforeach()
create_clang_tidy_targets(${target} fixes-${target}.yaml ${abs_srcs})
# All targets are included in the world target
list(APPEND world_abs_srcs ${abs_srcs})
if(${target} IN_LIST all_targets)
# Only "core" executables and libraries are included in the all target
list(APPEND all_abs_srcs ${abs_srcs})
endif()
endforeach()
if(NOT all_abs_srcs)
message(WARNING "No sources to lint for clang-tidy-all, that doesn't sound right")
else()
list(REMOVE_DUPLICATES all_abs_srcs)
list(FILTER all_abs_srcs EXCLUDE REGEX "pb.cc")
create_clang_tidy_targets(all fixes.yaml ${all_abs_srcs})
create_clang_tidy_targets(diff fixes.yaml `git diff --diff-filter=ACMRTUXB --name-only master -- ${all_abs_srcs}`)
endif()
if(NOT world_abs_srcs)
message(WARNING "No sources to lint for clang-tidy-world, that doesn't sound right")
else()
list(REMOVE_DUPLICATES world_abs_srcs)
create_clang_tidy_targets(world fixes.yaml ${world_abs_srcs})
endif()
endfunction()