From bfc17f027ba7b9d88ce1beb5584f15c7492ce408 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh Date: Mon, 2 Nov 2026 12:50:21 +0530 Subject: [PATCH 01/11] sdk%feat(build): add `clang-{format,tidy}` 20 to workspace with rules --- contrib/nix/mods/nixpkgs.nix | 3 ++- contrib/nix/shell/common.nix | 2 +- maint/.clang-format | 32 ++++++++++++++++++++++ maint/.clang-tidy | 51 ++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 ++ uv.lock | 47 ++++++++++++++++++++++++++++++++- 6 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 maint/.clang-format create mode 100644 maint/.clang-tidy diff --git a/contrib/nix/mods/nixpkgs.nix b/contrib/nix/mods/nixpkgs.nix index 4c9f55058..d1a3eabae 100644 --- a/contrib/nix/mods/nixpkgs.nix +++ b/contrib/nix/mods/nixpkgs.nix @@ -1,6 +1,6 @@ # Native packages sourced from the Nix Packages collection (nixpkgs) -{ pkgs }: +{ pkgs, llvm }: let # nixpkgs builds `zensical` against its own interpreter, which lacks @@ -37,6 +37,7 @@ in pkgs.wasm-pack # Packages synced with '.tools' from 'pyproject.toml'. + llvm.clang-tools pkgs.ruff pkgs.semgrep pkgs.taplo diff --git a/contrib/nix/shell/common.nix b/contrib/nix/shell/common.nix index 5fdc32716..342865756 100644 --- a/contrib/nix/shell/common.nix +++ b/contrib/nix/shell/common.nix @@ -93,7 +93,7 @@ in codeql = import ../mods/codeql.nix { inherit pkgs unstable; }; cxx = cxx.compiler; fhs = import ../mods/fhs.nix { inherit pkgs lib; }; - nixpkgs = import ../mods/nixpkgs.nix { inherit pkgs; }; + nixpkgs = import ../mods/nixpkgs.nix { inherit pkgs llvm; }; python = import ../mods/python.nix { inherit pkgs lib; inherit (inputs) uv2nix pyproject-nix pyproject-build-systems; diff --git a/maint/.clang-format b/maint/.clang-format new file mode 100644 index 000000000..017dfe4df --- /dev/null +++ b/maint/.clang-format @@ -0,0 +1,32 @@ +BasedOnStyle: WebKit +Standard: c++17 +IndentWidth: 2 +AccessModifierOffset: -2 +ContinuationIndentWidth: 2 +ObjCBlockIndentWidth: 2 +ColumnLimit: 100 +PointerAlignment: Left +FixNamespaceComments: true +SortIncludes: CaseSensitive +InsertNewlineAtEOF: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortLambdasOnASingleLine: Inline +BinPackArguments: false +BinPackParameters: false +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterFunction: true +BreakConstructorInitializers: BeforeComma +BreakTemplateDeclarations: Yes +AlignAfterOpenBracket: BlockIndent +AlignEscapedNewlines: Left +Cpp11BracedListStyle: true +SpaceBeforeCpp11BracedList: false +IncludeBlocks: Preserve +InsertBraces: true +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +BreakBeforeBinaryOperators: None +KeepEmptyLinesAtTheStartOfBlocks: false +AllowShortBlocksOnASingleLine: Empty diff --git a/maint/.clang-tidy b/maint/.clang-tidy new file mode 100644 index 000000000..6023bb9b1 --- /dev/null +++ b/maint/.clang-tidy @@ -0,0 +1,51 @@ +HeaderFilterRegex: '.*' +ExcludeHeaderFilterRegex: '/fuzz/pow/sphlib/' +WarningsAsErrors: '*' +ExtraArgs: ['-Wdocumentation'] +Checks: ' +-*, + +clang-diagnostic-documentation*, + +bugprone-argument-comment, +bugprone-assert-side-effect, +bugprone-dangling-handle, +bugprone-forwarding-reference-overload, +bugprone-lambda-function-name, +bugprone-move-forwarding-reference, +bugprone-string-constructor, +bugprone-suspicious-include, +bugprone-unhandled-self-assignment, +bugprone-unused-return-value, +bugprone-use-after-move, + +misc-static-assert, +misc-unconventional-assign-operator, +misc-unused-using-decls, + +modernize-avoid-bind, +modernize-avoid-c-arrays, +modernize-deprecated-headers, +modernize-use-default-member-init, +modernize-use-emplace, +modernize-use-equals-default, +modernize-use-equals-delete, +modernize-use-noexcept, +modernize-use-nullptr, +modernize-use-starts-ends-with, +modernize-use-using, + +performance-*, +-performance-avoid-endl, +-performance-enum-size, +-performance-inefficient-string-concatenation, +-performance-no-int-to-ptr, +-performance-noexcept-move-constructor, + +readability-avoid-const-params-in-decls, +readability-const-return-type, +readability-container-contains, +readability-duplicate-include, +readability-redundant-declaration, +readability-redundant-string-init, +' diff --git a/pyproject.toml b/pyproject.toml index 6ed2edd15..51f83cbfb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,8 @@ lib = [ "rjsmin>=1.2", ] tools = [ + "clang-format==20.1.3", + "clang-tidy==20.1.0", "ruff>=0.9", "semgrep>=1.118.0", "taplo>=0.9.3; sys_platform != 'linux' or platform_machine != 'aarch64'", diff --git a/uv.lock b/uv.lock index 2e7bb03a5..3efc62df8 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 5 requires-python = ">=3.11" resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -339,6 +339,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fc/ad/d07d7862a62ffa6d79d68074d14823243dd235a77c45262acbf6adeb28bf/charset_normalizer-3.5.2-py3-none-any.whl", hash = "sha256:b6b751274acb69d77b3323d6b7dbaa3c7fdfc1eb829b7eb61d262f32e1af9685", size = 68872, upload-time = "2026-09-30T04:39:21.828Z" }, ] +[[package]] +name = "clang-format" +version = "20.1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/f3/c6641b19aad867eff10add438e1a8f4fd2f801a6a641daed865173cc0794/clang_format-20.1.3.tar.gz", hash = "sha256:3ec909edce6d9ef1f6f0f78b0ef75519a88e9678c4ea3758d39b08c3acfabd1b", size = 11225, upload-time = "2025-04-28T13:35:36.964Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/57/11efc2f350454f421a9e9cbdce6046c7a12f2fa3537e946b03bdc2871dc0/clang_format-20.1.3-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:1380f866e4ca1dd92167d9fb7f7847fdf2b11a6f435811da15ee5e04853aaf1b", size = 1436122, upload-time = "2025-04-28T13:35:13.18Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ab/4311f99f6cb3cb88146f08b63224d1fa7963206b5b4f4582e662518e4bd3/clang_format-20.1.3-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:e6440f53333a42c46ea3f78cbf2f19ccec2d40f4233ee7ca027d3b6a75e81558", size = 1410102, upload-time = "2025-04-28T13:35:15.587Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c3/244e279da51c9622c548641c9e398fa58f0b414cfb4e1ef2ddd7034061b7/clang_format-20.1.3-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5aff27ada318fa5204a5f916779b53c44c12a41e2de42ec0b2bc336865fddb2", size = 1793492, upload-time = "2025-04-28T13:35:17.551Z" }, + { url = "https://files.pythonhosted.org/packages/e6/93/1dfc36705e611fb78eff5843f612459490a1f185cc46d961eceb39841c67/clang_format-20.1.3-py2.py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40272f1ba17c430ac8f6ffc4e6bf4a57e859ce655438afea677faf962da20de0", size = 1968079, upload-time = "2025-04-28T13:35:18.989Z" }, + { url = "https://files.pythonhosted.org/packages/c7/33/2bf9c32f405b9393a3f99f027d3e6ba4e0a98b8a17dd09ff049f6dfbbeac/clang_format-20.1.3-py2.py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:622cf026ce331c33b267f05edea027cee3b6ca96028ea695c8c73708d99e9918", size = 2724419, upload-time = "2025-04-28T13:35:20.401Z" }, + { url = "https://files.pythonhosted.org/packages/3d/cf/3b84d415c570e4d5c3120f83de3832b8698a0cc765384d1da5161bfd5d58/clang_format-20.1.3-py2.py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:789944ef03de0b97bd5f621d3f05d3cf8a0a2a77cc937ed5bc334184fa223fc8", size = 1788249, upload-time = "2025-04-28T13:35:21.83Z" }, + { url = "https://files.pythonhosted.org/packages/d3/e2/70887cb55ef738be4e7431d2f0280e305953e6a1406e52548b7b9d1dd0ba/clang_format-20.1.3-py2.py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de902d607f54ce2047f45afae29d2ecefe2138b73558f619d2758a619bac75c2", size = 1800488, upload-time = "2025-04-28T13:35:23.244Z" }, + { url = "https://files.pythonhosted.org/packages/70/ae/cae4b5678970035f27a3594c43a22f3a434dafff9460c29d4c00e0582269/clang_format-20.1.3-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2ff61a21394124e11b51d8ac7b3cda7a19643a2b45c0b20381dc6757d3764e46", size = 2775071, upload-time = "2025-04-28T13:35:24.735Z" }, + { url = "https://files.pythonhosted.org/packages/6e/9c/617c1d951189f4c16aa5129f5f4642c15c14ef04047883096dd88e29237b/clang_format-20.1.3-py2.py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9edfd36ffb9566e24c35b0783a98275dbcd70ec73efaf51fe9ea22df1b11587", size = 3100386, upload-time = "2025-04-28T13:35:27.171Z" }, + { url = "https://files.pythonhosted.org/packages/86/3f/f316c56914692947c2874533d71277bb403274be7b4430b230944fc75dd6/clang_format-20.1.3-py2.py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:67c182d447d351423a0b80de446a459f9833ba234e32a3fb4a93e04d995468d2", size = 3186173, upload-time = "2025-04-28T13:35:28.747Z" }, + { url = "https://files.pythonhosted.org/packages/33/fb/01b500cd177f6cc3ce0176b049afc3ac17fc1a47b3ef1c214fb4ca2714bb/clang_format-20.1.3-py2.py3-none-musllinux_1_2_s390x.whl", hash = "sha256:6491d9dd9eb5e92e4a0554d5f7200d93d27e0e6bf440d780a7d525d7c6991ec3", size = 3224130, upload-time = "2025-04-28T13:35:30.553Z" }, + { url = "https://files.pythonhosted.org/packages/75/96/f502059ac94e38fa21d67b2ea4063671d8a9f041f88e145d4db63066822c/clang_format-20.1.3-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:98d508344b830783e8d2dac76f63fbc3c21b7f299fa83715e7f71702479d0bda", size = 2881596, upload-time = "2025-04-28T13:35:31.972Z" }, + { url = "https://files.pythonhosted.org/packages/1f/4d/8a49531b8cf6e006943e573390b142ed2914ac107da38c9f3df522974edf/clang_format-20.1.3-py2.py3-none-win32.whl", hash = "sha256:8ab1c0fe45e6baa34910cc9195160a2854ac71ae40d6f83ae5b4dc467969d1ab", size = 1258802, upload-time = "2025-04-28T13:35:33.432Z" }, + { url = "https://files.pythonhosted.org/packages/08/e0/0bd989900eadb896520442f80d6af1a67bbc5fd0ace965a425c979c3943d/clang_format-20.1.3-py2.py3-none-win_amd64.whl", hash = "sha256:2584cac1070c058432c6374bac0099464e8c2f5e61bbe51cd8ad58c883e0911f", size = 1443493, upload-time = "2025-04-28T13:35:35.213Z" }, +] + +[[package]] +name = "clang-tidy" +version = "20.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/f3/f48da67de400ad36a784dac9513d5c7ade2d96705cad0f4c0c6d11828d0a/clang_tidy-20.1.0.tar.gz", hash = "sha256:f14eb62003f0ddfec6dd638a4fd6af4c46e24615dd22d55aa6beec374d2fa1d5", size = 11300, upload-time = "2025-03-13T12:53:09.267Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/5b/dcfc84b895d8544e00186738ca85132bbd14db4d11dbe39502630ece5391/clang_tidy-20.1.0-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:85544b69c34217a25272733194f0fafd2563d92fd170ba1e6497792219f49f87", size = 27888003, upload-time = "2025-03-13T12:52:35.058Z" }, + { url = "https://files.pythonhosted.org/packages/95/02/838baf08764b08327322096bda55e8d1e2344e4a13b9308e5642cfaafd8e/clang_tidy-20.1.0-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:812e27de4b006ca2f1b24c1ff6efb9a5fb01dac5eea48cb68cd7d7493a148996", size = 26795920, upload-time = "2025-03-13T12:52:38.704Z" }, + { url = "https://files.pythonhosted.org/packages/be/61/9e1a0797639e81c41d38d7b8b2508a9be4b05b9a23baa9d64e7284d07238/clang_tidy-20.1.0-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14125fd35b2d324da33c884ab8b5a1d9d70a5a2e76f071ca14783d325a992917", size = 37776733, upload-time = "2025-03-13T12:52:42.736Z" }, + { url = "https://files.pythonhosted.org/packages/69/41/80c3878c0fc39a0cc2923f03043276a5b52e8c2303ff937cce5914a563fc/clang_tidy-20.1.0-py2.py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d78e47463988dd38c9ef8f6aa40bab7974876bbc73150b6a34fed34920a655e", size = 43829430, upload-time = "2025-03-13T12:52:46.856Z" }, + { url = "https://files.pythonhosted.org/packages/52/76/42c61be1c1fdf8bacdbb265f0cd3e11321fee7362f91fa840717a6a41ad6/clang_tidy-20.1.0-py2.py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e205541e350044ffe0b0d1fcaf62b9953890f8ecba31458bbcdcfe73040e58b6", size = 38429166, upload-time = "2025-03-13T12:52:51.42Z" }, + { url = "https://files.pythonhosted.org/packages/34/ee/94926ea7d2d43e040555f691db532abe69860ec29c4a27097da57dd54c3c/clang_tidy-20.1.0-py2.py3-none-musllinux_1_2_i686.whl", hash = "sha256:ce8bbe64bdc4d616f184fc51d9fce4d9c8d50ff973a3af7787ff65a878bc1c31", size = 46206649, upload-time = "2025-03-13T12:52:56.258Z" }, + { url = "https://files.pythonhosted.org/packages/63/54/3e7f5e0539c29f9fa1943525453ebd41251b9762906fcd0bae6c74ed7a46/clang_tidy-20.1.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9c2162c4b776b14dff84f3c4d69d31fa2fa9bc9b10ff4708e50e444f2fa7bef3", size = 41510721, upload-time = "2025-03-13T12:53:00.482Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cd/a7b5de57a3af1ce388ecff7f9ef48530af060c0868a6374aeb56e6281ffe/clang_tidy-20.1.0-py2.py3-none-win32.whl", hash = "sha256:fd43cd33868c4bbd4d1ac14377ca7b393e9b31872d0869f9c411606b31f5ca5b", size = 20401100, upload-time = "2025-03-13T12:53:04.096Z" }, + { url = "https://files.pythonhosted.org/packages/02/f0/dd985d9d9b76f8c39f1995aa475d8d5aabbea0d3e0cf498df44dc7bf1cb0/clang_tidy-20.1.0-py2.py3-none-win_amd64.whl", hash = "sha256:bd850dd40c4a14539f0aa8c80e8fa84a6f50e54bd967267a4a219cf7f66e0114", size = 23211436, upload-time = "2025-03-13T12:53:06.993Z" }, +] + [[package]] name = "click" version = "8.4.2" @@ -461,6 +500,8 @@ source = { editable = "." } [package.optional-dependencies] dev = [ + { name = "clang-format" }, + { name = "clang-tidy" }, { name = "lief" }, { name = "markdown" }, { name = "pygments-styles" }, @@ -481,6 +522,8 @@ lib = [ { name = "rjsmin" }, ] tools = [ + { name = "clang-format" }, + { name = "clang-tidy" }, { name = "ruff" }, { name = "semgrep" }, { name = "taplo", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, @@ -489,6 +532,8 @@ tools = [ [package.metadata] requires-dist = [ + { name = "clang-format", marker = "extra == 'tools'", specifier = "==20.1.3" }, + { name = "clang-tidy", marker = "extra == 'tools'", specifier = "==20.1.0" }, { name = "dash-base-sdk", extras = ["tools", "lib"], marker = "extra == 'dev'" }, { name = "lief", marker = "extra == 'lib'", specifier = ">=1.0" }, { name = "markdown", marker = "extra == 'lib'", specifier = ">=3.7" }, From ae1b9717290892a173f55b7a9ad0d037846b43ae Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh Date: Mon, 2 Nov 2026 13:07:45 +0530 Subject: [PATCH 02/11] sdk%feat(lint): add `lint_clang.py` for managing `clang-{format,tidy}` --- .github/workflows/build_stable.yml | 1 + maint/README.md | 1 + maint/lint/lint_clang.py | 221 +++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100755 maint/lint/lint_clang.py diff --git a/.github/workflows/build_stable.yml b/.github/workflows/build_stable.yml index 1a856d777..e3cb9017a 100644 --- a/.github/workflows/build_stable.yml +++ b/.github/workflows/build_stable.yml @@ -29,6 +29,7 @@ jobs: uses: actions/checkout@v7 with: fetch-depth: 100 + submodules: true - name: Check cache write access id: cache_write diff --git a/maint/README.md b/maint/README.md index 3b1a73eda..37b6f1313 100644 --- a/maint/README.md +++ b/maint/README.md @@ -17,6 +17,7 @@ use [`lint_all.py`](./lint_all.py). | [`lint_commit.py`](./lint/lint_commit.py) | Lint commit names against [`commit.toml`](./commit.toml) | `run` | `git` | | [`lint_bazel.py`](./lint/lint_bazel.py) | Lint Starlark files with `buildifier`, hold the rustc flags and edition Bazel builds with to cargo's, and the crates Bazel builds to cargo's graph | `check`, `apply`, `apply-all`, `graph` | `buildifier`, `bazel` and `cargo` (`graph`) | | [`lint_cargo.py`](./lint/lint_cargo.py) | Deny dependencies per [`deny.toml`](../.cargo/deny.toml), check/format TOML files against [`taplo.toml`](./taplo.toml) | `check` , `apply`, `apply-all` | `cargo-deny` (deny dependencies), `taplo` (TOML formatting) | +| [`lint_clang.py`](./lint/lint_clang.py) | Check/format C and C++ sources against [`.clang-format`](./.clang-format), lint them against [`.clang-tidy`](./.clang-tidy) | `check`, `apply`, `apply-all` | `clang-format` (formatting), `clang-tidy` (lint), `git` | | [`lint_codeql.py`](./lint/lint_codeql.py) | Query Rust sources against [`maint/codeql/rust/*.ql`](./codeql/rust) | `check`, `apply`, `apply-all`, `run`, `run-all` | `codeql`, `rustc` | | [`lint_javascript.py`](./lint/lint_javascript.py) | Lint Javascript sources against [`eslint.config.mjs`](js/eslint.config.mjs) | *None* | `npx` (part of Node.js), `eslint` (auto-retrieved by script) | | [`lint_just.py`](./lint/lint_just.py) | Lint the root `Justfile` and every module against `just --fmt` | `check`, `apply`, `apply-all` | `just`, `git` | diff --git a/maint/lint/lint_clang.py b/maint/lint/lint_clang.py new file mode 100755 index 000000000..67731c66f --- /dev/null +++ b/maint/lint/lint_clang.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 +# coding: latin-1 + +# +# Copyright (c) 2026-present, The Dash Core developers +# SPDX-License-Identifier: MIT +# See the accompanying file LICENSE or https://opensource.org/license/MIT +# + +"""Check (and apply) clang-format styling, and lint with clang-tidy.""" + +from __future__ import annotations + +import os +import platform +import re +import subprocess +import sys +from pathlib import Path + +from common import ( + DEFAULT_BASE, + RETCODE_ERR, + RETCODE_PASS, + RETCODE_SKIP, + declare_verbs, + formatted, + relay, + repo_files, + require_bin, + root_dir, + touched, +) + +# Base name of this script (equivalent to argv[0]). +SCRIPT = Path(__file__).stem + +# Translation units, visited by `clang-tidy`. +UNITS: tuple[str, ...] = (".c", ".cc", ".cpp", ".cxx") + +# Headers, visited by `clang-format`. +HEADERS: tuple[str, ...] = (".h", ".hh", ".hpp", ".hxx") + +# Include directories a unit is built with, keyed by the directory holding it. +INCLUDES: dict[str, tuple[str, ...]] = { + "fuzz/pow": ("fuzz/pow/sphlib/c",), +} + +# Progress and summary lines clang-tidy prints per unit, naming no fault. +_NOISE_RE = re.compile( + r"^(\[\d+/\d+\] Processing file .*" + r"|\d+ warnings?( and \d+ errors?)? generated\.)$", +) + + +def _sources(repo_root: Path, only: list[str] | None) -> list[Path]: + """Return the C and C++ files to lint, or just *only* when given.""" + if only is not None: + return [repo_root / name for name in only] + return repo_files(repo_root, *(f"*{s}" for s in UNITS + HEADERS)) + + +def _includes(repo_root: Path, unit: Path) -> tuple[str, ...]: + """Return the include directories *unit* is built with.""" + relative = unit.relative_to(repo_root) + return tuple( + include + for holder, includes in INCLUDES.items() + if relative.is_relative_to(holder) + for include in includes + ) + + +def _environ() -> dict[str, str]: + """Return the environment clang-tidy runs in. + + A clang that is not Apple's finds the macOS system headers only through + 'SDKROOT', so it is filled in from xcrun when unset. + """ + env = dict(os.environ) + if platform.system() != "Darwin" or env.get("SDKROOT"): + return env + try: + xcrun = require_bin("xcrun") + except FileNotFoundError: + return env + env["SDKROOT"] = subprocess.run( # noqa: S603 + [xcrun, "--show-sdk-path"], + capture_output=True, + check=True, + text=True, + ).stdout.strip() + return env + + +def _check_format( + repo_root: Path, + sources: list[Path], + *, + fix: bool, + scoped: bool, +) -> int | None: + """Format or check *sources*, or None when clang-format is absent.""" + try: + clang_format = require_bin("clang-format") + except FileNotFoundError as e: + print(f"{e}, skipping the format check", file=sys.stderr) + return None + + style = repo_root / "maint" / ".clang-format" + return formatted( + SCRIPT, + "C/C++ file", + sources, + lambda paths: [ + clang_format, + f"--style=file:{style}", + *(["-i"] if fix else ["--dry-run", "--Werror"]), + *[str(p) for p in paths], + ], + fix=fix, + scoped=scoped, + cwd=repo_root, + output=lambda out, err: relay(out + err, repo_root, stream=sys.stderr), + ) + + +def _check_tidy(repo_root: Path, sources: list[Path]) -> int | None: + """Lint the units among *sources*, or None when none could be linted.""" + try: + clang_tidy = require_bin("clang-tidy") + except FileNotFoundError as e: + print(f"{e}, skipping the lint", file=sys.stderr) + return None + + units = [s for s in sources if s.suffix in UNITS] + if not units: + print(f"{SCRIPT}: no C/C++ unit was touched") + return RETCODE_PASS + + # Units sharing include directories share one run. + groups: dict[tuple[str, ...], list[Path]] = {} + for unit in units: + groups.setdefault(_includes(repo_root, unit), []).append(unit) + + config = repo_root / "maint" / ".clang-tidy" + env = _environ() + linted = 0 + failed = False + for includes, members in groups.items(): + # An uninitialised submodule leaves its include directory absent. + absent = [i for i in includes if not (repo_root / i).is_dir()] + if absent: + print( + f"{', '.join(absent)} is absent, skipping the lint of " + f"{len(members)} unit(s)", + file=sys.stderr, + ) + continue + result = subprocess.run( # noqa: S603 + [ + clang_tidy, + "--quiet", + f"--config-file={config}", + *[str(m) for m in members], + "--", + *[f"-I{repo_root / i}" for i in includes], + ], + capture_output=True, + check=False, + cwd=str(repo_root), + env=env, + text=True, + ) + relay( + result.stdout + result.stderr, + repo_root, + stream=sys.stderr, + drop=lambda line: _NOISE_RE.match(line) is not None, + ) + linted += len(members) + failed = failed or result.returncode != 0 + + if failed: + return RETCODE_ERR + if not linted: + return None + print(f"{SCRIPT}: {linted} C/C++ unit(s) clean") + return RETCODE_PASS + + +def main() -> int: + args = declare_verbs( + __doc__ or "", + { + "check": "report every fault, changing nothing", + "apply": f"also rewrite C/C++ this branch changed vs {DEFAULT_BASE}", + "apply-all": "also rewrite every C/C++ file in the tree", + }, + ).parse_args() + fix = args.verb.startswith("apply") + repo_root = root_dir() + only = touched(repo_root, UNITS + HEADERS) if args.verb == "apply" else None + sources = _sources(repo_root, only) + + verdicts: list[int | None] = [ + _check_format(repo_root, sources, fix=fix, scoped=only is not None), + _check_tidy(repo_root, sources), + ] + ran = [v for v in verdicts if v is not None] + if not ran: + return RETCODE_SKIP + return RETCODE_ERR if any(v != RETCODE_PASS for v in ran) else RETCODE_PASS + + +if __name__ == "__main__": + try: + sys.exit(main()) + except Exception as exc: # noqa: BLE001 + print(exc, file=sys.stderr) + sys.exit(RETCODE_ERR) From 297d32bbe06494491f8c45108623ce887f2d882f Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh Date: Mon, 2 Nov 2026 13:59:12 +0530 Subject: [PATCH 03/11] sdk%feat(lint): enforce C/C++ comment style with semgrep --- AGENTS.md | 2 ++ maint/common.py | 6 ++++++ maint/lint/lint_clang.py | 18 +++++++++--------- maint/lint/lint_semgrep.py | 3 +++ maint/semgrep/cxx/style.yml | 29 +++++++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 maint/semgrep/cxx/style.yml diff --git a/AGENTS.md b/AGENTS.md index acb864f6d..a9a054de8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,6 +26,8 @@ The full guide is at [`docs/dev/guide_rust.md`](./docs/dev/guide_rust.md). Key p fragmentation. - No vague references (`the other`, `that thing`); name the item. - No exposition, history, or conversational tone. + - C/C++ doc comments are Javadoc-style `/** */` blocks with `@` commands (`@param[in]`, `@return`). `//!` (and + `//!<`) suits variables and members, `/*!` suits copyright headers. Never `///`. - **Code segmentation**: organise code through modules (in-file or separate files) and naming prefixes. Never use decorative separator comments (`// ----`, `// ====`, `// -- Section --`). Latin-1/ISO 8859-1 characters in source files only; no Unicode dashes, arrows, box drawing, or other decoration in comments or identifiers. diff --git a/maint/common.py b/maint/common.py index dbdfe659d..038ee18ca 100644 --- a/maint/common.py +++ b/maint/common.py @@ -38,6 +38,12 @@ # Rust source roots the analysers scan, relative to the repository root. SOURCE_DIRS: tuple[str, ...] = ("meta", "pkgs", "docs/samples", "fuzz") +# C and C++ translation units. +CXX_UNITS: tuple[str, ...] = (".c", ".cc", ".cpp", ".cxx") + +# C and C++ headers; a parser reaches them only through a unit. +CXX_HEADERS: tuple[str, ...] = (".h", ".hh", ".hpp", ".hxx") + # Assumed base branch for codebase. DEFAULT_BASE = "develop" diff --git a/maint/lint/lint_clang.py b/maint/lint/lint_clang.py index 67731c66f..868c92a65 100755 --- a/maint/lint/lint_clang.py +++ b/maint/lint/lint_clang.py @@ -19,6 +19,8 @@ from pathlib import Path from common import ( + CXX_HEADERS, + CXX_UNITS, DEFAULT_BASE, RETCODE_ERR, RETCODE_PASS, @@ -35,12 +37,6 @@ # Base name of this script (equivalent to argv[0]). SCRIPT = Path(__file__).stem -# Translation units, visited by `clang-tidy`. -UNITS: tuple[str, ...] = (".c", ".cc", ".cpp", ".cxx") - -# Headers, visited by `clang-format`. -HEADERS: tuple[str, ...] = (".h", ".hh", ".hpp", ".hxx") - # Include directories a unit is built with, keyed by the directory holding it. INCLUDES: dict[str, tuple[str, ...]] = { "fuzz/pow": ("fuzz/pow/sphlib/c",), @@ -57,7 +53,7 @@ def _sources(repo_root: Path, only: list[str] | None) -> list[Path]: """Return the C and C++ files to lint, or just *only* when given.""" if only is not None: return [repo_root / name for name in only] - return repo_files(repo_root, *(f"*{s}" for s in UNITS + HEADERS)) + return repo_files(repo_root, *(f"*{s}" for s in CXX_UNITS + CXX_HEADERS)) def _includes(repo_root: Path, unit: Path) -> tuple[str, ...]: @@ -133,7 +129,7 @@ def _check_tidy(repo_root: Path, sources: list[Path]) -> int | None: print(f"{e}, skipping the lint", file=sys.stderr) return None - units = [s for s in sources if s.suffix in UNITS] + units = [s for s in sources if s.suffix in CXX_UNITS] if not units: print(f"{SCRIPT}: no C/C++ unit was touched") return RETCODE_PASS @@ -200,7 +196,11 @@ def main() -> int: ).parse_args() fix = args.verb.startswith("apply") repo_root = root_dir() - only = touched(repo_root, UNITS + HEADERS) if args.verb == "apply" else None + only = ( + touched(repo_root, CXX_UNITS + CXX_HEADERS) + if args.verb == "apply" + else None + ) sources = _sources(repo_root, only) verdicts: list[int | None] = [ diff --git a/maint/lint/lint_semgrep.py b/maint/lint/lint_semgrep.py index 745fd5f9c..b2fa19ce4 100755 --- a/maint/lint/lint_semgrep.py +++ b/maint/lint/lint_semgrep.py @@ -19,6 +19,8 @@ from pathlib import Path from common import ( + CXX_HEADERS, + CXX_UNITS, RETCODE_ERR, RETCODE_PASS, RETCODE_SKIP, @@ -33,6 +35,7 @@ # Pathspecs each language's rules are scanned against. LANGUAGES: dict[str, tuple[str, ...]] = { + "cxx": tuple(f"*{s}" for s in CXX_UNITS + CXX_HEADERS), "rust": SOURCE_DIRS, } diff --git a/maint/semgrep/cxx/style.yml b/maint/semgrep/cxx/style.yml new file mode 100644 index 000000000..577697c52 --- /dev/null +++ b/maint/semgrep/cxx/style.yml @@ -0,0 +1,29 @@ +rules: + - id: style-doc-no-triple-slash + message: "doc comments use /** */ blocks, //! or /*!, not ///" + severity: ERROR + languages: [regex] + pattern-regex: '(?m)^\s*///|///<' + + - id: style-doc-at-commands + message: "doxygen commands are spelt with @, not a backslash" + severity: ERROR + languages: [regex] + pattern-regex: |- + (?xm) + ^[ \t]* (?: /\*[*!] | //! | \* ) # a doc comment line or continuation + .*? \\ # ... holding a backslash command + (?: a | addtogroup | b | brief | c | code | copydoc | defgroup + | deprecated | details | e | endcode | exception | file | ingroup + | note | p | par | param | post | pre | ref | return | returns + | retval | sa | see | since | throw | throws | todo | tparam + | warning ) \b + + - id: style-no-separator-comments + message: "decorative separator comments are not permitted" + severity: ERROR + languages: [regex] + pattern-regex: |- + (?xm) + ^[ \t]* (?: //[/!]? | /\*[*!]? ) # a line or block comment + (?: [ \t]* [-=]{4,} | [ \t]+ -- [ \t] ) # ---- or a -- Section -- banner From 944b821243fcb4dba39112ecd92dc0aa2af2595f Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh Date: Mon, 2 Nov 2026 14:11:33 +0530 Subject: [PATCH 04/11] sdk%lint: format C/C++ sources with `clang-format` --- contrib/probes/hello.c | 10 +++++++--- contrib/probes/hello.cpp | 13 +++++++++---- fuzz/pow/src/shim.c | 13 +++++++------ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/contrib/probes/hello.c b/contrib/probes/hello.c index 386fd7d41..e48a08560 100644 --- a/contrib/probes/hello.c +++ b/contrib/probes/hello.c @@ -21,7 +21,8 @@ static int say(const char* text, DWORD length) { HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); DWORD written = 0; - return out != INVALID_HANDLE_VALUE && WriteFile(out, text, length, &written, NULL) && written == length; + return out != INVALID_HANDLE_VALUE && WriteFile(out, text, length, &written, NULL) && + written == length; } #elif defined(__APPLE__) static int say(const char* text, size_t length) @@ -38,7 +39,8 @@ static int say(const char* text, size_t length) } dispatch_queue_t queue = dispatch_get_global_queue(qos, 0); dispatch_semaphore_t done = dispatch_semaphore_create(0); - dispatch_data_t data = dispatch_data_create(text, length, queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + dispatch_data_t data = + dispatch_data_create(text, length, queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT); __block int failed = 1; dispatch_write(STDOUT_FILENO, data, queue, ^(dispatch_data_t rest, int error) { failed = rest != NULL || error != 0; @@ -48,7 +50,9 @@ static int say(const char* text, size_t length) return !failed; } #elif defined(__linux__) -_Static_assert(LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0), "the UAPI headers predate Linux 5.10"); +_Static_assert( + LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0), "the UAPI headers predate Linux 5.10" +); static int say(const char* text, size_t length) { diff --git a/contrib/probes/hello.cpp b/contrib/probes/hello.cpp index e5de6c701..f2ce8bbda 100644 --- a/contrib/probes/hello.cpp +++ b/contrib/probes/hello.cpp @@ -24,7 +24,8 @@ bool say(const std::string& text) HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); DWORD written = 0; const auto length = static_cast(text.size()); - return out != INVALID_HANDLE_VALUE && WriteFile(out, text.data(), length, &written, nullptr) && written == length; + return out != INVALID_HANDLE_VALUE && WriteFile(out, text.data(), length, &written, nullptr) && + written == length; } #elif defined(__APPLE__) bool say(const std::string& text) @@ -41,7 +42,8 @@ bool say(const std::string& text) } dispatch_queue_t queue = dispatch_get_global_queue(qos, 0); dispatch_semaphore_t done = dispatch_semaphore_create(0); - dispatch_data_t data = dispatch_data_create(text.data(), text.size(), queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + dispatch_data_t data = + dispatch_data_create(text.data(), text.size(), queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT); __block bool failed = true; dispatch_write(STDOUT_FILENO, data, queue, ^(dispatch_data_t rest, int error) { failed = rest != nullptr || error != 0; @@ -51,11 +53,14 @@ bool say(const std::string& text) return !failed; } #elif defined(__linux__) -static_assert(LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0), "the UAPI headers predate Linux 5.10"); +static_assert( + LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0), "the UAPI headers predate Linux 5.10" +); bool say(const std::string& text) { - return syscall(SYS_write, STDOUT_FILENO, text.data(), text.size()) == static_cast(text.size()); + return syscall(SYS_write, STDOUT_FILENO, text.data(), text.size()) == + static_cast(text.size()); } #endif diff --git a/fuzz/pow/src/shim.c b/fuzz/pow/src/shim.c index 55d4b23bd..758fed323 100644 --- a/fuzz/pow/src/shim.c +++ b/fuzz/pow/src/shim.c @@ -20,12 +20,13 @@ #include "sph_simd.h" #include "sph_skein.h" -#define DASH_SPH(name) \ - void dash_sph_##name(const void *data, size_t len, unsigned char *out) { \ - sph_##name##512_context cc; \ - sph_##name##512_init(&cc); \ - sph_##name##512(&cc, data, len); \ - sph_##name##512_close(&cc, out); \ +#define DASH_SPH(name) \ + void dash_sph_##name(const void* data, size_t len, unsigned char* out) \ + { \ + sph_##name##512_context cc; \ + sph_##name##512_init(&cc); \ + sph_##name##512(&cc, data, len); \ + sph_##name##512_close(&cc, out); \ } DASH_SPH(blake) From 40dc4a1a2c59736019a5ca3fddc4645ae00f43b3 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh Date: Mon, 2 Nov 2026 16:46:32 +0530 Subject: [PATCH 05/11] sdk%ci(nix): fetch per-platform CodeQL 2.27, drop `unstable` channel --- .github/workflows/build_stable.yml | 4 ++ contrib/nix/flake.lock | 17 ------ contrib/nix/flake.nix | 9 ---- contrib/nix/mods/codeql.nix | 83 +++++++++++++++++++++++++----- contrib/nix/mods/fhs.nix | 1 + contrib/nix/shell/common.nix | 7 +-- 6 files changed, 78 insertions(+), 43 deletions(-) diff --git a/.github/workflows/build_stable.yml b/.github/workflows/build_stable.yml index e3cb9017a..86fbd079e 100644 --- a/.github/workflows/build_stable.yml +++ b/.github/workflows/build_stable.yml @@ -51,6 +51,10 @@ jobs: with: large-packages: false + - name: Allow unprivileged user namespaces + shell: bash + run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 + - name: Install Nix uses: DeterminateSystems/nix-installer-action@v23 diff --git a/contrib/nix/flake.lock b/contrib/nix/flake.lock index b553c682b..47488c7b3 100644 --- a/contrib/nix/flake.lock +++ b/contrib/nix/flake.lock @@ -32,22 +32,6 @@ "type": "github" } }, - "nixpkgs-unstable": { - "locked": { - "lastModified": 1789632929, - "narHash": "sha256-RjR8AyvGlWuw16XRj7C1YDEw4E27ciuyPeB2nDMzgTU=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "a32edd7654519351e48e80372a928df336394670", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, "pyproject-build-systems": { "inputs": { "nixpkgs": [ @@ -98,7 +82,6 @@ "inputs": { "nixpkgs": "nixpkgs", "nixpkgs-llvm": "nixpkgs-llvm", - "nixpkgs-unstable": "nixpkgs-unstable", "pyproject-build-systems": "pyproject-build-systems", "pyproject-nix": "pyproject-nix", "rust-overlay": "rust-overlay", diff --git a/contrib/nix/flake.nix b/contrib/nix/flake.nix index 911c1ba43..ffc50dc6b 100644 --- a/contrib/nix/flake.nix +++ b/contrib/nix/flake.nix @@ -6,8 +6,6 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05"; - nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - nixpkgs-llvm.url = "github:NixOS/nixpkgs/3e2cf88148e732abc1d259286123e06a9d8c964a"; pyproject-build-systems = { @@ -38,7 +36,6 @@ { nixpkgs, nixpkgs-llvm, - nixpkgs-unstable, rust-overlay, ... }@inputs: @@ -62,10 +59,6 @@ config.allowUnfree = true; overlays = [ rust-overlay.overlays.default ]; }; - unstable = import nixpkgs-unstable { - inherit system; - config.allowUnfree = true; - }; llvm = (import nixpkgs-llvm { inherit system; }).llvmPackages_20; } ); @@ -74,7 +67,6 @@ devShells = eachSystem ( { pkgs, - unstable, llvm, }: let @@ -84,7 +76,6 @@ lib inputs llvm - unstable ; root = ../..; }; diff --git a/contrib/nix/mods/codeql.nix b/contrib/nix/mods/codeql.nix index 06321a410..fbfc28448 100644 --- a/contrib/nix/mods/codeql.nix +++ b/contrib/nix/mods/codeql.nix @@ -1,25 +1,80 @@ # CodeQL CLI -{ pkgs, unstable }: +{ pkgs, fhs }: let - version = "2.27.0"; + inherit (pkgs) lib stdenv; - # Fetching more recent CodeQL releases requires `unstable` and `x86_64-darwin` - # as a target is no longer supported since 26.11 (see nixos/nixpkgs#535508), - # though GitHub themselves still provide binaries that support Intel Macs so - # we rely on a pin for `x86_64-darwin` and consume from `unstable` otherwise. - pinned = pkgs.codeql.overrideAttrs (_: { - inherit version; + version = "2.27.0"; - src = pkgs.fetchzip { - url = "https://github.com/github/codeql-cli-binaries/releases/download/v${version}/codeql.zip"; - hash = "sha256-8WhsburnhVWvVLTNOmAwmkjwN45exmKmw7Q5elTjYiI="; + # GitHub's archive for each host, named by the directory under `tools/` that + # holds its native binaries. The universal `codeql.zip` lacks `linux-arm64`. + osx64 = { + name = "osx64"; + hash = "sha256-47H7FQy/67sZvgSTAAgXfoGKxChL1khLaLqxanHFd7c="; + }; + archives = { + "aarch64-darwin" = osx64; + "x86_64-darwin" = osx64; + "aarch64-linux" = { + name = "linux-arm64"; + hash = "sha256-PdJozYk3zs+vRH0OwIJawGrCuatVd7huzACGIViVN/0="; }; - }); + "x86_64-linux" = { + name = "linux64"; + hash = "sha256-L87sPrfRSYfCjjZqv1V7L7diZoL+LG5HbWSMqIeZCEE="; + }; + }; - codeql = if pkgs.stdenv.hostPlatform.system == "x86_64-darwin" then pinned else unstable.codeql; + archive = + archives.${stdenv.hostPlatform.system} + or (throw "codeql: no archive for ${stdenv.hostPlatform.system}"); in { - packages = [ codeql ]; + packages = [ + (stdenv.mkDerivation { + pname = "codeql"; + inherit version; + + src = pkgs.fetchzip { + url = "https://github.com/github/codeql-cli-binaries/releases/download/v${version}/codeql-${archive.name}.zip"; + inherit (archive) hash; + }; + + dontConfigure = true; + dontBuild = true; + dontStrip = true; + + # CodeQL searches the parent of its distribution for packs, so the tree + # sits in `$out/codeql` to keep that parent from being the whole store. + installPhase = '' + mkdir -p $out/{codeql,bin} + cp -R * $out/codeql/ + ${lib.optionalString stdenv.hostPlatform.isLinux '' + tools=$out/codeql/tools/${archive.name} + # The tracer preloads `libtrace.so`; `linux-arm64` names it otherwise. + [ -e $tools/libtrace.so ] || ln -s $tools/lib64trace.so $tools/libtrace.so + # The extractors find Java through the tools directory, and the + # bundled one expects an FHS loader, so nixpkgs' Java stands in. + rm -rf $tools/java + ln -s ${pkgs.jdk21} $tools/java + # The native extractors expect an FHS loader and glibc, so the CLI + # and every tool it spawns run inside `fhs-run`. + printf '#!%s\nexec %s %s "$@"\n' ${stdenv.shell} \ + ${fhs.run}/bin/fhs-run $out/codeql/codeql > $out/bin/codeql + chmod +x $out/bin/codeql + ''} + ${lib.optionalString (!stdenv.hostPlatform.isLinux) '' + ln -s $out/codeql/codeql $out/bin/ + ''} + ''; + + meta = { + description = "Semantic code analysis engine"; + homepage = "https://codeql.github.com"; + license = lib.licenses.unfree; + platforms = lib.attrNames archives; + }; + }) + ]; } diff --git a/contrib/nix/mods/fhs.nix b/contrib/nix/mods/fhs.nix index 36a2a38f7..b71cb5f80 100644 --- a/contrib/nix/mods/fhs.nix +++ b/contrib/nix/mods/fhs.nix @@ -41,5 +41,6 @@ let }; in { + inherit run; packages = lib.optional pkgs.stdenv.hostPlatform.isLinux run; } diff --git a/contrib/nix/shell/common.nix b/contrib/nix/shell/common.nix index 342865756..6e6ec1639 100644 --- a/contrib/nix/shell/common.nix +++ b/contrib/nix/shell/common.nix @@ -6,7 +6,6 @@ inputs, llvm, root, - unstable, }: let @@ -18,6 +17,8 @@ let xcodeSdk = import ../mods/xcode_sdk.nix { inherit pkgs; }; }; + fhs = import ../mods/fhs.nix { inherit pkgs lib; }; + rsComponents = (lib.importTOML (root + "/rust-toolchain.toml")).toolchain.components; hostTriple = pkgs.stdenv.hostPlatform.rust.rustcTarget; @@ -90,9 +91,9 @@ in mods = lib.mapAttrs (name: m: m // { _name = name; }) { bazel = import ../mods/bazel.nix { inherit pkgs; }; - codeql = import ../mods/codeql.nix { inherit pkgs unstable; }; + codeql = import ../mods/codeql.nix { inherit pkgs fhs; }; cxx = cxx.compiler; - fhs = import ../mods/fhs.nix { inherit pkgs lib; }; + inherit fhs; nixpkgs = import ../mods/nixpkgs.nix { inherit pkgs llvm; }; python = import ../mods/python.nix { inherit pkgs lib; From ea01edeb7503ec9166df6bae9e2c30bfff57c5d7 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh Date: Mon, 2 Nov 2026 17:59:19 +0530 Subject: [PATCH 06/11] sdk%refac(lint): move clang-tidy include and SDK helpers to `common` `git diff --color-moved=dimmed-zebra --color-moved-ws=ignore-all-space` --- maint/common.py | 39 ++++++++++++++++++++++++++++++++++ maint/lint/lint_clang.py | 46 ++++------------------------------------ 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/maint/common.py b/maint/common.py index 038ee18ca..4eddb07c4 100644 --- a/maint/common.py +++ b/maint/common.py @@ -13,6 +13,7 @@ import argparse import os +import platform import re import shutil import subprocess @@ -44,6 +45,11 @@ # C and C++ headers; a parser reaches them only through a unit. CXX_HEADERS: tuple[str, ...] = (".h", ".hh", ".hpp", ".hxx") +# Include directories a unit is built with, keyed by the directory holding it. +CXX_INCLUDES: dict[str, tuple[str, ...]] = { + "fuzz/pow": ("fuzz/pow/sphlib/c",), +} + # Assumed base branch for codebase. DEFAULT_BASE = "develop" @@ -310,6 +316,39 @@ def fmt(cells: tuple[str, ...], *, color: bool = False) -> str: ) +def cxx_includes(repo_root: Path, unit: Path) -> tuple[str, ...]: + """Return the include directories *unit* is built with.""" + relative = unit.relative_to(repo_root) + return tuple( + include + for holder, includes in CXX_INCLUDES.items() + if relative.is_relative_to(holder) + for include in includes + ) + + +def cxx_environ() -> dict[str, str]: + """Return the environment clang-tidy runs in. + + A clang that is not Apple's finds the macOS system headers only through + 'SDKROOT', so it is filled in from xcrun when unset. + """ + env = dict(os.environ) + if platform.system() != "Darwin" or env.get("SDKROOT"): + return env + try: + xcrun = require_bin("xcrun") + except FileNotFoundError: + return env + env["SDKROOT"] = subprocess.run( # noqa: S603 + [xcrun, "--show-sdk-path"], + capture_output=True, + check=True, + text=True, + ).stdout.strip() + return env + + def find_up_dir( start: Path, predicate: Callable[[Path], bool], diff --git a/maint/lint/lint_clang.py b/maint/lint/lint_clang.py index 868c92a65..0acccbd36 100755 --- a/maint/lint/lint_clang.py +++ b/maint/lint/lint_clang.py @@ -11,8 +11,6 @@ from __future__ import annotations -import os -import platform import re import subprocess import sys @@ -25,6 +23,8 @@ RETCODE_ERR, RETCODE_PASS, RETCODE_SKIP, + cxx_environ, + cxx_includes, declare_verbs, formatted, relay, @@ -37,11 +37,6 @@ # Base name of this script (equivalent to argv[0]). SCRIPT = Path(__file__).stem -# Include directories a unit is built with, keyed by the directory holding it. -INCLUDES: dict[str, tuple[str, ...]] = { - "fuzz/pow": ("fuzz/pow/sphlib/c",), -} - # Progress and summary lines clang-tidy prints per unit, naming no fault. _NOISE_RE = re.compile( r"^(\[\d+/\d+\] Processing file .*" @@ -56,39 +51,6 @@ def _sources(repo_root: Path, only: list[str] | None) -> list[Path]: return repo_files(repo_root, *(f"*{s}" for s in CXX_UNITS + CXX_HEADERS)) -def _includes(repo_root: Path, unit: Path) -> tuple[str, ...]: - """Return the include directories *unit* is built with.""" - relative = unit.relative_to(repo_root) - return tuple( - include - for holder, includes in INCLUDES.items() - if relative.is_relative_to(holder) - for include in includes - ) - - -def _environ() -> dict[str, str]: - """Return the environment clang-tidy runs in. - - A clang that is not Apple's finds the macOS system headers only through - 'SDKROOT', so it is filled in from xcrun when unset. - """ - env = dict(os.environ) - if platform.system() != "Darwin" or env.get("SDKROOT"): - return env - try: - xcrun = require_bin("xcrun") - except FileNotFoundError: - return env - env["SDKROOT"] = subprocess.run( # noqa: S603 - [xcrun, "--show-sdk-path"], - capture_output=True, - check=True, - text=True, - ).stdout.strip() - return env - - def _check_format( repo_root: Path, sources: list[Path], @@ -137,10 +99,10 @@ def _check_tidy(repo_root: Path, sources: list[Path]) -> int | None: # Units sharing include directories share one run. groups: dict[tuple[str, ...], list[Path]] = {} for unit in units: - groups.setdefault(_includes(repo_root, unit), []).append(unit) + groups.setdefault(cxx_includes(repo_root, unit), []).append(unit) config = repo_root / "maint" / ".clang-tidy" - env = _environ() + env = cxx_environ() linted = 0 failed = False for includes, members in groups.items(): From 8be76e0144f2b0248713e79298456166527191fc Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh Date: Mon, 2 Nov 2026 17:59:21 +0530 Subject: [PATCH 07/11] sdk%feat(lint): pass the builds' C/C++ standards to clang-tidy --- maint/common.py | 32 +++++++++++++++++++++++++------- maint/lint/lint_clang.py | 25 +++++++++++-------------- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/maint/common.py b/maint/common.py index 4eddb07c4..6a1c812da 100644 --- a/maint/common.py +++ b/maint/common.py @@ -39,13 +39,22 @@ # Rust source roots the analysers scan, relative to the repository root. SOURCE_DIRS: tuple[str, ...] = ("meta", "pkgs", "docs/samples", "fuzz") +# C translation units. The builds pass no standard, so clang's default holds. +C_UNITS: tuple[str, ...] = (".c",) +C_STD = "gnu17" + +# C++ translation units, at the standard the Bazel toolchain passes. +CPP_UNITS: tuple[str, ...] = (".cc", ".cpp", ".cxx") +CPP_STD = "c++17" + # C and C++ translation units. -CXX_UNITS: tuple[str, ...] = (".c", ".cc", ".cpp", ".cxx") +CXX_UNITS: tuple[str, ...] = C_UNITS + CPP_UNITS # C and C++ headers; a parser reaches them only through a unit. CXX_HEADERS: tuple[str, ...] = (".h", ".hh", ".hpp", ".hxx") # Include directories a unit is built with, keyed by the directory holding it. +# Each entry mirrors its directory's build script and changes with it. CXX_INCLUDES: dict[str, tuple[str, ...]] = { "fuzz/pow": ("fuzz/pow/sphlib/c",), } @@ -316,15 +325,24 @@ def fmt(cells: tuple[str, ...], *, color: bool = False) -> str: ) -def cxx_includes(repo_root: Path, unit: Path) -> tuple[str, ...]: - """Return the include directories *unit* is built with.""" +def cxx_flags(repo_root: Path, unit: Path) -> list[str]: + """Return the flags *unit* is compiled with. + + Raises FileNotFoundError naming an absent include directory, which is + what an uninitialised submodule leaves behind. + """ + std = C_STD if unit.suffix in C_UNITS else CPP_STD relative = unit.relative_to(repo_root) - return tuple( + includes = [ include - for holder, includes in CXX_INCLUDES.items() + for holder, held in CXX_INCLUDES.items() if relative.is_relative_to(holder) - for include in includes - ) + for include in held + ] + absent = [i for i in includes if not (repo_root / i).is_dir()] + if absent: + raise FileNotFoundError(f"{', '.join(absent)} is absent") + return [f"-std={std}", *(f"-I{repo_root / i}" for i in includes)] def cxx_environ() -> dict[str, str]: diff --git a/maint/lint/lint_clang.py b/maint/lint/lint_clang.py index 0acccbd36..2f134de8a 100755 --- a/maint/lint/lint_clang.py +++ b/maint/lint/lint_clang.py @@ -24,7 +24,7 @@ RETCODE_PASS, RETCODE_SKIP, cxx_environ, - cxx_includes, + cxx_flags, declare_verbs, formatted, relay, @@ -96,25 +96,22 @@ def _check_tidy(repo_root: Path, sources: list[Path]) -> int | None: print(f"{SCRIPT}: no C/C++ unit was touched") return RETCODE_PASS - # Units sharing include directories share one run. + # Units sharing flags share one run. groups: dict[tuple[str, ...], list[Path]] = {} for unit in units: - groups.setdefault(cxx_includes(repo_root, unit), []).append(unit) + try: + flags = cxx_flags(repo_root, unit) + except FileNotFoundError as e: + name = unit.relative_to(repo_root) + print(f"{e}, skipping the lint of {name}", file=sys.stderr) + continue + groups.setdefault(tuple(flags), []).append(unit) config = repo_root / "maint" / ".clang-tidy" env = cxx_environ() linted = 0 failed = False - for includes, members in groups.items(): - # An uninitialised submodule leaves its include directory absent. - absent = [i for i in includes if not (repo_root / i).is_dir()] - if absent: - print( - f"{', '.join(absent)} is absent, skipping the lint of " - f"{len(members)} unit(s)", - file=sys.stderr, - ) - continue + for flags, members in groups.items(): result = subprocess.run( # noqa: S603 [ clang_tidy, @@ -122,7 +119,7 @@ def _check_tidy(repo_root: Path, sources: list[Path]) -> int | None: f"--config-file={config}", *[str(m) for m in members], "--", - *[f"-I{repo_root / i}" for i in includes], + *flags, ], capture_output=True, check=False, From 8bb9eebe0cf0d1887a82db0a3082061a9ffce19e Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh Date: Mon, 2 Nov 2026 16:46:32 +0530 Subject: [PATCH 08/11] sdk%feat(lint): check C/C++ against SEI CERT 2016 with CodeQL --- Justfile | 2 + maint/README.md | 2 +- maint/codeql/c/codeql-pack.lock.yml | 38 +++++++++ maint/codeql/c/qlpack.yml | 5 ++ maint/codeql/cpp/codeql-pack.lock.yml | 36 +++++++++ maint/codeql/cpp/qlpack.yml | 5 ++ maint/common.py | 2 +- maint/lint/lint_codeql.py | 111 ++++++++++++++++++++++---- 8 files changed, 185 insertions(+), 16 deletions(-) create mode 100644 maint/codeql/c/codeql-pack.lock.yml create mode 100644 maint/codeql/c/qlpack.yml create mode 100644 maint/codeql/cpp/codeql-pack.lock.yml create mode 100644 maint/codeql/cpp/qlpack.yml diff --git a/Justfile b/Justfile index d983a47d9..62ad190f2 100644 --- a/Justfile +++ b/Justfile @@ -40,6 +40,8 @@ lint: {{ python }} maint/lint/lint_bazel.py graph {{ python }} maint/lint/lint_codeql.py check {{ python }} maint/lint/lint_codeql.py run --lang=rust --with-suite=rust-security-and-quality + {{ python }} maint/lint/lint_codeql.py run --lang=c --with-suite=cpp-security-and-quality + {{ python }} maint/lint/lint_codeql.py run --lang=cpp --with-suite=cpp-security-and-quality [positional-arguments] [unix] diff --git a/maint/README.md b/maint/README.md index 37b6f1313..a8a6495ab 100644 --- a/maint/README.md +++ b/maint/README.md @@ -18,7 +18,7 @@ use [`lint_all.py`](./lint_all.py). | [`lint_bazel.py`](./lint/lint_bazel.py) | Lint Starlark files with `buildifier`, hold the rustc flags and edition Bazel builds with to cargo's, and the crates Bazel builds to cargo's graph | `check`, `apply`, `apply-all`, `graph` | `buildifier`, `bazel` and `cargo` (`graph`) | | [`lint_cargo.py`](./lint/lint_cargo.py) | Deny dependencies per [`deny.toml`](../.cargo/deny.toml), check/format TOML files against [`taplo.toml`](./taplo.toml) | `check` , `apply`, `apply-all` | `cargo-deny` (deny dependencies), `taplo` (TOML formatting) | | [`lint_clang.py`](./lint/lint_clang.py) | Check/format C and C++ sources against [`.clang-format`](./.clang-format), lint them against [`.clang-tidy`](./.clang-tidy) | `check`, `apply`, `apply-all` | `clang-format` (formatting), `clang-tidy` (lint), `git` | -| [`lint_codeql.py`](./lint/lint_codeql.py) | Query Rust sources against [`maint/codeql/rust/*.ql`](./codeql/rust) | `check`, `apply`, `apply-all`, `run`, `run-all` | `codeql`, `rustc` | +| [`lint_codeql.py`](./lint/lint_codeql.py) | Query Rust sources against [`maint/codeql/rust/*.ql`](./codeql/rust), C and C++ sources against SEI CERT C/C++ 2016 from [`codeql-coding-standards`](https://github.com/github/codeql-coding-standards) | `check`, `apply`, `apply-all`, `run`, `run-all` | `codeql`, `rustc` (Rust), `cc` (C), `c++` (C++) | | [`lint_javascript.py`](./lint/lint_javascript.py) | Lint Javascript sources against [`eslint.config.mjs`](js/eslint.config.mjs) | *None* | `npx` (part of Node.js), `eslint` (auto-retrieved by script) | | [`lint_just.py`](./lint/lint_just.py) | Lint the root `Justfile` and every module against `just --fmt` | `check`, `apply`, `apply-all` | `just`, `git` | | [`lint_markdown.py`](./lint/lint_markdown.py) | Lint Markdown [documentation](../docs/dev/about_docs.md) | *None* | `pymarkdownlnt` | diff --git a/maint/codeql/c/codeql-pack.lock.yml b/maint/codeql/c/codeql-pack.lock.yml new file mode 100644 index 000000000..f1fa9ec4e --- /dev/null +++ b/maint/codeql/c/codeql-pack.lock.yml @@ -0,0 +1,38 @@ +--- +lockVersion: 1.0.0 +dependencies: + advanced-security/qtil: + version: 0.0.3 + codeql/cert-c-coding-standards: + version: 2.62.0 + codeql/common-c-coding-standards: + version: 2.62.0 + codeql/common-cpp-coding-standards: + version: 2.62.0 + codeql/cpp-all: + version: 5.0.0 + codeql/cpp-queries: + version: 1.4.1 + codeql/dataflow: + version: 2.0.8 + codeql/mad: + version: 1.0.24 + codeql/quantum: + version: 0.0.2 + codeql/rangeanalysis: + version: 1.0.24 + codeql/ssa: + version: 2.0.0 + codeql/suite-helpers: + version: 1.0.24 + codeql/tutorial: + version: 1.0.24 + codeql/typeflow: + version: 1.0.24 + codeql/typetracking: + version: 2.0.8 + codeql/util: + version: 2.0.11 + codeql/xml: + version: 1.0.24 +compiled: false diff --git a/maint/codeql/c/qlpack.yml b/maint/codeql/c/qlpack.yml new file mode 100644 index 000000000..55a0040ec --- /dev/null +++ b/maint/codeql/c/qlpack.yml @@ -0,0 +1,5 @@ +name: base-sdk/codeql-c +version: 0.0.0 +dependencies: + codeql/cert-c-coding-standards: 2.62.0 + codeql/cpp-queries: ~1.4.1 diff --git a/maint/codeql/cpp/codeql-pack.lock.yml b/maint/codeql/cpp/codeql-pack.lock.yml new file mode 100644 index 000000000..5fc1dc1ab --- /dev/null +++ b/maint/codeql/cpp/codeql-pack.lock.yml @@ -0,0 +1,36 @@ +--- +lockVersion: 1.0.0 +dependencies: + advanced-security/qtil: + version: 0.0.3 + codeql/cert-cpp-coding-standards: + version: 2.62.0 + codeql/common-cpp-coding-standards: + version: 2.62.0 + codeql/cpp-all: + version: 5.0.0 + codeql/cpp-queries: + version: 1.4.1 + codeql/dataflow: + version: 2.0.8 + codeql/mad: + version: 1.0.24 + codeql/quantum: + version: 0.0.2 + codeql/rangeanalysis: + version: 1.0.24 + codeql/ssa: + version: 2.0.0 + codeql/suite-helpers: + version: 1.0.24 + codeql/tutorial: + version: 1.0.24 + codeql/typeflow: + version: 1.0.24 + codeql/typetracking: + version: 2.0.8 + codeql/util: + version: 2.0.11 + codeql/xml: + version: 1.0.24 +compiled: false diff --git a/maint/codeql/cpp/qlpack.yml b/maint/codeql/cpp/qlpack.yml new file mode 100644 index 000000000..9dc9dfed5 --- /dev/null +++ b/maint/codeql/cpp/qlpack.yml @@ -0,0 +1,5 @@ +name: base-sdk/codeql-cpp +version: 0.0.0 +dependencies: + codeql/cert-cpp-coding-standards: 2.62.0 + codeql/cpp-queries: ~1.4.1 diff --git a/maint/common.py b/maint/common.py index 6a1c812da..00d201e0d 100644 --- a/maint/common.py +++ b/maint/common.py @@ -346,7 +346,7 @@ def cxx_flags(repo_root: Path, unit: Path) -> list[str]: def cxx_environ() -> dict[str, str]: - """Return the environment clang-tidy runs in. + """Return the environment a C/C++ parser runs in. A clang that is not Apple's finds the macOS system headers only through 'SDKROOT', so it is filled in from xcrun when unset. diff --git a/maint/lint/lint_codeql.py b/maint/lint/lint_codeql.py index 4a531bda6..146726b98 100755 --- a/maint/lint/lint_codeql.py +++ b/maint/lint/lint_codeql.py @@ -15,6 +15,7 @@ import csv import datetime import re +import shlex import shutil import subprocess import sys @@ -27,10 +28,14 @@ from collections.abc import Iterator from common import ( + C_UNITS, + CPP_UNITS, RETCODE_ERR, RETCODE_PASS, RETCODE_SKIP, SOURCE_DIRS, + cxx_environ, + cxx_flags, declare_verbs, format_verbs, formatted, @@ -65,6 +70,17 @@ class Language(NamedTuple): requires: tuple[str, ...] # Findings to drop, each matched whole as (path, message). suppressions: tuple[tuple[str, str], ...] = () + # Suites every analysis runs, each as (pack, suite), on top of any + # `--with-suite`. + standards: tuple[tuple[str, str], ...] = () + # Units a traced build compiles, with `compiler`. Left empty, the + # extractor reads the sources on disk without a build. + units: tuple[str, ...] = () + compiler: str = "" + + +# MinGW ships no `cc`, so Windows calls `gcc` by name. +_CC = "gcc" if sys.platform == "win32" else "cc" # Every language this harness knows, in the order `run-all` walks them. @@ -75,6 +91,26 @@ class Language(NamedTuple): pack="codeql/rust-queries", requires=("rustc",), ), + # SEI CERT C 2016 and CERT C++ 2016, as github/codeql-coding-standards + # enables them. Each standard reads only the units of its own language. + Language( + name="c", + directory="c", + pack="codeql/cpp-queries", + requires=(_CC,), + standards=(("codeql/cert-c-coding-standards", "cert-c-default"),), + units=C_UNITS, + compiler=_CC, + ), + Language( + name="cpp", + directory="cpp", + pack="codeql/cpp-queries", + requires=("c++",), + standards=(("codeql/cert-cpp-coding-standards", "cert-cpp-default"),), + units=CPP_UNITS, + compiler="c++", + ), ) @@ -172,8 +208,9 @@ def _generate_source_lines( def _print_csv_diagnostics( results_path: Path, suppressions: tuple[tuple[str, str], ...], + owned: frozenset[str], ) -> int: - """Print CSV results to stderr. Returns the unsuppressed finding count.""" + """Print the findings in *owned* files to stderr, returning how many.""" count = 0 with results_path.open(newline="") as f: for row in csv.reader(f): @@ -184,7 +221,7 @@ def _print_csv_diagnostics( uri = Path(row[4].lstrip("/")) line = row[5] msg = row[3].replace("\n", " ") - if (str(uri), msg) in suppressions: + if str(uri) not in owned or (str(uri), msg) in suppressions: continue print(f"{uri}:{line}: {msg}", file=sys.stderr) count += 1 @@ -207,6 +244,30 @@ def _locked_pack(pack: str, query_dir: Path) -> str: return f"{pack}@{match.group(1)}" +def _build_commands( + repo_root: Path, + language: Language, +) -> list[str]: + """Return the `--command` options compiling each unit of *language*.""" + compiler = require_bin(language.compiler) + options: list[str] = [] + for unit in repo_files(repo_root, *(f"*{s}" for s in language.units)): + try: + flags = cxx_flags(repo_root, unit) + except FileNotFoundError as e: + name = unit.relative_to(repo_root) + print(f"{e}, skipping the analysis of {name}", file=sys.stderr) + continue + argv = [compiler, "-fsyntax-only", *flags, str(unit)] + # Windows splits a command line by its own quoting rules, not POSIX ones. + if sys.platform == "win32": + command = subprocess.list2cmdline(argv) + else: + command = shlex.join(argv) + options.append(f"--command={command}") + return options + + def _search_suites(pack: str, names: list[str]) -> list[str]: """Return query-suite references under the pinned *pack*.""" for name in names: @@ -314,11 +375,23 @@ def _analyse( query_dir = repo_root / "maint" / "codeql" / language.directory queries = _discover_queries(query_dir) - if not queries: + if not queries and not language.standards: print(f"no queries for {language.name}, skipping", file=sys.stderr) return None - print(f"checking {language.name}: {len(queries)} query(s)") + # Without a command the extractor parses the sources on disk, so a query + # sees every cfg rather than one build's selection. + build: list[str] = ["--build-mode=none"] + if language.units: + build = _build_commands(repo_root, language) + if not build: + print(f"no units for {language.name}, skipping", file=sys.stderr) + return None + + print( + f"checking {language.name}: {len(queries)} query(s), " + f"{len(language.standards) + len(args.suites)} suite(s)", + ) # Only Rust needs the pre-scanned source text; see the generator itself. if language.name == "rust": @@ -331,15 +404,17 @@ def _analyse( # The lock file is only consulted to pin a suite, so a language that # requests none is not held to having one. suites: list[str] = [] - if args.suites: - try: - suites = _search_suites( + try: + for pack, name in language.standards: + suites += _search_suites(_locked_pack(pack, query_dir), [name]) + if args.suites: + suites += _search_suites( _locked_pack(language.pack, query_dir), args.suites, ) - except ValueError as e: - print(f"error: {e}", file=sys.stderr) - return RETCODE_ERR + except ValueError as e: + print(f"error: {e}", file=sys.stderr) + return RETCODE_ERR # Install CodeQL pack dependencies. subprocess.run( # noqa: S603 @@ -360,6 +435,11 @@ def _analyse( shutil.rmtree(active_db) active_db.parent.mkdir(parents=True, exist_ok=True) config_file = query_dir / "codeql-config.yml" + config = ( + [f"--codescanning-config={config_file}"] + if config_file.is_file() + else [] + ) result = subprocess.run( # noqa: S603 [ codeql_bin, @@ -367,14 +447,13 @@ def _analyse( "create", str(active_db), f"--language={language.name}", - # Without a command the extractor parses the sources on disk, - # so a query sees every cfg rather than one build's selection. - "--build-mode=none", + *build, f"--source-root={repo_root}", - f"--codescanning-config={config_file}", + *config, f"-j{usable_threads()}", ], cwd=str(repo_root), + env=cxx_environ() if language.units else None, check=False, ) if result.returncode != RETCODE_PASS or not db_yml.is_file(): @@ -402,9 +481,13 @@ def _analyse( check=True, ) + owned = frozenset( + str(p.relative_to(repo_root)) for p in repo_files(repo_root) + ) total_findings = _print_csv_diagnostics( results_path, language.suppressions, + owned, ) return RETCODE_ERR if total_findings > 0 else RETCODE_PASS From d44404ec5ce47f1615156e225c73e6ccfbcacf63 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh Date: Mon, 2 Nov 2026 16:55:16 +0530 Subject: [PATCH 09/11] sdk%lint: fix CERT INT32-C and ERR51-CPP in probes --- contrib/probes/free.c | 3 ++- contrib/probes/hello.cpp | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/contrib/probes/free.c b/contrib/probes/free.c index 2bca74961..062f5c38e 100644 --- a/contrib/probes/free.c +++ b/contrib/probes/free.c @@ -2,5 +2,6 @@ int add(int a, int b) { - return a + b; + int sum = 0; + return __builtin_add_overflow(a, b, &sum) ? 0 : sum; } diff --git a/contrib/probes/hello.cpp b/contrib/probes/hello.cpp index f2ce8bbda..246059acf 100644 --- a/contrib/probes/hello.cpp +++ b/contrib/probes/hello.cpp @@ -68,6 +68,10 @@ bool say(const std::string& text) int main() { - const std::string greeting = std::string("Hello") + ", world!\n"; - return say(greeting) ? 0 : 1; + try { + const std::string greeting = std::string("Hello") + ", world!\n"; + return say(greeting) ? 0 : 1; + } catch (...) { + return 1; + } } From 003bcdca1bad84330dbedd02b1f5dc4ff528f5a5 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh Date: Mon, 2 Nov 2026 16:58:37 +0530 Subject: [PATCH 10/11] sdk%refac(lint): read CodeQL suppressions from per-language TOML --- maint/lint/lint_codeql.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/maint/lint/lint_codeql.py b/maint/lint/lint_codeql.py index 146726b98..2eb18192a 100755 --- a/maint/lint/lint_codeql.py +++ b/maint/lint/lint_codeql.py @@ -20,6 +20,7 @@ import subprocess import sys import tempfile +import tomllib from pathlib import Path from typing import TYPE_CHECKING, NamedTuple @@ -53,6 +54,10 @@ "Serialize", "Deserialize", "Unencodable", "TypeId", "Zeroize", "#[cfg", ) +# Findings a language drops, relative to its pack directory. Each +# `[[finding]]` names a `path` and a `message`, both matched whole. +_SUPPRESSIONS = "suppressions.toml" + # QL a run writes into the tree, relative to a language's pack directory. _GENERATED_QL = ("lib", "source_lines.qll") @@ -68,8 +73,6 @@ class Language(NamedTuple): pack: str # Binaries it needs; absent, the language skips rather than fails. requires: tuple[str, ...] - # Findings to drop, each matched whole as (path, message). - suppressions: tuple[tuple[str, str], ...] = () # Suites every analysis runs, each as (pack, suite), on top of any # `--with-suite`. standards: tuple[tuple[str, str], ...] = () @@ -205,9 +208,24 @@ def _generate_source_lines( return out +def _load_suppressions(query_dir: Path) -> frozenset[tuple[str, str]]: + """Return the (path, message) pairs that *query_dir* suppresses.""" + path = query_dir / _SUPPRESSIONS + if not path.is_file(): + return frozenset() + found: set[tuple[str, str]] = set() + for entry in tomllib.loads(path.read_text(encoding="utf-8")).get( + "finding", [] + ): + if not isinstance(entry, dict) or set(entry) != {"path", "message"}: + raise ValueError(f"{path}: want only `path` and `message`: {entry!r}") + found.add((entry["path"], entry["message"])) + return frozenset(found) + + def _print_csv_diagnostics( results_path: Path, - suppressions: tuple[tuple[str, str], ...], + suppressions: frozenset[tuple[str, str]], owned: frozenset[str], ) -> int: """Print the findings in *owned* files to stderr, returning how many.""" @@ -486,7 +504,7 @@ def _analyse( ) total_findings = _print_csv_diagnostics( results_path, - language.suppressions, + _load_suppressions(query_dir), owned, ) From 901e65199e2691817e31351306373f00fed8fcd1 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh Date: Mon, 2 Nov 2026 16:58:38 +0530 Subject: [PATCH 11/11] sdk%lint(codeql): suppress DCL37-C, `cpp/implicit-function-declaration` --- maint/codeql/c/suppressions.toml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 maint/codeql/c/suppressions.toml diff --git a/maint/codeql/c/suppressions.toml b/maint/codeql/c/suppressions.toml new file mode 100644 index 000000000..edca8a2ca --- /dev/null +++ b/maint/codeql/c/suppressions.toml @@ -0,0 +1,9 @@ +# CodeQL's front end does not know clang's `__builtin_available`, so it reads +# the builtin as an implicitly declared function. +[[finding]] +path = "contrib/probes/hello.c" +message = "Reserved identifier '__builtin_available' is declared." + +[[finding]] +path = "contrib/probes/hello.c" +message = "Function call implicitly declares '__builtin_available'."