From 6eb0476195e1b04781f1926f667b67999e2d55b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20S=C5=82uszniak?= Date: Thu, 25 Jun 2026 14:24:45 +0200 Subject: [PATCH 1/4] [RNE Rewrite] chore: set up clangd for C++ sources Add a committed clangd config for the core package's C/C++ sources so the rewrite gets code intelligence and a shared, strict warning set: - compile_flags.txt: include roots / std / defines (third-party as -isystem so library warnings don't pollute our diagnostics); relative paths anchored to the package dir, so it is portable with no per-machine generation - .clangd: strict warning set (incl. -Wconversion/-Wsign-conversion) layered on top, with clangd's include cleaner disabled (ET umbrella headers) - .gitignore: track the shared config while keeping generated DBs local - CONTRIBUTING.md: document prerequisites and editor setup clang-tidy CI is a follow-up. --- .gitignore | 3 ++ CONTRIBUTING.md | 28 ++++++++++++++++ packages/react-native-executorch/.clangd | 33 +++++++++++++++++++ .../react-native-executorch/compile_flags.txt | 11 +++++++ 4 files changed, 75 insertions(+) create mode 100644 packages/react-native-executorch/.clangd create mode 100644 packages/react-native-executorch/compile_flags.txt diff --git a/.gitignore b/.gitignore index 9595a23273..042dcebcb0 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ jsconfig.json .cache/ compile_commands.json compile_flags.txt +# ...but the core package ships a shared, committed clangd config for its C++ sources: +!/packages/react-native-executorch/.clangd +!/packages/react-native-executorch/compile_flags.txt # Xcode # diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 78d87744a8..a70d9b8564 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,6 +45,34 @@ Found a model you would like to use in your app but it is not currently supplied Do you have a neat example use case and want to share it with us? You can just drop us a message on Discord server and/or open a PR to `apps` directory here. If you found some inconsistencies in our documentation or just something is missing just open a PR with suggested changes (remember to add changes to previous docs versions too, e.g `docs/versioned_docs/version-0.3.x`, `docs/versioned_docs/version-0.4.x`). +# C++ tooling (clangd) + +The core package ships a committed [clangd](https://clangd.llvm.org/) setup so the +C/C++ sources under `packages/react-native-executorch/cpp` get code intelligence and +a strict, shared set of compiler warnings: + +- `packages/react-native-executorch/compile_flags.txt` — the compilation database: + language standard, preprocessor defines, and include roots (paths are relative to + the package, so the config is portable). ExecuTorch/torch/JSI headers are added as + system includes so their internal warnings don't pollute diagnostics for our code. +- `packages/react-native-executorch/.clangd` — the warning policy layered on top. + +For clangd to resolve the `` and `` includes you need the +same prerequisites as a native build: + +1. `yarn install` at the repo root (provides the JSI headers under `node_modules`). +2. ExecuTorch headers provisioned under `packages/react-native-executorch/third-party/include` + (see [third-party/README.md](./packages/react-native-executorch/third-party/README.md)). + +Without them clangd still lints your own code; the third-party includes simply stay +unresolved until the headers are present. + +Editor setup: install the official **clangd** extension (e.g. `llvm-vs-code-extensions.vscode-clangd` +for VS Code) and disable the default Microsoft C/C++ IntelliSense engine so the two +don't conflict. clangd discovers `compile_flags.txt`/`.clangd` automatically from the +open file's directory. If you produce a `compile_commands.json` from a native build, +clangd will prefer it — it's gitignored and takes precedence over `compile_flags.txt`. + # Creating a Pull Request Before writing any code reach out to us to make sure no one is currently working on it, you can always open an issue first. diff --git a/packages/react-native-executorch/.clangd b/packages/react-native-executorch/.clangd new file mode 100644 index 0000000000..fedb3c7201 --- /dev/null +++ b/packages/react-native-executorch/.clangd @@ -0,0 +1,33 @@ +# clangd configuration for the react-native-executorch C/C++ sources. +# +# Include roots, the language standard, and preprocessor defines live in +# `compile_flags.txt` (the compilation database). This file layers the project's +# warning policy on top and configures clangd's diagnostics. +# +# The warning set is intentionally strict to surface bugs early during the +# rewrite. Third-party headers are included as `-isystem` (see compile_flags.txt), +# so these warnings only apply to our own code in `cpp/`. +CompileFlags: + Add: + - -Wall + - -Wextra + - -Wpedantic + - -Wshadow + - -Wnon-virtual-dtor + - -Woverloaded-virtual + - -Wold-style-cast + - -Wcast-align + - -Wunused + - -Wnull-dereference + - -Wimplicit-fallthrough + - -Wextra-semi + - -Wformat=2 + - -Wconversion + - -Wsign-conversion + - -Wdouble-promotion + +Diagnostics: + # ExecuTorch/torch rely heavily on umbrella headers, which clangd's include + # cleaner misreports as unused/missing. Disable it to avoid false positives. + UnusedIncludes: None + MissingIncludes: None diff --git a/packages/react-native-executorch/compile_flags.txt b/packages/react-native-executorch/compile_flags.txt new file mode 100644 index 0000000000..5e215eaad2 --- /dev/null +++ b/packages/react-native-executorch/compile_flags.txt @@ -0,0 +1,11 @@ +-xc++ +-std=c++20 +-DC10_USING_CUSTOM_GENERATED_MACROS=1 +-DEXECUTORCH_ENABLE_EXECUTION_PROFILING=1 +-Icpp +-isystem../../node_modules/react-native/ReactCommon/jsi +-isystemthird-party/include +-isystemthird-party/include/executorch/extension/llm/tokenizers/include +-isystemthird-party/include/executorch/extension/llm/tokenizers/third-party/json/include +-isystemthird-party/include/executorch/extension/llm/tokenizers/third-party/re2 +-isystemthird-party/include/executorch/extension/llm/tokenizers/third-party/abseil-cpp From 2cbfc0aadc65e4597bde16274019cfb9b6670c01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20S=C5=82uszniak?= Date: Thu, 25 Jun 2026 15:51:12 +0200 Subject: [PATCH 2/4] [RNE Rewrite] chore: enforce clangd C++ warnings in pre-commit Add a lefthook pre-commit command that compiles staged cpp/ sources with the project's clangd warning set (compile_flags.txt + the -W flags in .clangd) and aborts the commit on any warning, keeping the editor and the commit gate in sync. It skips gracefully when no compiler or the provisioned ExecuTorch/JSI headers are available, so contributors who don't build native code are never blocked; bypass with `git commit --no-verify`. --- CONTRIBUTING.md | 6 ++ lefthook.yml | 3 + .../scripts/check-cpp-warnings.sh | 68 +++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100755 packages/react-native-executorch/scripts/check-cpp-warnings.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a70d9b8564..f890f80fd7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -67,6 +67,12 @@ same prerequisites as a native build: Without them clangd still lints your own code; the third-party includes simply stay unresolved until the headers are present. +A `pre-commit` hook (lefthook) compiles staged `cpp/` sources with this same warning set +and aborts the commit if any warning is introduced — the editor and the commit gate stay +in sync. It skips automatically when no compiler or the provisioned headers are available, +so it never blocks contributors who don't build the native code; bypass with +`git commit --no-verify`. + Editor setup: install the official **clangd** extension (e.g. `llvm-vs-code-extensions.vscode-clangd` for VS Code) and disable the default Microsoft C/C++ IntelliSense engine so the two don't conflict. clangd discovers `compile_flags.txt`/`.clangd` automatically from the diff --git a/lefthook.yml b/lefthook.yml index 99bb1b2655..3c80fbc7ea 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -13,6 +13,9 @@ pre-commit: format-objc: glob: '*.{h,hpp,m,mm,c,cpp}' run: clang-format -i {staged_files} && git add {staged_files} + cpp-warnings: + glob: '*.{h,hpp,c,cpp}' + run: packages/react-native-executorch/scripts/check-cpp-warnings.sh {staged_files} format-kotlin: glob: '*.{kt}' run: ktlint -F {staged_files} && git add {staged_files} diff --git a/packages/react-native-executorch/scripts/check-cpp-warnings.sh b/packages/react-native-executorch/scripts/check-cpp-warnings.sh new file mode 100755 index 0000000000..35bb5e7ae4 --- /dev/null +++ b/packages/react-native-executorch/scripts/check-cpp-warnings.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# +# Pre-commit guard: fail the commit when staged C/C++ sources in the core package +# produce compiler warnings under the project's clangd warning set — i.e. the +# include flags in compile_flags.txt plus the -W flags in .clangd. This mirrors +# what clangd shows in the editor, so a commit can't introduce a warning that the +# editor would have flagged. +# +# It skips gracefully (without blocking the commit) when a C++ compiler or the +# provisioned ExecuTorch/JSI headers are not available, so contributors who don't +# build the native code are never blocked. Bypass explicitly with `git commit +# --no-verify`. Override the compiler with CXX=/path/to/clang++. +# +# Usage: check-cpp-warnings.sh [ ...] +set -uo pipefail + +PKG_DIR="$(cd "$(dirname "$0")/.." && pwd)" +PKG_PREFIX="packages/react-native-executorch/" + +# Keep only staged files inside this package's cpp/ tree, relative to PKG_DIR. +rel_files=() +for f in "$@"; do + case "$f" in + "${PKG_PREFIX}"cpp/*) rel_files+=("${f#"$PKG_PREFIX"}") ;; + cpp/*) rel_files+=("$f") ;; + esac +done +[ "${#rel_files[@]}" -eq 0 ] && exit 0 + +CXX="${CXX:-clang++}" +if ! command -v "$CXX" >/dev/null 2>&1; then + echo "ℹ C++ warning check skipped: no compiler ('$CXX') on PATH (set CXX to override)." + exit 0 +fi + +cd "$PKG_DIR" + +if [ ! -d third-party/include/executorch ] || + [ ! -f ../../node_modules/react-native/ReactCommon/jsi/jsi/jsi.h ]; then + echo "ℹ C++ warning check skipped: provision third-party/include and run 'yarn install' to enable it." + exit 0 +fi + +# Compilation database (includes / std / defines) + the .clangd warning set. +db_flags=() +while IFS= read -r line; do + [ -n "$line" ] && db_flags+=("$line") +done < compile_flags.txt +warn_flags=() +while IFS= read -r w; do warn_flags+=("$w"); done < <(grep -oE '\-W[A-Za-z0-9=-]+' .clangd) + +status=0 +for f in "${rel_files[@]}"; do + [ -f "$f" ] || continue # skip deleted/renamed entries + if out="$("$CXX" -fsyntax-only "${db_flags[@]}" "${warn_flags[@]}" "$f" 2>&1)" && [ -z "$out" ]; then + continue + fi + if printf '%s\n' "$out" | grep -qE '(warning|error):'; then + printf '%s\n' "$out" >&2 + status=1 + fi +done + +if [ "$status" -ne 0 ]; then + echo >&2 + echo "✖ C++ warnings in staged sources (above). Fix them, or bypass with 'git commit --no-verify'." >&2 +fi +exit "$status" From b6084d68022746029ed662d74ad35cc2b3e0ad07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20S=C5=82uszniak?= Date: Thu, 25 Jun 2026 16:41:44 +0200 Subject: [PATCH 3/4] [RNE Rewrite] fix: resolve C++ warnings flagged by the clangd set Bring the existing cpp/ sources into compliance with the strict warning set so clangd (and the pre-commit guard) start clean: - comment out unused JSI host-function parameters (thisVal/args) using the /* name */ anonymous-parameter idiom - fix int -> size_t sign-conversions: route validated axis indices through a size_t axisIdx, make byte-offset sizes (hw, plane) size_t, and cast the few remaining count/index sites - reformat pre-existing clang-format drift in tokenizer.cpp --- .../cpp/core/model.cpp | 10 ++-- .../cpp/core/tensor.cpp | 8 +-- .../cpp/core/utils.cpp | 2 +- .../cpp/extensions/cv/box_ops.cpp | 4 +- .../cpp/extensions/cv/image_ops.cpp | 25 ++++---- .../cpp/extensions/math/operations.cpp | 22 +++---- .../cpp/extensions/nlp/tokenizer.cpp | 60 +++++++++---------- 7 files changed, 67 insertions(+), 64 deletions(-) diff --git a/packages/react-native-executorch/cpp/core/model.cpp b/packages/react-native-executorch/cpp/core/model.cpp index 6f89925639..fec9478e58 100644 --- a/packages/react-native-executorch/cpp/core/model.cpp +++ b/packages/react-native-executorch/cpp/core/model.cpp @@ -27,7 +27,7 @@ jsi::Value ModelHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) { if (nameStr == "getMethodNames") { auto self = shared_from_this(); - auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value * /*args*/, size_t count) -> jsi::Value { if (count != 0) { throw jsi::JSError(rt, "getMethodNames: Usage: getMethodNames()"); } @@ -61,7 +61,7 @@ jsi::Value ModelHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) { if (nameStr == "getMethodMeta") { auto self = shared_from_this(); - auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 1) { throw jsi::JSError(rt, "getMethodMeta: Usage: getMethodMeta(methodName)"); } @@ -176,7 +176,7 @@ jsi::Value ModelHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) { if (nameStr == "execute") { auto self = shared_from_this(); - auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 3) { throw jsi::JSError(rt, "execute: Usage: execute(methodName, inputs, outputTensors)"); } @@ -435,7 +435,7 @@ jsi::Value ModelHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) { if (nameStr == "dispose") { auto self = shared_from_this(); - auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value * /*args*/, size_t count) -> jsi::Value { if (count != 0) { throw jsi::JSError(rt, "dispose: Usage: dispose()"); } @@ -468,7 +468,7 @@ std::vector ModelHostObject::getPropertyNames(jsi::Ru void install_loadModel(jsi::Runtime &rt, jsi::Object &module) { auto name = "loadModel"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 1) { throw jsi::JSError(rt, "loadModel: Usage: loadModel(arg0)"); } diff --git a/packages/react-native-executorch/cpp/core/tensor.cpp b/packages/react-native-executorch/cpp/core/tensor.cpp index ebdabdccf2..83bec4afaa 100644 --- a/packages/react-native-executorch/cpp/core/tensor.cpp +++ b/packages/react-native-executorch/cpp/core/tensor.cpp @@ -38,7 +38,7 @@ jsi::Value TensorHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) if (nameStr == "copyTo") { auto self = shared_from_this(); - auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 1) { throw jsi::JSError(rt, "copyTo: Usage: copyTo(dst)"); } @@ -143,7 +143,7 @@ jsi::Value TensorHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) if (nameStr == "getData") { auto self = shared_from_this(); - auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 1) { throw jsi::JSError(rt, "getData: Usage: getData(array)"); } @@ -259,7 +259,7 @@ jsi::Value TensorHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) if (nameStr == "dispose") { auto self = shared_from_this(); - auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value * /*args*/, size_t count) -> jsi::Value { if (count != 0) { throw jsi::JSError(rt, "dispose: Usage: dispose()"); } @@ -297,7 +297,7 @@ std::vector TensorHostObject::getPropertyNames(jsi::R void install_createTensor(jsi::Runtime &rt, jsi::Object &module) { auto name = "createTensor"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 2) { throw jsi::JSError(rt, "createTensor: Usage: createTensor(shape, dtype)"); } diff --git a/packages/react-native-executorch/cpp/core/utils.cpp b/packages/react-native-executorch/cpp/core/utils.cpp index 12bf5d943f..1e64763bd8 100644 --- a/packages/react-native-executorch/cpp/core/utils.cpp +++ b/packages/react-native-executorch/cpp/core/utils.cpp @@ -10,7 +10,7 @@ namespace jsi = facebook::jsi; void install_getExecuTorchRegisteredBackends(jsi::Runtime &rt, jsi::Object &module) { auto name = "getExecuTorchRegisteredBackends"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value * /*args*/, size_t count) -> jsi::Value { if (count != 0) { throw jsi::JSError(rt, "Usage: getExecuTorchRegisteredBackends()"); } diff --git a/packages/react-native-executorch/cpp/extensions/cv/box_ops.cpp b/packages/react-native-executorch/cpp/extensions/cv/box_ops.cpp index 5399d10b8d..5d42fc5097 100644 --- a/packages/react-native-executorch/cpp/extensions/cv/box_ops.cpp +++ b/packages/react-native-executorch/cpp/extensions/cv/box_ops.cpp @@ -67,7 +67,7 @@ std::array decodeToXyxy( void install_nms(jsi::Runtime &rt, jsi::Object &module) { auto name = "nms"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count < 3) { throw jsi::JSError(rt, "Usage: nms(boxes, scores, options)"); } @@ -143,7 +143,7 @@ void install_nms(jsi::Runtime &rt, jsi::Object &module) { const float *scoresPtr = reinterpret_cast(scores->data_.get()); std::vector> candidates; - candidates.reserve(numAnchors); + candidates.reserve(static_cast(numAnchors)); for (size_t idx = 0; std::cmp_less(idx, numAnchors); ++idx) { float score = scoresPtr[idx]; diff --git a/packages/react-native-executorch/cpp/extensions/cv/image_ops.cpp b/packages/react-native-executorch/cpp/extensions/cv/image_ops.cpp index 6121de3838..2511bb69e1 100644 --- a/packages/react-native-executorch/cpp/extensions/cv/image_ops.cpp +++ b/packages/react-native-executorch/cpp/extensions/cv/image_ops.cpp @@ -65,7 +65,7 @@ FitBox computeFit(int32_t srcW, int32_t srcH, int32_t dstW, int32_t dstH, bool i void install_resize(jsi::Runtime &rt, jsi::Object &module) { auto name = "resize"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 3) { throw jsi::JSError(rt, "Usage: resize(src, dst, options)"); } @@ -233,7 +233,7 @@ int codeToColorConversionFlag(const std::string &code) { void install_cvtColor(jsi::Runtime &rt, jsi::Object &module) { auto name = "cvtColor"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 3) { throw jsi::JSError(rt, "Usage: cvtColor(src, dst, code)"); } @@ -319,7 +319,7 @@ void install_cvtColor(jsi::Runtime &rt, jsi::Object &module) { void install_toChannelsFirst(jsi::Runtime &rt, jsi::Object &module) { auto name = "toChannelsFirst"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 2) { throw jsi::JSError(rt, "Usage: toChannelsFirst(src, dst)"); } @@ -391,7 +391,7 @@ void install_toChannelsFirst(jsi::Runtime &rt, jsi::Object &module) { std::vector<::cv::Mat> channels; ::cv::split(srcMat, channels); - int32_t hw = srcH * srcW; + size_t hw = static_cast(srcH) * static_cast(srcW); size_t elemSize = rnexecutorch::core::types::elementSize(src->dtype_); uint8_t *dstPtr = dst->data_.get(); @@ -407,7 +407,7 @@ void install_toChannelsFirst(jsi::Runtime &rt, jsi::Object &module) { void install_toChannelsLast(jsi::Runtime &rt, jsi::Object &module) { auto name = "toChannelsLast"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 2) { throw jsi::JSError(rt, "Usage: toChannelsLast(src, dst)"); } @@ -475,7 +475,7 @@ void install_toChannelsLast(jsi::Runtime &rt, jsi::Object &module) { throw jsi::JSError(rt, "toChannelsLast: " + std::string(e.what())); } - int32_t hw = srcH * srcW; + size_t hw = static_cast(srcH) * static_cast(srcW); size_t elemSize = rnexecutorch::core::types::elementSize(src->dtype_); uint8_t *srcPtr = src->data_.get(); @@ -495,7 +495,7 @@ void install_toChannelsLast(jsi::Runtime &rt, jsi::Object &module) { void install_normalize(jsi::Runtime &rt, jsi::Object &module) { auto name = "normalize"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 3) { throw jsi::JSError(rt, "Usage: normalize(src, dst, options)"); } @@ -541,7 +541,7 @@ void install_normalize(jsi::Runtime &rt, jsi::Object &module) { } auto val = opts.getProperty(rt, name); - std::vector result(c); + std::vector result(static_cast(c)); if (val.isNumber()) { std::ranges::fill(result, val.asNumber()); @@ -602,9 +602,10 @@ void install_normalize(jsi::Runtime &rt, jsi::Object &module) { uint8_t *srcPtr = src->data_.get(); uint8_t *dstPtr = dst->data_.get(); + const size_t plane = static_cast(h) * static_cast(w); for (size_t ch = 0; std::cmp_less(ch, c); ++ch) { - ::cv::Mat srcChannel(h, w, srcDepthType, srcPtr + ch * h * w * srcElemSize); - ::cv::Mat dstChannel(h, w, dstDepthType, dstPtr + ch * h * w * dstElemSize); + ::cv::Mat srcChannel(h, w, srcDepthType, srcPtr + ch * plane * srcElemSize); + ::cv::Mat dstChannel(h, w, dstDepthType, dstPtr + ch * plane * dstElemSize); srcChannel.convertTo(dstChannel, dstDepthType, alpha[ch], beta[ch]); } @@ -617,7 +618,7 @@ void install_normalize(jsi::Runtime &rt, jsi::Object &module) { void install_applyColormap(jsi::Runtime &rt, jsi::Object &module) { auto name = "applyColormap"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 3) { throw jsi::JSError(rt, "Usage: applyColormap(src, dst, colormap)"); } @@ -695,7 +696,7 @@ void install_applyColormap(jsi::Runtime &rt, jsi::Object &module) { std::to_string(numColors) + ")"); } for (size_t c = 0; c < numRgbaChannels; ++c) { - dstData[i * numRgbaChannels + c] = lut[idx][c]; + dstData[i * numRgbaChannels + c] = lut[static_cast(idx)][c]; } } diff --git a/packages/react-native-executorch/cpp/extensions/math/operations.cpp b/packages/react-native-executorch/cpp/extensions/math/operations.cpp index d02738ca45..4d6fdde5ff 100644 --- a/packages/react-native-executorch/cpp/extensions/math/operations.cpp +++ b/packages/react-native-executorch/cpp/extensions/math/operations.cpp @@ -13,7 +13,7 @@ using TensorHostObject = rnexecutorch::core::tensor::TensorHostObject; void install_sigmoid(jsi::Runtime &rt, jsi::Object &module) { auto name = "sigmoid"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 2) { throw jsi::JSError(rt, "Usage: sigmoid(src, dst)"); } @@ -79,7 +79,7 @@ void install_sigmoid(jsi::Runtime &rt, jsi::Object &module) { void install_softmax(jsi::Runtime &rt, jsi::Object &module) { auto name = "softmax"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 3) { throw jsi::JSError(rt, "Usage: softmax(src, dst, axis)"); } @@ -130,6 +130,7 @@ void install_softmax(jsi::Runtime &rt, jsi::Object &module) { if (axis < 0 || axis >= rank) { throw jsi::JSError(rt, "softmax: axis is out of range"); } + const size_t axisIdx = static_cast(axis); std::shared_lock srcLock(src->mutex_, std::try_to_lock); if (!srcLock.owns_lock()) { @@ -152,7 +153,7 @@ void install_softmax(jsi::Runtime &rt, jsi::Object &module) { const auto *srcData = reinterpret_cast(src->data_.get()); auto *dstData = reinterpret_cast(dst->data_.get()); - const size_t axisDim = static_cast(src->shape_[axis]); + const size_t axisDim = static_cast(src->shape_[axisIdx]); if (axisDim == 0) { throw jsi::JSError(rt, "softmax: axis dimension must be greater than zero"); } @@ -163,7 +164,7 @@ void install_softmax(jsi::Runtime &rt, jsi::Object &module) { } size_t inner = 1; - for (size_t i = axis + 1; std::cmp_less(i, rank); ++i) { + for (size_t i = axisIdx + 1; std::cmp_less(i, rank); ++i) { inner *= static_cast(src->shape_[i]); } @@ -197,7 +198,7 @@ void install_softmax(jsi::Runtime &rt, jsi::Object &module) { void install_argmax(jsi::Runtime &rt, jsi::Object &module) { auto name = "argmax"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 3) { throw jsi::JSError(rt, "Usage: argmax(src, dst, axis)"); } @@ -240,9 +241,10 @@ void install_argmax(jsi::Runtime &rt, jsi::Object &module) { if (axis < 0 || axis >= rank) { throw jsi::JSError(rt, "argmax: axis is out of range"); } + const size_t axisIdx = static_cast(axis); auto dstExpectedShape = src->shape_; - dstExpectedShape[axis] = 1; + dstExpectedShape[axisIdx] = 1; if (dst->shape_ != dstExpectedShape) { throw jsi::JSError(rt, "argmax: dst shape must match src shape but with axis dimension 1"); } @@ -263,17 +265,17 @@ void install_argmax(jsi::Runtime &rt, jsi::Object &module) { const float *srcData = reinterpret_cast(src->data_.get()); - size_t axisDim = src->shape_[axis]; + const size_t axisDim = static_cast(src->shape_[axisIdx]); if (axisDim == 0) { throw jsi::JSError(rt, "argmax: axis dimension must be greater than zero"); } size_t outer = 1, inner = 1; for (size_t i = 0; std::cmp_less(i, axis); ++i) { - outer *= src->shape_[i]; + outer *= static_cast(src->shape_[i]); } - for (size_t i = axis + 1; std::cmp_less(i, rank); ++i) { - inner *= src->shape_[i]; + for (size_t i = axisIdx + 1; std::cmp_less(i, rank); ++i) { + inner *= static_cast(src->shape_[i]); } int32_t *dstData = reinterpret_cast(dst->data_.get()); diff --git a/packages/react-native-executorch/cpp/extensions/nlp/tokenizer.cpp b/packages/react-native-executorch/cpp/extensions/nlp/tokenizer.cpp index a313b4df37..4afd634de3 100644 --- a/packages/react-native-executorch/cpp/extensions/nlp/tokenizer.cpp +++ b/packages/react-native-executorch/cpp/extensions/nlp/tokenizer.cpp @@ -18,29 +18,29 @@ constexpr uint64_t kNumAddedEosTokens = 0; // tokenizers::Error is its own enum (not executorch::runtime::Error), and the // tokenizers library ships no to_string for it, so map it to a readable name. std::string toString(tokenizers::Error error) { - switch (error) { - case tokenizers::Error::Ok: - return "Ok"; - case tokenizers::Error::Internal: - return "Internal"; - case tokenizers::Error::Uninitialized: - return "Uninitialized"; - case tokenizers::Error::OutOfRange: - return "OutOfRange"; - case tokenizers::Error::LoadFailure: - return "LoadFailure"; - case tokenizers::Error::EncodeFailure: - return "EncodeFailure"; - case tokenizers::Error::Base64DecodeFailure: - return "Base64DecodeFailure"; - case tokenizers::Error::ParseFailure: - return "ParseFailure"; - case tokenizers::Error::DecodeFailure: - return "DecodeFailure"; - case tokenizers::Error::RegexFailure: - return "RegexFailure"; - } - return "Unknown(" + std::to_string(static_cast(error)) + ")"; + switch (error) { + case tokenizers::Error::Ok: + return "Ok"; + case tokenizers::Error::Internal: + return "Internal"; + case tokenizers::Error::Uninitialized: + return "Uninitialized"; + case tokenizers::Error::OutOfRange: + return "OutOfRange"; + case tokenizers::Error::LoadFailure: + return "LoadFailure"; + case tokenizers::Error::EncodeFailure: + return "EncodeFailure"; + case tokenizers::Error::Base64DecodeFailure: + return "Base64DecodeFailure"; + case tokenizers::Error::ParseFailure: + return "ParseFailure"; + case tokenizers::Error::DecodeFailure: + return "DecodeFailure"; + case tokenizers::Error::RegexFailure: + return "RegexFailure"; + } + return "Unknown(" + std::to_string(static_cast(error)) + ")"; } } // namespace @@ -63,7 +63,7 @@ jsi::Value TokenizerHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &nam if (nameStr == "encode") { auto self = shared_from_this(); - auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 1) { throw jsi::JSError(rt, "encode: Usage: encode(text)"); } @@ -101,7 +101,7 @@ jsi::Value TokenizerHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &nam if (nameStr == "decode") { auto self = shared_from_this(); - auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count < 1 || count > 2) { throw jsi::JSError(rt, "decode: Usage: decode(tokens, skipSpecialTokens?)"); } @@ -157,7 +157,7 @@ jsi::Value TokenizerHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &nam if (nameStr == "getVocabSize") { auto self = shared_from_this(); - auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value * /*args*/, size_t count) -> jsi::Value { if (count != 0) { throw jsi::JSError(rt, "getVocabSize: Usage: getVocabSize()"); } @@ -178,7 +178,7 @@ jsi::Value TokenizerHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &nam if (nameStr == "idToToken") { auto self = shared_from_this(); - auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 1) { throw jsi::JSError(rt, "idToToken: Usage: idToToken(id)"); } @@ -210,7 +210,7 @@ jsi::Value TokenizerHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &nam if (nameStr == "tokenToId") { auto self = shared_from_this(); - auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 1) { throw jsi::JSError(rt, "tokenToId: Usage: tokenToId(token)"); } @@ -242,7 +242,7 @@ jsi::Value TokenizerHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &nam if (nameStr == "dispose") { auto self = shared_from_this(); - auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value * /*args*/, size_t count) -> jsi::Value { if (count != 0) { throw jsi::JSError(rt, "dispose: Usage: dispose()"); } @@ -277,7 +277,7 @@ std::vector TokenizerHostObject::getPropertyNames(jsi void install_loadTokenizer(jsi::Runtime &rt, jsi::Object &module) { auto name = "loadTokenizer"; - auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { + auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value { if (count != 1) { throw jsi::JSError(rt, "loadTokenizer: Usage: loadTokenizer(arg0)"); } From 927f6151f2e73e74b358add856e48bbcd9752896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20S=C5=82uszniak?= Date: Fri, 26 Jun 2026 12:50:35 +0200 Subject: [PATCH 4/4] [RNE Rewrite] chore: harden cpp-warnings pre-commit script Address review feedback: - skip with a notice if compile_flags.txt or .clangd are missing - read the last line of compile_flags.txt even without a trailing newline --- .../scripts/check-cpp-warnings.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/react-native-executorch/scripts/check-cpp-warnings.sh b/packages/react-native-executorch/scripts/check-cpp-warnings.sh index 35bb5e7ae4..914bab75c0 100755 --- a/packages/react-native-executorch/scripts/check-cpp-warnings.sh +++ b/packages/react-native-executorch/scripts/check-cpp-warnings.sh @@ -35,6 +35,11 @@ fi cd "$PKG_DIR" +if [ ! -f compile_flags.txt ] || [ ! -f .clangd ]; then + echo "ℹ C++ warning check skipped: compile_flags.txt or .clangd not found." + exit 0 +fi + if [ ! -d third-party/include/executorch ] || [ ! -f ../../node_modules/react-native/ReactCommon/jsi/jsi/jsi.h ]; then echo "ℹ C++ warning check skipped: provision third-party/include and run 'yarn install' to enable it." @@ -42,12 +47,15 @@ if [ ! -d third-party/include/executorch ] || fi # Compilation database (includes / std / defines) + the .clangd warning set. +# The `|| [ -n "$line" ]` keeps the final line even if the file has no trailing newline. db_flags=() -while IFS= read -r line; do +while IFS= read -r line || [ -n "$line" ]; do [ -n "$line" ] && db_flags+=("$line") done < compile_flags.txt warn_flags=() -while IFS= read -r w; do warn_flags+=("$w"); done < <(grep -oE '\-W[A-Za-z0-9=-]+' .clangd) +while IFS= read -r w || [ -n "$w" ]; do + [ -n "$w" ] && warn_flags+=("$w") +done < <(grep -oE '\-W[A-Za-z0-9=-]+' .clangd) status=0 for f in "${rel_files[@]}"; do