diff --git a/CMakeLists.txt b/CMakeLists.txt index 2de5fe7c3..7e7facf29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,7 @@ option(SOURCEMETA_CORE_TESTS "Build the Sourcemeta Core tests" OFF) option(SOURCEMETA_CORE_TESTS_CI "Build the Sourcemeta Core CI tests" OFF) option(SOURCEMETA_CORE_BENCHMARK "Build the Sourcemeta Core benchmarks" OFF) option(SOURCEMETA_CORE_DOCS "Build the Sourcemeta Core docs" OFF) +option(SOURCEMETA_CORE_CLANG_TIDY_ANALYZER "Run the Clang static analyzer alongside ClangTidy" OFF) option(SOURCEMETA_CORE_INSTALL "Install the Sourcemeta Core library" ON) option(SOURCEMETA_CORE_ADDRESS_SANITIZER "Build Sourcemeta Core with an address sanitizer" OFF) option(SOURCEMETA_CORE_UNDEFINED_SANITIZER "Build Sourcemeta Core with an undefined behavior sanitizer" OFF) diff --git a/cmake/common/clang-tidy-test.json b/cmake/common/clang-tidy-test.json new file mode 100644 index 000000000..8a6a0559a --- /dev/null +++ b/cmake/common/clang-tidy-test.json @@ -0,0 +1,41 @@ +{ + "Checks": "-*,bugprone-*,cert-*,concurrency-*,cppcoreguidelines-*,google-*,hicpp-*,misc-*,modernize-*,performance-*,portability-*,readability-*,llvm-namespace-comment,-readability-avoid-const-params-in-decls,-llvm-prefer-static-over-anonymous-namespace,-hicpp-multiway-paths-covered,-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-hicpp-no-array-decay,-cppcoreguidelines-avoid-goto,-hicpp-avoid-goto,-readability-magic-numbers,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-macro-usage,-misc-no-recursion,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-vararg,-hicpp-vararg,-cppcoreguidelines-owning-memory,-misc-non-private-member-variables-in-classes,-cppcoreguidelines-non-private-member-variables-in-classes,-llvm-header-guard,-readability-use-anyofallof,-readability-use-concise-preprocessor-directives,-hicpp-signed-bitwise,-bugprone-easily-swappable-parameters,-google-objc-*,-google-readability-todo,-misc-include-cleaner,-modernize-use-std-numbers,-bugprone-exception-escape,-bugprone-macro-parentheses,-bugprone-unchecked-optional-access,-cert-dcl59-cpp,-cert-err33-c,-concurrency-mt-unsafe,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-avoid-const-or-ref-data-members,-cppcoreguidelines-avoid-non-const-global-variables,-cppcoreguidelines-init-variables,-cppcoreguidelines-no-malloc,-cppcoreguidelines-prefer-member-initializer,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-member-init,-cppcoreguidelines-rvalue-reference-param-not-moved,-cppcoreguidelines-special-member-functions,-google-build-namespaces,-google-build-using-namespace,-google-explicit-constructor,-google-runtime-int,-hicpp-avoid-c-arrays,-hicpp-exception-baseclass,-hicpp-explicit-conversions,-hicpp-invalid-access-moved,-hicpp-member-init,-hicpp-no-malloc,-hicpp-special-member-functions,-hicpp-uppercase-literal-suffix,-misc-anonymous-namespace-in-header,-misc-const-correctness,-misc-definitions-in-headers,-misc-misplaced-const,-misc-multiple-inheritance,-misc-redundant-expression,-misc-use-anonymous-namespace,-misc-use-internal-linkage,-modernize-use-ranges,-modernize-use-trailing-return-type,-performance-no-automatic-move,-readability-avoid-nested-conditional-operator,-readability-container-contains,-readability-convert-member-functions-to-static,-readability-function-cognitive-complexity,-readability-redundant-casting,-readability-redundant-member-init,-readability-redundant-string-init,-readability-simplify-boolean-expr,-readability-static-accessed-through-instance,-readability-suspicious-call-argument,-readability-uppercase-literal-suffix,-bugprone-empty-catch,-bugprone-implicit-widening-of-multiplication-result,-bugprone-random-generator-seed,-bugprone-signed-char-misuse,-bugprone-string-constructor,-bugprone-string-literal-with-embedded-nul,-bugprone-throwing-static-initialization,-bugprone-unchecked-string-to-number-conversion,-bugprone-unused-raii,-cert-err09-cpp,-cert-err34-c,-cert-err58-cpp,-cert-err61-cpp,-cert-msc32-c,-cert-msc51-cpp,-cppcoreguidelines-avoid-do-while,-google-readability-avoid-underscore-in-googletest-name,-misc-throw-by-value-catch-by-reference,-modernize-avoid-c-arrays,-modernize-raw-string-literal,-modernize-type-traits,-modernize-use-designated-initializers,-modernize-use-scoped-lock,-performance-enum-size,-performance-inefficient-string-concatenation,-performance-inefficient-vector-operation,-performance-unnecessary-copy-initialization,-performance-unnecessary-value-param,-readability-identifier-length,-readability-implicit-bool-conversion,-readability-math-missing-parentheses,-readability-qualified-auto,-readability-redundant-string-cstr", + "WarningsAsErrors": "*", + "FormatStyle": "none", + "UseColor": true, + "CheckOptions": { + "portability-restrict-system-includes.Includes": "*,-expected", + "readability-function-cognitive-complexity.Threshold": "100", + "readability-identifier-length.IgnoredVariableNames": "^([a-h]|[x-z]|r|s)$", + "readability-identifier-length.IgnoredParameterNames": "^(iv|n)$", + "readability-identifier-naming.NamespaceCase": "lower_case", + "readability-identifier-naming.ClassCase": "CamelCase", + "readability-identifier-naming.ClassIgnoredRegexp": "^const_iterator$", + "readability-identifier-naming.StructCase": "CamelCase", + "readability-identifier-naming.UnionCase": "CamelCase", + "readability-identifier-naming.EnumCase": "CamelCase", + "readability-identifier-naming.EnumConstantCase": "CamelCase", + "readability-identifier-naming.EnumConstantIgnoredRegexp": "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$", + "readability-identifier-naming.TypeAliasCase": "CamelCase", + "readability-identifier-naming.TypeAliasIgnoredRegexp": "^[a-z][a-z0-9_]*$", + "readability-identifier-naming.TypedefCase": "CamelCase", + "readability-identifier-naming.TypeTemplateParameterCase": "CamelCase", + "readability-identifier-naming.ValueTemplateParameterCase": "CamelCase", + "readability-identifier-naming.FunctionCase": "lower_case", + "readability-identifier-naming.VariableCase": "lower_case", + "readability-identifier-naming.ParameterCase": "lower_case", + "readability-identifier-naming.PublicMemberCase": "lower_case", + "readability-identifier-naming.PrivateMemberCase": "lower_case", + "readability-identifier-naming.PrivateMemberSuffix": "_", + "readability-identifier-naming.ProtectedMemberCase": "lower_case", + "readability-identifier-naming.ProtectedMemberSuffix": "_", + "readability-identifier-naming.GlobalConstantCase": "UPPER_CASE", + "readability-identifier-naming.StaticConstantCase": "UPPER_CASE", + "readability-identifier-naming.ClassConstantCase": "UPPER_CASE", + "readability-identifier-naming.ConstexprVariableCase": "UPPER_CASE", + "readability-identifier-naming.LocalConstantCase": "lower_case", + "readability-identifier-naming.LocalVariableCase": "lower_case", + "readability-identifier-naming.MacroDefinitionCase": "UPPER_CASE", + "readability-identifier-naming.MacroDefinitionIgnoredRegexp": "^[A-Z][A-Z0-9_]*_$" + } +} \ No newline at end of file diff --git a/cmake/common/clang-tidy.cmake b/cmake/common/clang-tidy.cmake index f9d78a731..2a333ea43 100644 --- a/cmake/common/clang-tidy.cmake +++ b/cmake/common/clang-tidy.cmake @@ -86,7 +86,7 @@ function(sourcemeta_clang_tidy_attempt_install) endfunction() function(sourcemeta_clang_tidy_attempt_enable) - cmake_parse_arguments(SOURCEMETA_TARGET_CLANG_TIDY_ATTEMPT_ENABLE "" "TARGET" "" ${ARGN}) + cmake_parse_arguments(SOURCEMETA_TARGET_CLANG_TIDY_ATTEMPT_ENABLE "" "TARGET;CONFIG" "" ${ARGN}) if(NOT SOURCEMETA_TARGET_CLANG_TIDY_ATTEMPT_ENABLE_TARGET) message(FATAL_ERROR "You must pass the target name using the TARGET option") endif() @@ -111,13 +111,34 @@ function(sourcemeta_clang_tidy_attempt_enable) set(CLANG_TIDY_CONFIG "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/clang-tidy.json") execute_process(COMMAND xcrun --show-sdk-path OUTPUT_VARIABLE MACOSX_SDK_PATH OUTPUT_STRIP_TRAILING_WHITESPACE) + + # The static analyzer roughly triples ClangTidy time per translation unit, + # so it stays out of the local edit loop. The `--checks` argument is + # appended to the `Checks` option of the configuration file rather than + # replacing it, so the group composes with whatever the file enables + set(CLANG_TIDY_EXTRA_CHECKS "") + if(SOURCEMETA_CORE_CLANG_TIDY_ANALYZER) + set(CLANG_TIDY_EXTRA_CHECKS "--checks=clang-analyzer-*") + endif() + set(SOURCEMETA_CXX_CLANG_TIDY "${CLANG_TIDY_BIN};--config-file=${CLANG_TIDY_CONFIG};-header-filter=${PROJECT_SOURCE_DIR}/src/*" + ${CLANG_TIDY_EXTRA_CHECKS} "--extra-arg=-isysroot" "--extra-arg=${MACOSX_SDK_PATH}" CACHE STRING "CXX_CLANG_TIDY") endif() + # Test code legitimately relies on idioms that library code must not, such as + # the static registration and do-while expansions of the test macros, so a + # target may ask for a configuration of its own + set(TARGET_CLANG_TIDY "${SOURCEMETA_CXX_CLANG_TIDY}") + if(SOURCEMETA_TARGET_CLANG_TIDY_ATTEMPT_ENABLE_CONFIG) + list(TRANSFORM TARGET_CLANG_TIDY REPLACE + "^--config-file=.*$" + "--config-file=${CMAKE_CURRENT_FUNCTION_LIST_DIR}/${SOURCEMETA_TARGET_CLANG_TIDY_ATTEMPT_ENABLE_CONFIG}") + endif() + set_target_properties("${SOURCEMETA_TARGET_CLANG_TIDY_ATTEMPT_ENABLE_TARGET}" - PROPERTIES CXX_CLANG_TIDY "${SOURCEMETA_CXX_CLANG_TIDY}") + PROPERTIES CXX_CLANG_TIDY "${TARGET_CLANG_TIDY}") endfunction() diff --git a/cmake/common/clang-tidy.json b/cmake/common/clang-tidy.json index c9e314e3b..0d46504d5 100644 --- a/cmake/common/clang-tidy.json +++ b/cmake/common/clang-tidy.json @@ -1,7 +1,41 @@ { - "Checks": "-*, bugprone-*, -bugprone-easily-swappable-parameters,-bugprone-unchecked-optional-access, concurrency-*,cppcoreguidelines-missing-std-forward, - cppcoreguidelines-avoid-const-or-ref-data-members, modernize-*, performance-*, portability-*", + "Checks": "-*,bugprone-*,cert-*,concurrency-*,cppcoreguidelines-*,google-*,hicpp-*,misc-*,modernize-*,performance-*,portability-*,readability-*,llvm-namespace-comment,-readability-avoid-const-params-in-decls,-llvm-prefer-static-over-anonymous-namespace,-hicpp-multiway-paths-covered,-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-hicpp-no-array-decay,-cppcoreguidelines-avoid-goto,-hicpp-avoid-goto,-readability-magic-numbers,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-macro-usage,-misc-no-recursion,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-vararg,-hicpp-vararg,-cppcoreguidelines-owning-memory,-misc-non-private-member-variables-in-classes,-cppcoreguidelines-non-private-member-variables-in-classes,-llvm-header-guard,-readability-use-anyofallof,-readability-use-concise-preprocessor-directives,-hicpp-signed-bitwise,-bugprone-easily-swappable-parameters,-google-objc-*,-google-readability-todo,-misc-include-cleaner,-modernize-use-std-numbers,-bugprone-exception-escape,-bugprone-macro-parentheses,-bugprone-unchecked-optional-access,-cert-dcl59-cpp,-cert-err33-c,-concurrency-mt-unsafe,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-avoid-const-or-ref-data-members,-cppcoreguidelines-avoid-non-const-global-variables,-cppcoreguidelines-init-variables,-cppcoreguidelines-no-malloc,-cppcoreguidelines-prefer-member-initializer,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-member-init,-cppcoreguidelines-rvalue-reference-param-not-moved,-cppcoreguidelines-special-member-functions,-google-build-namespaces,-google-build-using-namespace,-google-explicit-constructor,-google-runtime-int,-hicpp-avoid-c-arrays,-hicpp-exception-baseclass,-hicpp-explicit-conversions,-hicpp-invalid-access-moved,-hicpp-member-init,-hicpp-no-malloc,-hicpp-special-member-functions,-hicpp-uppercase-literal-suffix,-misc-anonymous-namespace-in-header,-misc-const-correctness,-misc-definitions-in-headers,-misc-misplaced-const,-misc-multiple-inheritance,-misc-redundant-expression,-misc-use-anonymous-namespace,-misc-use-internal-linkage,-modernize-use-ranges,-modernize-use-trailing-return-type,-performance-no-automatic-move,-readability-avoid-nested-conditional-operator,-readability-container-contains,-readability-convert-member-functions-to-static,-readability-function-cognitive-complexity,-readability-redundant-casting,-readability-redundant-member-init,-readability-redundant-string-init,-readability-simplify-boolean-expr,-readability-static-accessed-through-instance,-readability-suspicious-call-argument,-readability-uppercase-literal-suffix", "WarningsAsErrors": "*", "FormatStyle": "none", - "UseColor": true -} + "UseColor": true, + "CheckOptions": { + "portability-restrict-system-includes.Includes": "*,-expected", + "readability-function-cognitive-complexity.Threshold": "100", + "readability-identifier-length.IgnoredVariableNames": "^([a-h]|[x-z]|r|s)$", + "readability-identifier-length.IgnoredParameterNames": "^(iv|n)$", + "readability-identifier-naming.NamespaceCase": "lower_case", + "readability-identifier-naming.ClassCase": "CamelCase", + "readability-identifier-naming.ClassIgnoredRegexp": "^const_iterator$", + "readability-identifier-naming.StructCase": "CamelCase", + "readability-identifier-naming.UnionCase": "CamelCase", + "readability-identifier-naming.EnumCase": "CamelCase", + "readability-identifier-naming.EnumConstantCase": "CamelCase", + "readability-identifier-naming.EnumConstantIgnoredRegexp": "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$", + "readability-identifier-naming.TypeAliasCase": "CamelCase", + "readability-identifier-naming.TypeAliasIgnoredRegexp": "^[a-z][a-z0-9_]*$", + "readability-identifier-naming.TypedefCase": "CamelCase", + "readability-identifier-naming.TypeTemplateParameterCase": "CamelCase", + "readability-identifier-naming.ValueTemplateParameterCase": "CamelCase", + "readability-identifier-naming.FunctionCase": "lower_case", + "readability-identifier-naming.VariableCase": "lower_case", + "readability-identifier-naming.ParameterCase": "lower_case", + "readability-identifier-naming.PublicMemberCase": "lower_case", + "readability-identifier-naming.PrivateMemberCase": "lower_case", + "readability-identifier-naming.PrivateMemberSuffix": "_", + "readability-identifier-naming.ProtectedMemberCase": "lower_case", + "readability-identifier-naming.ProtectedMemberSuffix": "_", + "readability-identifier-naming.GlobalConstantCase": "UPPER_CASE", + "readability-identifier-naming.StaticConstantCase": "UPPER_CASE", + "readability-identifier-naming.ClassConstantCase": "UPPER_CASE", + "readability-identifier-naming.ConstexprVariableCase": "UPPER_CASE", + "readability-identifier-naming.LocalConstantCase": "lower_case", + "readability-identifier-naming.LocalVariableCase": "lower_case", + "readability-identifier-naming.MacroDefinitionCase": "UPPER_CASE", + "readability-identifier-naming.MacroDefinitionIgnoredRegexp": "^[A-Z][A-Z0-9_]*_$" + } +} \ No newline at end of file diff --git a/cmake/common/targets/executable.cmake b/cmake/common/targets/executable.cmake index 84814a5b6..1015cbde6 100644 --- a/cmake/common/targets/executable.cmake +++ b/cmake/common/targets/executable.cmake @@ -71,4 +71,9 @@ function(sourcemeta_executable) endif() set_target_properties("${TARGET_NAME}" PROPERTIES FOLDER "${FOLDER_NAME}") + + # We don't want consumers to be bothered with this + if(PROJECT_IS_TOP_LEVEL) + sourcemeta_clang_tidy_attempt_enable(TARGET "${TARGET_NAME}") + endif() endfunction() diff --git a/cmake/common/targets/test.cmake b/cmake/common/targets/test.cmake index 542ae97e4..1c131b18a 100644 --- a/cmake/common/targets/test.cmake +++ b/cmake/common/targets/test.cmake @@ -16,6 +16,11 @@ function(sourcemeta_test) SOURCES "${SOURCEMETA_TEST_SOURCES}" OUTPUT TARGET_NAME) + if(PROJECT_IS_TOP_LEVEL) + sourcemeta_clang_tidy_attempt_enable(TARGET "${TARGET_NAME}" + CONFIG "clang-tidy-test.json") + endif() + target_link_libraries("${TARGET_NAME}" PRIVATE sourcemeta::core::test) # Provides a default entry point through static archive resolution unless the