diff --git a/.github/workflows/lint-cpp-tidy.yml b/.github/workflows/lint-cpp-tidy.yml new file mode 100644 index 000000000..bad8c7c1c --- /dev/null +++ b/.github/workflows/lint-cpp-tidy.yml @@ -0,0 +1,33 @@ +name: Lint C++ (clang-tidy) + +on: + push: + branches: + - main + paths: + - '.github/workflows/lint-cpp-tidy.yml' + - 'config/.clang-tidy' + - '**/*.h' + - '**/*.hpp' + - '**/*.cpp' + pull_request: + paths: + - '.github/workflows/lint-cpp-tidy.yml' + - 'config/.clang-tidy' + - '**/*.h' + - '**/*.hpp' + - '**/*.cpp' + +jobs: + lint: + name: clang-tidy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: oven-sh/setup-bun@v2 + + - name: Install clang-tidy + run: sudo apt-get update && sudo apt-get install -y clang-tidy + + - name: Run clang-tidy + run: bun run clang-tidy diff --git a/config/.clang-tidy b/config/.clang-tidy new file mode 100644 index 000000000..be2243aaa --- /dev/null +++ b/config/.clang-tidy @@ -0,0 +1,88 @@ +# Config for clang-tidy +# C++20 codebase with heavy template metaprogramming, JSI interop, and Objective-C++ mixing + +Checks: > + -*, + bugprone-*, + -bugprone-easily-swappable-parameters, + -bugprone-exception-escape, + -bugprone-forward-declaration-namespace, + -bugprone-narrowing-conversions, + concurrency-*, + cppcoreguidelines-*, + -cppcoreguidelines-avoid-c-arrays, + -cppcoreguidelines-avoid-const-or-ref-data-members, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-narrowing-conversions, + -cppcoreguidelines-non-private-member-variables-in-classes, + -cppcoreguidelines-owning-memory, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-bounds-constant-array-index, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-type-cstyle-cast, + -cppcoreguidelines-pro-type-reinterpret-cast, + -cppcoreguidelines-pro-type-static-cast-downcast, + -cppcoreguidelines-pro-type-union-access, + -cppcoreguidelines-pro-type-vararg, + -cppcoreguidelines-special-member-functions, + misc-*, + -misc-const-correctness, + -misc-include-cleaner, + -misc-no-recursion, + -misc-non-private-member-variables-in-classes, + -misc-use-anonymous-namespace, + modernize-*, + -modernize-avoid-c-arrays, + -modernize-return-braced-init-list, + -modernize-use-nodiscard, + -modernize-use-trailing-return-type, + performance-*, + readability-*, + -readability-convert-member-functions-to-static, + -readability-else-after-return, + -readability-function-cognitive-complexity, + -readability-identifier-length, + -readability-implicit-bool-conversion, + -readability-magic-numbers, + -readability-named-parameter, + -readability-qualified-auto, + -readability-redundant-access-specifiers, + -readability-uppercase-literal-suffix, + +WarningsAsErrors: '' + +HeaderFilterRegex: '.*\.(h|hpp)$' + +CheckOptions: + - key: readability-identifier-naming.NamespaceCase + value: lower_case + - key: readability-identifier-naming.ClassCase + value: CamelCase + - key: readability-identifier-naming.StructCase + value: CamelCase + - key: readability-identifier-naming.FunctionCase + value: camelBack + - key: readability-identifier-naming.MethodCase + value: camelBack + - key: readability-identifier-naming.VariableCase + value: camelBack + - key: readability-identifier-naming.ParameterCase + value: camelBack + - key: readability-identifier-naming.MemberCase + value: camelBack + - key: readability-identifier-naming.PrivateMemberPrefix + value: '_' + - key: readability-identifier-naming.ConstexprVariableCase + value: CamelCase + - key: readability-identifier-naming.EnumConstantCase + value: CamelCase + - key: readability-identifier-naming.TemplateParameterCase + value: CamelCase + - key: readability-braces-around-statements.ShortStatementLines + value: 1 + - key: performance-unnecessary-value-param.AllowedTypes + value: 'shared_ptr;unique_ptr;jsi::Value;jsi::Object;jsi::String;jsi::Function' + - key: misc-unused-parameters.StrictMode + value: false diff --git a/package.json b/package.json index 91e614d87..e402089a6 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "typecheck": "bun --filter=\"**\" typecheck", "lint": "bun nitro lint && bun nitrogen lint && bun nitro-test lint && bun example lint", "lint-cpp": "./scripts/clang-format.sh", + "clang-tidy": "./scripts/clang-tidy.sh", "lint-swift": "./scripts/swift-format.sh", "lint-kotlin": "./scripts/kotlin-format.sh", "lint-all": "bun lint-cpp && bun lint-swift && bun lint-kotlin && bun lint", diff --git a/scripts/clang-tidy.sh b/scripts/clang-tidy.sh new file mode 100755 index 000000000..00540f5e4 --- /dev/null +++ b/scripts/clang-tidy.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +set -e + +NITRO_CPP="packages/react-native-nitro-modules/cpp" + +CPP_DIRS=( + # react-native-nitro-modules + "$NITRO_CPP" + # react-native-nitro-test + "packages/react-native-nitro-test/cpp" +) + +# Include paths needed to resolve all #includes. +# In a real build (CocoaPods/CMake) these are set by the build system, +# but for standalone clang-tidy we need to supply them manually. +INCLUDE_DIRS=( + "$NITRO_CPP/core" + "$NITRO_CPP/entrypoint" + "$NITRO_CPP/jsi" + "$NITRO_CPP/platform" + "$NITRO_CPP/prototype" + "$NITRO_CPP/registry" + "$NITRO_CPP/templates" + "$NITRO_CPP/threading" + "$NITRO_CPP/utils" + "$NITRO_CPP/views" + # JSI headers from react-native + "node_modules/react-native/ReactCommon/jsi" +) + +INCLUDE_FLAGS="" +for dir in "${INCLUDE_DIRS[@]}"; do + INCLUDE_FLAGS="$INCLUDE_FLAGS -I$dir" +done + +if which clang-tidy >/dev/null; then + DIRS=$(printf "%s " "${CPP_DIRS[@]}") + FILES=$(find $DIRS -type f \( -name "*.h" -o -name "*.hpp" -o -name "*.cpp" \)) + + if [ -z "$FILES" ]; then + echo "No C++ files found!" + exit 1 + fi + + echo "Running clang-tidy on $(echo "$FILES" | wc -l | tr -d ' ') files..." + + FAILED=0 + while IFS= read -r file; do + if ! clang-tidy --config-file=./config/.clang-tidy -p=. --quiet "$file" -- -std=c++20 -x c++ $INCLUDE_FLAGS 2>/dev/null; then + FAILED=1 + fi + done <<< "$FILES" + + if [ $FAILED -ne 0 ]; then + echo "clang-tidy found issues!" + exit 1 + fi + + echo "clang-tidy done!" +else + echo "error: clang-tidy not installed, install with 'brew install llvm' (or manually from https://clang.llvm.org/extra/clang-tidy/ )" + exit 1 +fi