SDL: Add clang-tidy config and CMake/CI support.

From fa8fba3812a03bb08a9eeaefc82aa140baa47bbb Mon Sep 17 00:00:00 2001
From: Pierre Wendling <[EMAIL REDACTED]>
Date: Tue, 25 Oct 2022 23:26:42 -0400
Subject: [PATCH] Add clang-tidy config and CMake/CI support.

---
 .clang-tidy                      |  58 +++++++++++++++
 .github/workflows/android.yml    |   1 +
 .github/workflows/main.yml       |   9 ++-
 CMakeLists.txt                   |  24 +++++++
 cmake/3rdparty.cmake             | 120 +++++++++++++++++++++++++++++++
 src/video/cocoa/SDL_cocoavideo.m |   5 +-
 6 files changed, 211 insertions(+), 6 deletions(-)
 create mode 100644 .clang-tidy
 create mode 100644 cmake/3rdparty.cmake

diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644
index 000000000000..739a8eb0417a
--- /dev/null
+++ b/.clang-tidy
@@ -0,0 +1,58 @@
+---
+Checks: >
+  -*,
+  bugprone-assert-side-effect,
+  bugprone-assignment-in-if-condition,
+  bugprone-bool-pointer-implicit-conversion,
+  bugprone-dangling-handle,
+  bugprone-dynamic-static-initializers,
+  bugprone-infinite-loop,
+  bugprone-integer-division,
+  bugprone-macro-repeated-side-effects,
+  bugprone-misplaced-operator-in-strlen-in-alloc,
+  bugprone-misplaced-pointer-arithmetic-in-alloc,
+  bugprone-misplaced-widening-cast,
+  bugprone-not-null-terminated-result,
+  bugprone-posix-return,
+  bugprone-redundant-branch-condition,
+  bugprone-string-literal-with-embedded-nul,
+  bugprone-suspicious-memset-usage,
+  bugprone-suspicious-semicolon,
+  bugprone-suspicious-string-compare,
+  bugprone-too-small-loop-variable,
+  bugprone-unused-return-value,
+  cert-err33-c,
+  clang-analyzer-core.*,
+  clang-analyzer-valist.*,
+  clang-analyzer-unix.Malloc,
+  google-readability-casting,
+  misc-misleading-bidirectional,
+  misc-misleading-identifier,
+  misc-misplaced-const,
+  misc-redundant-expression,
+  objc-*,
+  performance-type-promotion-in-math-fn,
+  readability-avoid-const-params-in-decls,
+  readability-braces-around-statements,
+  readability-const-return-type,
+  readability-duplicate-include,
+  readability-inconsistent-declaration-parameter-name,
+  readability-misplaced-array-index,
+  readability-non-const-parameter,
+  readability-redundant-control-flow,
+  readability-redundant-declaration,
+  readability-redundant-function-ptr-dereference,
+  readability-redundant-preprocessor,
+  readability-simplify-boolean-expr
+
+CheckOptions:
+  - key:   bugprone-assert-side-effect.AssertMacros
+    value: "SDL_assert, SDL_assert_release, SDL_assert_paranoid, SDL_assert_always, SDL_COMPILE_TIME_ASSERT"
+  - key:   bugprone-misplaced-widening-cast.CheckImplicitCasts
+    value: true
+  - key:   bugprone-not-null-terminated-result.WantToUseSafeFunctions
+    value: false # Do not recommend _s functions
+
+FormatStyle: "file"
+HeaderFilterRegex: "*.h$"
+WarningsAsErrors: ""
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 56eec67dcce0..f66fca1c1701 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -40,6 +40,7 @@ jobs:
             -Wdeprecated -Wdev -Werror \
             -DCMAKE_TOOLCHAIN_FILE=${{ steps.setup_ndk.outputs.ndk-path }}/build/cmake/android.toolchain.cmake \
             -DSDL_WERROR=ON \
+            -DSDL_CLANG_TIDY=ON \
             -DANDROID_PLATFORM=${{ matrix.platform.android_platform }} \
             -DANDROID_ABI=${{ matrix.platform.android_abi }} \
             -DSDL_STATIC_PIC=ON \
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 0d30beea8f94..e25b4f902439 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -26,8 +26,8 @@ jobs:
         - { name: Windows (ucrt64),         os: windows-latest, shell: 'msys2 {0}', msystem: ucrt64,  msys-env: mingw-w64-ucrt-x86_64, artifact: 'SDL-msys2-ucrt64' }
         - { name: Ubuntu 20.04,             os: ubuntu-20.04,   shell: sh, artifact: 'SDL-ubuntu20.04' }
         - { name: Ubuntu 22.04,             os: ubuntu-22.04,   shell: sh, artifact: 'SDL-ubuntu22.04' }
-        - { name: MacOS (Framework),  os: macos-latest,   shell: sh,    cmake: '-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DSDL_FRAMEWORK=ON', skip_test_pkgconfig: true, artifact: 'SDL-macos-framework' }
-        - { name: MacOS (GNU prefix), os: macos-latest,   shell: sh,    cmake: '-DCMAKE_OSX_ARCHITECTURES="x86_64"', artifact: 'SDL-macos-gnu' }
+        - { name: MacOS (Framework),  os: macos-latest,   shell: sh,    cmake: '-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DSDL_FRAMEWORK=ON -DSDL_CLANG_TIDY=OFF', skip_test_pkgconfig: true, artifact: 'SDL-macos-framework' }
+        - { name: MacOS (GNU prefix), os: macos-latest,   shell: sh,    cmake: '-DCMAKE_OSX_ARCHITECTURES="x86_64" -DCLANG_TIDY_BINARY="$(brew --prefix llvm)/bin/clang-tidy"', artifact: 'SDL-macos-gnu' }
 
     steps:
     - name: Set up MSYS2
@@ -40,6 +40,7 @@ jobs:
           ${{ matrix.platform.msys-env }}-cmake
           ${{ matrix.platform.msys-env }}-ninja
           ${{ matrix.platform.msys-env }}-pkg-config
+          ${{ matrix.platform.msys-env }}-clang-tools-extra
 
     - name: Setup Linux dependencies
       if: runner.os == 'Linux'
@@ -61,7 +62,8 @@ jobs:
       if: runner.os == 'macOS'
       run: |
         brew install \
-          ninja
+          ninja \
+          llvm
     - uses: actions/checkout@v3
     - name: Check that versioning is consistent
       # We only need to run this once: arbitrarily use the Linux/CMake build
@@ -75,6 +77,7 @@ jobs:
           -DSDL_WERROR=ON \
           -DSDL_INSTALL_TESTS=ON \
           -DSDL_VENDOR_INFO="Github Workflow" \
+          -DSDL_CLANG_TIDY=ON \
           -DCMAKE_INSTALL_PREFIX=cmake_prefix \
           -DCMAKE_BUILD_TYPE=Release \
           ${{ matrix.platform.cmake }}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a0efb6b1ef6d..9899593dce59 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,6 +69,7 @@ include(${SDL3_SOURCE_DIR}/cmake/sdlfind.cmake)
 include(${SDL3_SOURCE_DIR}/cmake/sdlplatform.cmake)
 include(${SDL3_SOURCE_DIR}/cmake/CheckCPUArchitecture.cmake)
 include(${SDL3_SOURCE_DIR}/cmake/GetGitRevisionDescription.cmake)
+include(${SDL3_SOURCE_DIR}/cmake/3rdparty.cmake)
 
 # Enable large file support on 32-bit glibc, so that we can access files
 # with large inode numbers
@@ -439,6 +440,7 @@ set_option(SDL_LIBUDEV             "Enable libudev support" ON)
 set_option(SDL_ASAN                "Use AddressSanitizer to detect memory errors" OFF)
 option_string(SDL_VENDOR_INFO      "Vendor name and/or version to add to SDL_REVISION" "")
 set_option(SDL_CCACHE              "Use Ccache to speed up build" ON)
+set_option(SDL_CLANG_TIDY          "Run clang-tidy static analysis" OFF)
 
 option(SDL_WERROR "Enable -Werror" OFF)
 
@@ -2968,6 +2970,28 @@ else()
   set(HAVE_CCACHE OFF)
 endif()
 
+if(SDL_CLANG_TIDY)
+  cmake_minimum_required(VERSION 3.6)
+  find_program(CLANG_TIDY_BINARY clang-tidy)
+
+  if(CLANG_TIDY_BINARY)
+    set(HAVE_CLANG_TIDY ON)
+    get_clang_tidy_ignored_files(CLANG_TIDY_IGNORED_FILES)
+    set(CLANG_TIDY_COMMAND "${CLANG_TIDY_BINARY}" "-extra-arg=-Wno-unknown-warning-option" "--line-filter=[${CLANG_TIDY_IGNORED_FILES}]")
+    if(SDL_WERROR)
+      list(APPEND CLANG_TIDY_COMMAND "--warnings-as-errors=*")
+    endif()
+    set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_COMMAND})
+    set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_COMMAND})
+    set(CMAKE_OBJC_CLANG_TIDY ${CLANG_TIDY_COMMAND})
+    set_source_files_properties(${SOURCE_FILES} PROPERTIES SKIP_PRECOMPILE_HEADERS TRUE)
+    file(GLOB STDLIB_SOURCES "${SDL3_SOURCE_DIR}/src/stdlib/*.c")
+    set_property(SOURCE ${STDLIB_SOURCES} APPEND PROPERTY COMPILE_DEFINITIONS "SDL_DISABLE_ANALYZE_MACROS")
+  else()
+    set(HAVE_CLANG_TIDY OFF)
+  endif()
+endif()
+
 if(SDL_TESTS)
   set(HAVE_TESTS ON)
 endif()
diff --git a/cmake/3rdparty.cmake b/cmake/3rdparty.cmake
new file mode 100644
index 000000000000..3d1325deab49
--- /dev/null
+++ b/cmake/3rdparty.cmake
@@ -0,0 +1,120 @@
+include_guard()
+
+function(get_clang_tidy_ignored_files OUTVAR)
+  set(3RD_PARTY_SOURCES
+      # Public GL headers
+      "SDL_egl.h"
+      "SDL_hidapi.h"
+      "SDL_opengl.h"
+      "SDL_opengl_glext.h"
+      "SDL_opengles2_gl2.h"
+      "SDL_opengles2_gl2ext.h"
+      "SDL_opengles2_gl2platform.h"
+      "SDL_opengles2_khrplatform.h"
+      # stdlib
+      "SDL_malloc.c"
+      "SDL_qsort.c"
+      "SDL_strtokr.c"
+      # edid
+      "edid-parse.c"
+      "edid.h"
+      # imKStoUCS
+      "imKStoUCS.c"
+      "imKStoUCS.h"
+      # Joystick controller type
+      "controller_type.h"
+      "controller_type.c"
+      # HIDAPI Steam controller
+      "controller_constants.h"
+      "controller_structs.h"
+      # Nokia Pixman
+      "pixman-arm-asm.h"
+      "pixman-arm-neon-asm.h"
+      "pixman-arm-simd-asm.h"
+      # YUV2RGB
+      "yuv_rgb.c"
+      "yuv_rgb_lsx_func.h"
+      "yuv_rgb_sse_func.h"
+      "yuv_rgb_std_func.h"
+      # LIBM
+      "e_atan2.c"
+      "e_exp.c"
+      "e_fmod.c"
+      "e_log10.c"
+      "e_log.c"
+      "e_pow.c"
+      "e_rem_pio2.c"
+      "e_sqrt.c"
+      "k_cos.c"
+      "k_rem_pio2.c"
+      "k_sin.c"
+      "k_tan.c"
+      "s_atan.c"
+      "s_copysign.c"
+      "s_cos.c"
+      "s_fabs.c"
+      "s_floor.c"
+      "s_scalbn.c"
+      "s_sin.c"
+      "s_tan.c"
+      "math_private.h"
+      "math_libm.h"
+      # EGL
+      "egl.h"
+      "eglext.h"
+      "eglplatform.h"
+      # GLES2
+      "gl2.h"
+      "gl2ext.h"
+      "gl2platform.h"
+      # KHR
+      "khrplatform.h"
+      # Vulkan
+      "vk_icd.h"
+      "vk_layer.h"
+      "vk_platform.h"
+      "vk_sdk_platform.h"
+      "vulkan_android.h"
+      "vulkan_beta.h"
+      "vulkan_core.h"
+      "vulkan_directfb.h"
+      "vulkan_fuchsia.h"
+      "vulkan_ggp.h"
+      "vulkan_ios.h"
+      "vulkan_macos.h"
+      "vulkan_metal.h"
+      "vulkan_screen.h"
+      "vulkan_vi.h"
+      "vulkan_wayland.h"
+      "vulkan_win32.h"
+      "vulkan_xcb.h"
+      "vulkan_xlib_xrandr.h"
+      "vulkan_xlib.h"
+      "vulkan.h"
+      "vulkan_enums.hpp"
+      "vulkan_format_traits.hpp"
+      "vulkan_funcs.hpp"
+      "vulkan_handles.hpp"
+      "vulkan_hash.hpp"
+      "vulkan_raii.hpp"
+      "vulkan_static_assertions.hpp"
+      "vulkan_structs.hpp"
+      "vulkan_to_string.hpp"
+      "vulkan.hpp"
+      # HIDAPI
+      "hid.c"
+      "hid.cpp"
+      "hid.m"
+      "hidraw.cpp"
+      "hidusb.cpp"
+      "hidapi.h")
+
+  foreach(SOURCE_FILE ${3RD_PARTY_SOURCES})
+    list(APPEND IGNORED_LIST "{\"name\":\"${SOURCE_FILE}\",\"lines\":[[1,1]]}")
+  endforeach()
+
+  string(REPLACE ";" "," IGNORED_FILES "${IGNORED_LIST}")
+  set(${OUTVAR}
+      "${IGNORED_FILES}"
+      PARENT_SCOPE)
+endfunction()
diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m
index fac10acf062c..1031cf5435a1 100644
--- a/src/video/cocoa/SDL_cocoavideo.m
+++ b/src/video/cocoa/SDL_cocoavideo.m
@@ -223,8 +223,7 @@ void Cocoa_VideoQuit(_THIS)
 }
 
 /* This function assumes that it's called from within an autorelease pool */
-NSImage *
-Cocoa_CreateImage(SDL_Surface *surface)
+NSImage *Cocoa_CreateImage(SDL_Surface *surface)
 {
     SDL_Surface *converted;
     NSBitmapImageRep *imgrep;
@@ -254,7 +253,7 @@ void Cocoa_VideoQuit(_THIS)
 
     /* Copy the pixels */
     pixels = [imgrep bitmapData];
-    SDL_memcpy(pixels, converted->pixels, converted->h * converted->pitch);
+    SDL_memcpy(pixels, converted->pixels, (size_t)converted->h * converted->pitch);
     SDL_DestroySurface(converted);
 
     /* Premultiply the alpha channel */