diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 3da31fa356b1fd..3066451cecb1a2 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -72,6 +72,64 @@ jobs: test/lint/commit-script-check.sh $COMMIT_RANGE fi + # Extended lint using cppcheck. Other jobs do NOT require this to pass. + extended-lint: + env: + CPPCHECK_VERSION: 2.14.0 + CPPCHECK_BIN_DIR: ${{ github.workspace }}/.cppcheck + CPPCHECK_BUILD_DIR: ${{ github.workspace }}/.ci-cppcheck + LC_ALL: C + runs-on: ubuntu-20.04 + defaults: + run: + shell: bash + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Initialize Python + uses: actions/setup-python@v5 + with: + python-version: '3.8' + + - name: cppcheck cache files + uses: actions/cache@v4 + with: + path: | + .cppcheck + .ci-cppcheck + key: ci-cppcheck + restore-keys: ci-cppcheck + + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + + mkdir -p ${CPPCHECK_BIN_DIR} + curl -s https://codeload.github.com/danmar/cppcheck/tar.gz/${CPPCHECK_VERSION} | tar -zxf - --directory ${CPPCHECK_BIN_DIR}/ + cd ${CPPCHECK_BIN_DIR}/cppcheck-${CPPCHECK_VERSION}/ + make -j 3 MATCHCOMPILER=yes FILESDIR=${CPPCHECK_BIN_DIR}/cppcheck-${CPPCHECK_VERSION}/cfg/ + + - name: Set TRAVIS_BRANCH workaround env variable + if: github.event_name == 'pull_request' + run: echo "TRAVIS_BRANCH=${{ github.base_ref }}" >> $GITHUB_ENV + + - name: Build cppcheck cache + run: | + export PATH="${CPPCHECK_BIN_DIR}/cppcheck-${CPPCHECK_VERSION}:${PATH}" + git checkout -qf -B master refs/remotes/origin/master + git checkout -qf $GITHUB_SHA + + # Build or rebuild the cppcheck cache. + test/lint/extended-lint-all.sh --setup + + - name: Lint + run: | + # Run the remainder of lint tests in `test/lint/`. + test/lint/extended-lint-all.sh + # CMake build jobs to ensure building with CMake remains viable. # Currently this only supports native linux and macOS. cmake: diff --git a/test/lint/extended-lint-all.sh b/test/lint/extended-lint-all.sh new file mode 100755 index 00000000000000..9d378324ffe84d --- /dev/null +++ b/test/lint/extended-lint-all.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2024 The PIVX Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# This script runs all test/lint/extended-lint-*.sh files, and fails if +# any exit with a non-zero status code. + +# This script is intentionally locale dependent by not setting "export LC_ALL=C" +# in order to allow for the executed lint scripts to opt in or opt out of locale +# dependence themselves. + +set -u + +print_usage() { + echo "Usage: $0 --setup to create/refresh cppcheck's cache and exit. No argument to run lint tests." +} + +SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}") +LINTALL=$(basename "${BASH_SOURCE[0]}") + +if [[ $# != 0 ]]; then + if [[ $1 == "--help" ]]; then + print_usage + exit 0 + fi + if [[ $1 == "--setup" ]]; then + if ! "${SCRIPTDIR}"/run-cppcheck.sh; then + echo "cppcheck cache creation failed" + exit 1 + fi + exit 0 + fi +fi + +for f in "${SCRIPTDIR}"/extended-lint-*.sh; do + if [ "$(basename "$f")" != "$LINTALL" ]; then + if ! "$f"; then + echo "^---- failure generated from $f" + exit 1 + fi + fi +done diff --git a/test/lint/extended-lint-noexplicitconstructor.sh b/test/lint/extended-lint-noexplicitconstructor.sh new file mode 100755 index 00000000000000..48e6b668cba4f7 --- /dev/null +++ b/test/lint/extended-lint-noexplicitconstructor.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2024 The PIVX Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +export LC_ALL=C + +ENABLED_CHECKS=( + "\[noExplicitConstructor\]" +# "Class '.*' has a constructor with 1 argument that is not explicit." +# "Struct '.*' has a constructor with 1 argument that is not explicit." +) + +IGNORED_WARNINGS=( + "src/immer/.*" + "src/chiabls/.*" + "src/arith_uint256.h:.* Class 'arith_uint160' has a constructor with 1 argument that is not explicit." + "src/arith_uint256.h:.* Class 'arith_uint256' has a constructor with 1 argument that is not explicit." + "src/arith_uint256.h:.* Class 'arith_uint512' has a constructor with 1 argument that is not explicit." + "src/arith_uint256.h:.* Class 'base_uint < 160 >' has a constructor with 1 argument that is not explicit." + "src/arith_uint256.h:.* Class 'base_uint < 256 >' has a constructor with 1 argument that is not explicit." + "src/arith_uint256.h:.* Class 'base_uint < 512 >' has a constructor with 1 argument that is not explicit." + "src/arith_uint256.h:.* Class 'base_uint' has a constructor with 1 argument that is not explicit." + "src/coins.h:.* Class 'CCoinsViewBacked' has a constructor with 1 argument that is not explicit." + "src/coins.h:.* Class 'CCoinsViewCache' has a constructor with 1 argument that is not explicit." + "src/bls/bls_wrapper.h:.* Struct 'CBLSIdImplicit' has a constructor with 1 argument that is not explicit." + "src/bls/bls_wrapper.h:.* Class 'CBLSId' has a constructor with 1 argument that is not explicit." + "src/bls/bls_wrapper.h:.* Class 'CBLSWrapper < CBLSIdImplicit , 32 , CBLSId >' has a constructor with 1 argument that is not explicit." + "src/bls/bls_wrapper.h:.* Class 'CBLSWrapper < bls :: G1Element , 48 , CBLSPublicKey >' has a constructor with 1 argument that is not explicit." + "src/bls/bls_wrapper.h:.* Class 'CBLSWrapper < bls :: G2Element , 96 , CBLSSignature >' has a constructor with 1 argument that is not explicit." + "src/bls/bls_wrapper.h:.* Class 'CBLSWrapper < bls :: PrivateKey , 32 , CBLSSecretKey >' has a constructor with 1 argument that is not explicit." + "src/operationresult.h:.* Class 'OperationResult' has a constructor with 1 argument that is not explicit." + "src/prevector.h:.* Class 'const_iterator' has a constructor with 1 argument that is not explicit." + "src/prevector.h:.* Class 'const_reverse_iterator' has a constructor with 1 argument that is not explicit." + "src/prevector.h:.* Class 'iterator' has a constructor with 1 argument that is not explicit." + "src/prevector.h:.* Class 'reverse_iterator' has a constructor with 1 argument that is not explicit." + "src/primitives/block.h:.* Class 'CBlock' has a constructor with 1 argument that is not explicit." + "src/primitives/transaction.h:.* Class 'CTransaction' has a constructor with 1 argument that is not explicit." + "src/primitives/transaction.h:.* Struct 'CMutableTransaction' has a constructor with 1 argument that is not explicit." + "src/libzerocoin/bignum.h:.* Class 'CBigNum' has a constructor with 1 argument that is not explicit." + "src/qt/walletmodel.h:.* Struct 'SendCoinsReturn' has a constructor with 1 argument that is not explicit." + "src/rpc/server.h:.* Struct 'UniValueType' has a constructor with 1 argument that is not explicit." + "src/sapling/incrementalmerkletree.h:.* Class 'PedersenHash' has a constructor with 1 argument that is not explicit." + "src/sapling/incrementalmerkletree.h:.* Class 'SHA256Compress' has a constructor with 1 argument that is not explicit." + "src/script/standard.h:.* Class 'CScriptID' has a constructor with 1 argument that is not explicit." + "src/span.h:.* Class 'Span' has a constructor with 1 argument that is not explicit." + "src/span.h:.* Class 'Span < const char >' has a constructor with 1 argument that is not explicit." + "src/span.h:.* Class 'Span < const uint8_t >' has a constructor with 1 argument that is not explicit." + "src/span.h:.* Class 'Span < const unsigned char >' has a constructor with 1 argument that is not explicit." + "src/span.h:.* Class 'Span < uint8_t >' has a constructor with 1 argument that is not explicit." + "src/span.h:.* Class 'Span < unsigned char >' has a constructor with 1 argument that is not explicit." + "src/support/allocators/secure.h:.* Struct 'secure_allocator < char >' has a constructor with 1 argument that is not explicit." + "src/support/allocators/secure.h:.* Struct 'secure_allocator < RNGState >' has a constructor with 1 argument that is not explicit." + "src/support/allocators/secure.h:.* Struct 'secure_allocator < unsigned char >' has a constructor with 1 argument that is not explicit." + "src/support/allocators/zeroafterfree.h:.* Struct 'zero_after_free_allocator < char >' has a constructor with 1 argument that is not explicit." + "src/wallet/wallet.h:.* Struct 'Confirmation' has a constructor with 1 argument that is not explicit." +) + +if [ ! -f cppcheck.txt ]; then + echo "cppcheck.txt cache not found, skipping linting." + exit 1 +fi + +function join_array { + local IFS="$1" + shift + echo "$*" +} + +ENABLED_CHECKS_REGEXP=$(join_array "|" "${ENABLED_CHECKS[@]}") +IGNORED_WARNINGS_REGEXP=$(join_array "|" "${IGNORED_WARNINGS[@]}") + +WARNINGS=$(< cppcheck.txt grep -E "${ENABLED_CHECKS_REGEXP}" | grep -vE "${IGNORED_WARNINGS_REGEXP}") + +if [[ ${WARNINGS} != "" ]]; then + echo "${WARNINGS}" + echo + echo "Advice not applicable in this specific case? Add an exception by updating" + echo "IGNORED_WARNINGS in $0" + # Uncomment to enforce the developer note policy "By default, declare single-argument constructors `explicit`" + # exit 1 +fi +exit 0 \ No newline at end of file diff --git a/test/lint/run-cppcheck.sh b/test/lint/run-cppcheck.sh new file mode 100755 index 00000000000000..913ce6efd1c50a --- /dev/null +++ b/test/lint/run-cppcheck.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2024 The PIVX Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +export LC_ALL=C + +CPPCHECK_BUILD_DIR=".ci-cppcheck" + +if ! command -v cppcheck > /dev/null; then + echo "Skipping cppcheck linting since cppcheck is not installed. Install by running \"apt install cppcheck\"" + exit 1 +fi + +echo "Building cppcheck cache..." +if [ ! -d "${CPPCHECK_BUILD_DIR}" ]; then + mkdir -p "${CPPCHECK_BUILD_DIR}" +fi + +cppcheck --cppcheck-build-dir="${CPPCHECK_BUILD_DIR}" --enable=all -j "$(getconf _NPROCESSORS_ONLN)" \ +--language=c++ --std=c++14 --template=gcc --check-level=exhaustive --file-filter=*.cpp --file-filter=*.h -isrc/chiabls \ +-isrc/crc32c -isrc/immer -isrc/leveldb -isrc/secp256k1 -isrc/univalue -D__cplusplus -DCLIENT_VERSION_BUILD \ +-DCLIENT_VERSION_IS_RELEASE -DCLIENT_VERSION_MAJOR -DCLIENT_VERSION_MINOR -DCLIENT_VERSION_REVISION -DCOPYRIGHT_YEAR \ +-DDEBUG -DWITH_LOCK -DPACKAGE_NAME -DENABLE_MINING_RPC --library=boost --library=qt -I src/ -I src/qt/ src/ 2> >(sort -u > cppcheck.txt) + +echo "cppcheck cache built." + +exit 0