diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..0bd90b6e8fc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + commit-message: + prefix: "actions" + schedule: + interval: "daily" + groups: + actions-dependencies: + applies-to: version-updates + patterns: + - "*" diff --git a/.github/workflows/CI-cygwin.yml b/.github/workflows/CI-cygwin.yml index 445c0953eb5..852202bafde 100644 --- a/.github/workflows/CI-cygwin.yml +++ b/.github/workflows/CI-cygwin.yml @@ -37,12 +37,12 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up Cygwin - uses: cygwin/cygwin-install-action@master + uses: cygwin/cygwin-install-action@f2009323764960f80959895c7bc3bb30210afe4d # v6 with: platform: ${{ matrix.platform }} packages: ${{ matrix.packages }} diff --git a/.github/workflows/CI-mingw.yml b/.github/workflows/CI-mingw.yml index 1b0cf3e5672..53adb127234 100644 --- a/.github/workflows/CI-mingw.yml +++ b/.github/workflows/CI-mingw.yml @@ -33,12 +33,12 @@ jobs: timeout-minutes: 19 # max + 3*std of the last 7K runs steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up MSYS2 - uses: msys2/setup-msys2@v2 + uses: msys2/setup-msys2@fb197b72ce45fb24f17bf3f807a388985654d1f2 # v2.29.0 with: release: false # use pre-installed # TODO: install mingw-w64-x86_64-make and use mingw32.make instead - currently fails with "Windows Subsystem for Linux has no installed distributions." @@ -49,7 +49,7 @@ jobs: mingw-w64-x86_64-gcc - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} diff --git a/.github/workflows/CI-unixish-docker.yml b/.github/workflows/CI-unixish-docker.yml index 4df9b4e9340..4c7a7530fd7 100644 --- a/.github/workflows/CI-unixish-docker.yml +++ b/.github/workflows/CI-unixish-docker.yml @@ -38,7 +38,7 @@ jobs: image: ${{ matrix.image }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -56,7 +56,7 @@ jobs: # needs to be called after the package installation since # - it doesn't call "apt-get update" - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ matrix.image }} @@ -91,7 +91,7 @@ jobs: image: ${{ matrix.image }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -104,7 +104,7 @@ jobs: # needs to be called after the package installation since # - it doesn't call "apt-get update" - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ matrix.image }} diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml index 4478f1b762d..8585e51a836 100644 --- a/.github/workflows/CI-unixish.yml +++ b/.github/workflows/CI-unixish.yml @@ -30,12 +30,12 @@ jobs: CCACHE_SLOPPINESS: pch_defines,time_macros steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} @@ -85,19 +85,19 @@ jobs: CCACHE_SLOPPINESS: pch_defines,time_macros steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} # TODO: move latest compiler to separate step # TODO: bail out on warnings with latest GCC - name: Set up GCC - uses: egor-tensin/setup-gcc@v1 + uses: egor-tensin/setup-gcc@eaa888eb19115a521fa72b65cd94fe1f25bbcaac # v1.3 if: false # matrix.os == 'ubuntu-22.04' with: version: 13 @@ -201,12 +201,12 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} @@ -235,12 +235,12 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} @@ -269,7 +269,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -297,12 +297,12 @@ jobs: CCACHE_SLOPPINESS: pch_defines,time_macros steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} @@ -359,12 +359,12 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} @@ -597,12 +597,12 @@ jobs: runs-on: ubuntu-22.04 # run on the latest image only steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} diff --git a/.github/workflows/CI-windows.yml b/.github/workflows/CI-windows.yml index 55be78ee06e..9e32e1cc514 100644 --- a/.github/workflows/CI-windows.yml +++ b/.github/workflows/CI-windows.yml @@ -33,17 +33,17 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up Visual Studio environment - uses: ilammy/msvc-dev-cmd@v1 + uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 with: arch: x64 - name: Install Qt ${{ matrix.qt_ver }} - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@d325aaf2a8baeeda41ad0b5d39f84a6af9bcf005 # v4.3.0 with: version: ${{ matrix.qt_ver }} modules: 'qtcharts' @@ -87,25 +87,25 @@ jobs: PCRE_VERSION: 8.45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up Python if: matrix.config == 'release' - uses: actions/setup-python@v5 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: '3.14' check-latest: true - name: Set up Visual Studio environment - uses: ilammy/msvc-dev-cmd@v1 + uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 with: arch: x64 - name: Cache PCRE id: cache-pcre - uses: actions/cache@v4 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: | externals\pcre.h diff --git a/.github/workflows/asan.yml b/.github/workflows/asan.yml index 70b3a30f0ae..9981a9a55e1 100644 --- a/.github/workflows/asan.yml +++ b/.github/workflows/asan.yml @@ -28,17 +28,17 @@ jobs: CCACHE_SLOPPINESS: pch_defines,time_macros steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: '3.14' check-latest: true @@ -57,7 +57,7 @@ jobs: sudo ./llvm.sh 21 - name: Install Qt ${{ env.QT_VERSION }} - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@d325aaf2a8baeeda41ad0b5d39f84a6af9bcf005 # v4.3.0 with: version: ${{ env.QT_VERSION }} modules: 'qtcharts' diff --git a/.github/workflows/buildman.yml b/.github/workflows/buildman.yml index b0b399dd851..e010d8af4a8 100644 --- a/.github/workflows/buildman.yml +++ b/.github/workflows/buildman.yml @@ -19,26 +19,26 @@ jobs: convert_via_pandoc: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - run: | mkdir output - - uses: docker://pandoc/latex:3.6.3 + - uses: docker://pandoc/latex:3.6.3@sha256:48831aabd0a24e180a34c0bc5dd09792af43dbd7c2a2d394fbc6b10f9c48fe50 with: args: --output=output/manual.html man/manual.md - - uses: docker://pandoc/latex:3.6.3 + - uses: docker://pandoc/latex:3.6.3@sha256:48831aabd0a24e180a34c0bc5dd09792af43dbd7c2a2d394fbc6b10f9c48fe50 with: args: --output=output/manual.pdf man/manual.md - - uses: docker://pandoc/latex:3.6.3 + - uses: docker://pandoc/latex:3.6.3@sha256:48831aabd0a24e180a34c0bc5dd09792af43dbd7c2a2d394fbc6b10f9c48fe50 with: args: --output=output/manual-premium.pdf man/manual-premium.md - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: output path: output @@ -46,7 +46,7 @@ jobs: manpage: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -59,7 +59,7 @@ jobs: run: | make man - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: cppcheck.1 path: cppcheck.1 diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 7b462c688f0..96464e53aeb 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -14,20 +14,20 @@ jobs: steps: - name: Build Fuzzers id: build - uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@6f5791d8b0ca004e5d35f8d75407347c7848f3b0 # master with: oss-fuzz-project-name: 'cppcheck' dry-run: false language: c++ - name: Run Fuzzers - uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@6f5791d8b0ca004e5d35f8d75407347c7848f3b0 # master with: oss-fuzz-project-name: 'cppcheck' fuzz-seconds: 300 dry-run: false language: c++ - name: Upload Crash - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 if: failure() && steps.build.outcome == 'success' with: name: artifacts diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index 7b2c4f4e2c7..cf1631845aa 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -27,7 +27,7 @@ jobs: QT_VERSION: 6.10.0 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -47,7 +47,7 @@ jobs: sudo apt-get install -y clang-tidy-21 - name: Install Qt ${{ env.QT_VERSION }} - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@d325aaf2a8baeeda41ad0b5d39f84a6af9bcf005 # v4.3.0 with: version: ${{ env.QT_VERSION }} modules: 'qtcharts' @@ -86,7 +86,7 @@ jobs: run: | cmake --build cmake.output --target run-clang-tidy-csa 2> /dev/null - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 if: success() || failure() with: name: Compilation Database diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 12e758d2c9e..30c9fa45d19 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -33,13 +33,13 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 with: languages: ${{ matrix.language }} @@ -49,4 +49,4 @@ jobs: make -j$(nproc) CXXOPTS="-Werror" HAVE_RULES=yes CPPCHK_GLIBCXX_DEBUG= cppcheck - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 13f56172a80..52b4539c529 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -21,12 +21,12 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ runner.os }} @@ -57,12 +57,12 @@ jobs: lcov --extract lcov_tmp.info "$(pwd)/*" --output-file lcov.info genhtml lcov.info -o coverage_report --frame --legend --demangle-cpp - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Coverage results path: coverage_report - - uses: codecov/codecov-action@v4 + - uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 with: token: ${{ secrets.CODECOV_TOKEN }} # file: ./coverage.xml # optional diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index 3c07b61d7c7..109f08875b2 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest if: ${{ github.repository_owner == 'danmar' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Install missing software on ubuntu diff --git a/.github/workflows/cppcheck-premium.yml b/.github/workflows/cppcheck-premium.yml index 42bca8a6ebb..34d9ada691f 100644 --- a/.github/workflows/cppcheck-premium.yml +++ b/.github/workflows/cppcheck-premium.yml @@ -25,7 +25,7 @@ jobs: build: runs-on: ubuntu-24.04 # run on the latest image only steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -60,13 +60,13 @@ jobs: #sed -i 's|"security-severity":.*||' results.sarif cat results.sarif - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: results path: results.sarif - name: Upload report - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 with: sarif_file: results.sarif category: cppcheckpremium diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index fd491c0ec0e..f5259d771e1 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -24,12 +24,12 @@ jobs: UNCRUSTIFY_VERSION: 0.80.1 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Cache uncrustify - uses: actions/cache@v4 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 id: cache-uncrustify with: path: | diff --git a/.github/workflows/iwyu.yml b/.github/workflows/iwyu.yml index 649991f9373..d0d82f872e8 100644 --- a/.github/workflows/iwyu.yml +++ b/.github/workflows/iwyu.yml @@ -42,7 +42,7 @@ jobs: QT_VERSION: 6.10.0 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -107,7 +107,7 @@ jobs: # Also the shell is broken afterwards: # OCI runtime exec failed: exec failed: unable to start container process: exec: "sh": executable file not found in $PATH: unknown - name: Install Qt ${{ env.QT_VERSION }} - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@d325aaf2a8baeeda41ad0b5d39f84a6af9bcf005 # v4.3.0 with: version: ${{ env.QT_VERSION }} modules: 'qtcharts' @@ -160,13 +160,13 @@ jobs: IWYU: include-what-you-use IWYU_CLANG_INC: ${{ matrix.clang_inc }} - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 if: success() || failure() with: name: Compilation Database (include-what-you-use - ${{ matrix.os }} ${{ matrix.stdlib }}) path: ./cmake.output/compile_commands.json - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 if: ${{ contains(matrix.os, 'macos') && (success() || failure()) }} with: name: macOS Mappings @@ -174,7 +174,7 @@ jobs: ./iwyu-mapgen-apple-libc.py ./macos.imp - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 if: success() || failure() with: name: Logs (include-what-you-use - ${{ matrix.os }} ${{ matrix.stdlib }}) @@ -199,7 +199,7 @@ jobs: QT_VERSION: 6.10.0 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -223,7 +223,7 @@ jobs: sudo apt-get install -y libc++-21-dev - name: Install Qt ${{ env.QT_VERSION }} - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@d325aaf2a8baeeda41ad0b5d39f84a6af9bcf005 # v4.3.0 with: version: ${{ env.QT_VERSION }} modules: 'qtcharts' @@ -255,13 +255,13 @@ jobs: # TODO: run multi-threaded find $PWD/cli $PWD/lib $PWD/test $PWD/gui -maxdepth 1 -name "*.cpp" | xargs -t -n 1 clang-include-cleaner-21 --print=changes --extra-arg=-w --extra-arg=-stdlib=${{ matrix.stdlib }} -p cmake.output > clang-include-cleaner.log 2>&1 - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 if: success() || failure() with: name: Compilation Database (clang-include-cleaner - ${{ matrix.stdlib }}) path: ./cmake.output/compile_commands.json - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 if: success() || failure() with: name: Logs (clang-include-cleaner - ${{ matrix.stdlib }}) diff --git a/.github/workflows/release-windows-mingw.yml b/.github/workflows/release-windows-mingw.yml index 3b9b836347f..d4989f1d986 100644 --- a/.github/workflows/release-windows-mingw.yml +++ b/.github/workflows/release-windows-mingw.yml @@ -33,12 +33,12 @@ jobs: timeout-minutes: 19 # max + 3*std of the last 7K runs steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up MSYS2 - uses: msys2/setup-msys2@v2 + uses: msys2/setup-msys2@fb197b72ce45fb24f17bf3f807a388985654d1f2 # v2.29.0 with: release: false # use pre-installed # TODO: install mingw-w64-x86_64-make and use mingw32.make instead - currently fails with "Windows Subsystem for Linux has no installed distributions." @@ -63,7 +63,7 @@ jobs: cp /mingw64/bin/libstdc*.dll cppcheck-mingw/ cp /mingw64/bin/libwinpthread-1.dll cppcheck-mingw/ - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: cppcheck-mingw path: cppcheck-mingw diff --git a/.github/workflows/release-windows.yml b/.github/workflows/release-windows.yml index 2836000acc5..d1af31c5fcc 100644 --- a/.github/workflows/release-windows.yml +++ b/.github/workflows/release-windows.yml @@ -31,12 +31,12 @@ jobs: BOOST_MINOR_VERSION: 89 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up Visual Studio environment - uses: ilammy/msvc-dev-cmd@v1 + uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 - name: Download PCRE run: | @@ -66,7 +66,7 @@ jobs: # available modules: https://github.com/miurahr/aqtinstall/blob/master/docs/getting_started.rst#installing-modules # available tools: https://github.com/miurahr/aqtinstall/blob/master/docs/getting_started.rst#installing-tools - name: Install Qt ${{ env.QT_VERSION }} - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@d325aaf2a8baeeda41ad0b5d39f84a6af9bcf005 # v4.3.0 with: version: ${{ env.QT_VERSION }} modules: 'qtcharts' @@ -89,7 +89,7 @@ jobs: del build\bin\Release\cppcheck-gui.ilk || exit /b !errorlevel! del build\bin\Release\cppcheck-gui.pdb || exit /b !errorlevel! - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: deploy path: build\bin\Release @@ -103,7 +103,7 @@ jobs: env: _CL_: /WX - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bin path: bin @@ -152,7 +152,7 @@ jobs: :: copy libcrypto-3-x64.dll and libssl-3-x64.dll copy %RUNNER_WORKSPACE%\Qt\Tools\OpenSSLv3\Win_x64\bin\lib*.dll win_installer\files || exit /b !errorlevel! - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: collect path: win_installer\files @@ -167,7 +167,7 @@ jobs: @echo ProductVersion="%PRODUCTVER%" || exit /b !errorlevel! msbuild -m cppcheck.wixproj -p:Platform=x64,ProductVersion=%PRODUCTVER%.${{ github.run_number }} || exit /b !errorlevel! - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: installer path: win_installer/Build/ @@ -203,7 +203,7 @@ jobs: del win_installer\files\Qt6Svg.dll || exit /b !errorlevel! del win_installer\files\vc_redist.x64.exe || exit /b !errorlevel! - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: portable path: win_installer\files diff --git a/.github/workflows/scriptcheck.yml b/.github/workflows/scriptcheck.yml index 66e54ddff98..eed164344c5 100644 --- a/.github/workflows/scriptcheck.yml +++ b/.github/workflows/scriptcheck.yml @@ -21,17 +21,17 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ runner.os }} - name: Cache Cppcheck - uses: actions/cache@v4 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: cppcheck key: ${{ runner.os }}-scriptcheck-cppcheck-${{ github.sha }} @@ -56,19 +56,19 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false # TODO: bailout on error - name: Restore Cppcheck - uses: actions/cache@v4 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: cppcheck key: ${{ runner.os }}-scriptcheck-cppcheck-${{ github.sha }} - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: ${{ matrix.python-version }} check-latest: true @@ -209,7 +209,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false diff --git a/.github/workflows/selfcheck.yml b/.github/workflows/selfcheck.yml index 61cc1463274..ae67f01a3aa 100644 --- a/.github/workflows/selfcheck.yml +++ b/.github/workflows/selfcheck.yml @@ -24,12 +24,12 @@ jobs: QT_VERSION: 6.10.0 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ runner.os }} @@ -42,7 +42,7 @@ jobs: sudo apt-get install -y libgl-dev # fixes missing dependency for Qt in CMake - name: Install Qt ${{ env.QT_VERSION }} - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@d325aaf2a8baeeda41ad0b5d39f84a6af9bcf005 # v4.3.0 with: version: ${{ env.QT_VERSION }} modules: 'qtcharts' @@ -198,7 +198,7 @@ jobs: env: DISABLE_VALUEFLOW: 1 - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Callgrind Output path: ./callgrind.* diff --git a/.github/workflows/tsan.yml b/.github/workflows/tsan.yml index 27ed6606386..ec748ad6276 100644 --- a/.github/workflows/tsan.yml +++ b/.github/workflows/tsan.yml @@ -27,17 +27,17 @@ jobs: CCACHE_SLOPPINESS: pch_defines,time_macros steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: '3.14' check-latest: true @@ -56,7 +56,7 @@ jobs: sudo ./llvm.sh 21 - name: Install Qt ${{ env.QT_VERSION }} - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@d325aaf2a8baeeda41ad0b5d39f84a6af9bcf005 # v4.3.0 with: version: ${{ env.QT_VERSION }} modules: 'qtcharts' diff --git a/.github/workflows/ubsan.yml b/.github/workflows/ubsan.yml index 64eb02a4b25..886ab66b935 100644 --- a/.github/workflows/ubsan.yml +++ b/.github/workflows/ubsan.yml @@ -27,17 +27,17 @@ jobs: CCACHE_SLOPPINESS: pch_defines,time_macros steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: '3.14' check-latest: true @@ -56,7 +56,7 @@ jobs: sudo ./llvm.sh 21 - name: Install Qt ${{ env.QT_VERSION }} - uses: jurplel/install-qt-action@v4 + uses: jurplel/install-qt-action@d325aaf2a8baeeda41ad0b5d39f84a6af9bcf005 # v4.3.0 with: version: ${{ env.QT_VERSION }} modules: 'qtcharts' diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml index 9a6026aa25b..5429b1ffc7e 100644 --- a/.github/workflows/valgrind.yml +++ b/.github/workflows/valgrind.yml @@ -21,12 +21,12 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2.20 with: key: ${{ github.workflow }}-${{ runner.os }} @@ -58,7 +58,7 @@ jobs: #env: # DEBUGINFOD_URLS: https://debuginfod.ubuntu.com - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 if: success() || failure() with: name: Logs diff --git a/htmlreport/cppcheck-htmlreport b/htmlreport/cppcheck-htmlreport index 915fda97300..5044443a0a9 100755 --- a/htmlreport/cppcheck-htmlreport +++ b/htmlreport/cppcheck-htmlreport @@ -356,6 +356,57 @@ HTML_HEAD = """ } } + function reapplyFilters() { + // Reapply ID filters + var idToggles = document.querySelectorAll(".idToggle"); + for (var i = 1; i < idToggles.length; i++) { + var cb = idToggles[i]; + var elements = document.querySelectorAll("." + cb.id); + for (var j = 0; j < elements.length; j++) { + elements[j].classList.toggle("id-filtered", !cb.checked); + } + } + + // Reapply severity filters + var sevToggles = document.querySelectorAll(".sev_toggle"); + for (var i = 0; i < sevToggles.length; i++) { + var cb = sevToggles[i]; + var elements = document.querySelectorAll(".sev_" + cb.id); + for (var j = 0; j < elements.length; j++) { + elements[j].classList.toggle("severity-filtered", !cb.checked); + } + } + + // Reapply classification filters + var classToggles = document.querySelectorAll(".class_toggle"); + for (var i = 0; i < classToggles.length; i++) { + var cb = classToggles[i]; + var elements = document.querySelectorAll(".class_" + cb.id); + for (var j = 0; j < elements.length; j++) { + elements[j].classList.toggle("classification-filtered", !cb.checked); + } + } + + // Reapply tool filters + var toolToggles = document.querySelectorAll(".tool_toggle"); + for (var i = 0; i < toolToggles.length; i++) { + var cb = toolToggles[i]; + var elements; + if (cb.id == "clang-tidy") + elements = document.querySelectorAll("[class^=clang-tidy-]"); + else + elements = document.querySelectorAll(".issue:not([class^=clang-tidy-])"); + + for (var j = 0; j < elements.length; j++) { + elements[j].classList.toggle("tool-filtered", !cb.checked); + } + } + + // Update file rows + updateFileRows(); + } + + window.addEventListener("pageshow", reapplyFilters); window.addEventListener("load", initExpandables); @@ -494,9 +545,20 @@ def tr_str(td_th, line, id, cwe, severity, classification, guideline, message, t if classification: items.extend([classification, guideline]) if htmlfile: - ret += '<%s>%d' % (td_th, htmlfile, line, line, td_th) + if htmlfile.startswith("http://") or htmlfile.startswith("https://"): + # GitHub/GitLab style line anchor + href = f"{htmlfile.rstrip('#L1')}#L{line}" + # Emit **line number with link** + ret += f'<{td_th}>{line}' + else: + # local HTML annotated + href = f"{htmlfile}#line-{line}" + # Emit **line number with link** + ret += f'<{td_th}>{line}' + + # Emit id, cwe, severity, classification, ... for item in items: - ret += '<%s>%s' % (td_th, item, td_th) + ret += f'<{td_th}>{item}' else: items.insert(0,line) for item in items: @@ -675,7 +737,9 @@ def main() -> None: 'written.') parser.add_argument('--source-dir', dest='source_dir', help='Base directory where source code files can be ' - 'found.') + 'found, or a URL to a remote GitHub/GitLab ' + 'repository including a branch, e.g. ' + '--source-dir=https://github.com///blob//') parser.add_argument('--add-author-information', dest='add_author_information', help='Blame information to include. ' 'Adds specified author information. ' @@ -705,6 +769,10 @@ def main() -> None: if options.source_dir: source_dir = options.source_dir + is_remote = False + if source_dir.startswith("http://") or source_dir.startswith("https://"): + is_remote = True + add_author_information = [] if options.add_author_information: fields = [x.strip() for x in options.add_author_information.split(',')] @@ -753,9 +821,14 @@ def main() -> None: for error in contentHandler.errors: filename = error['file'] if filename not in files: - files[filename] = { - 'errors': [], 'htmlfile': str(file_no) + '.html'} - file_no = file_no + 1 + if is_remote: + # Construct remote URL for GitHub/GitLab + # tr_str() will use the actual line number, so we can just start with line 1 + remote_url = source_dir.rstrip('/') + '/' + filename + '#L1' + files[filename] = {'errors': [], 'htmlfile': remote_url} + else: + files[filename] = {'errors': [], 'htmlfile': str(file_no) + '.html'} + file_no += 1 files[filename]['errors'].append(error) # Make sure that the report directory is created if it doesn't exist. @@ -795,6 +868,11 @@ def main() -> None: if filename == '': continue + if is_remote: + # Remote source: do NOT generate local annotated HTML files. + # The index will still point directly to GitHub/GitLab URLs. + continue + source_filename = os.path.join(source_dir, filename) try: with io.open(source_filename, 'r', encoding=options.source_encoding) as input_file: diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 68218b4402d..d5d38bc3453 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -3678,7 +3678,7 @@ bool isGlobalData(const Token *expr) // TODO check if pointer points at local data const Variable *lhsvar = tok->astOperand1()->variable(); const ValueType *lhstype = tok->astOperand1()->valueType(); - if (lhsvar->isPointer()) { + if (lhsvar->isPointer() || !lhstype || lhstype->type == ValueType::Type::ITERATOR) { globalData = true; return ChildrenToVisit::none; } @@ -3686,7 +3686,7 @@ bool isGlobalData(const Token *expr) globalData = true; return ChildrenToVisit::none; } - if (lhsvar->isArgument() && (!lhstype || (lhstype->type <= ValueType::Type::VOID && !lhstype->container))) { + if (lhsvar->isArgument() && lhstype->type <= ValueType::Type::VOID && !lhstype->container) { globalData = true; return ChildrenToVisit::none; } diff --git a/lib/check64bit.cpp b/lib/check64bit.cpp index eeda1c93648..aa8a9bcb18a 100644 --- a/lib/check64bit.cpp +++ b/lib/check64bit.cpp @@ -45,7 +45,14 @@ static bool is32BitIntegerReturn(const Function* func, const Settings* settings) if (settings->platform.sizeof_pointer != 8) return false; const ValueType* vt = func->arg->valueType(); - return vt && vt->pointer == 0 && vt->isIntegral() && vt->typeSize(settings->platform) == 4; + return vt && vt->pointer == 0 && vt->isIntegral() && vt->getSizeOf(*settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer) == 4; +} + +static bool isFunctionPointer(const Token* tok) +{ + if (!tok || !tok->variable()) + return false; + return Tokenizer::isFunctionPointer(tok->variable()->nameToken()); } void Check64BitPortability::pointerassignment() @@ -120,7 +127,8 @@ void Check64BitPortability::pointerassignment() !tok->astOperand2()->isNumber() && rhstype->pointer == 0U && rhstype->originalTypeName.empty() && - rhstype->type == ValueType::Type::INT) + rhstype->type == ValueType::Type::INT && + !isFunctionPointer(tok->astOperand1())) assignmentIntegerToAddressError(tok); // Assign pointer to integer.. diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index 337f2b1de3f..57c46492bc0 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -96,7 +96,7 @@ static int getMinFormatStringOutputLength(const std::vector ¶m std::string digits_string; bool i_d_x_f_found = false; int parameterLength = 0; - int inputArgNr = formatStringArgNr; + nonneg int inputArgNr = formatStringArgNr; for (std::size_t i = 1; i + 1 < formatString.length(); ++i) { if (formatString[i] == '\\') { if (i < formatString.length() - 1 && formatString[i + 1] == '0') @@ -229,7 +229,8 @@ static bool getDimensionsEtc(const Token * const arrayToken, const Settings &set Dimension dim; dim.known = value->isKnown(); dim.tok = nullptr; - const MathLib::bigint typeSize = array->valueType()->typeSize(settings.platform, array->valueType()->pointer > 1); + const auto sizeOf = array->valueType()->pointer > 1 ? ValueType::SizeOf::Pointer : ValueType::SizeOf::Pointee; + const size_t typeSize = array->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, sizeOf); if (typeSize == 0) return false; dim.num = value->intvalue / typeSize; @@ -585,7 +586,7 @@ ValueFlow::Value CheckBufferOverrun::getBufferSize(const Token *bufTok) const if (var->isPointerArray()) v.intvalue = dim * mSettings->platform.sizeof_pointer; else { - const MathLib::bigint typeSize = bufTok->valueType()->typeSize(mSettings->platform); + const size_t typeSize = bufTok->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); v.intvalue = dim * typeSize; } @@ -929,7 +930,7 @@ bool CheckBufferOverrun::isCtuUnsafeBufferUsage(const Settings &settings, const { if (!offset) return false; - if (!argtok->valueType() || argtok->valueType()->typeSize(settings.platform) == 0) + if (!argtok->valueType() || argtok->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee) == 0) return false; const Token *indexTok = nullptr; if (type == 1 && Token::Match(argtok, "%name% [") && argtok->astParent() == argtok->next() && !Token::simpleMatch(argtok->linkAt(1), "] [")) @@ -942,7 +943,7 @@ bool CheckBufferOverrun::isCtuUnsafeBufferUsage(const Settings &settings, const return false; if (!indexTok->hasKnownIntValue()) return false; - offset->value = indexTok->getKnownIntValue() * argtok->valueType()->typeSize(settings.platform); + offset->value = indexTok->getKnownIntValue() * argtok->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); return true; } @@ -1102,7 +1103,7 @@ void CheckBufferOverrun::objectIndex() continue; } if (obj->valueType() && var->valueType() && (obj->isCast() || (obj->isCpp() && isCPPCast(obj)) || obj->valueType()->pointer)) { // allow cast to a different type - const auto varSize = var->valueType()->typeSize(mSettings->platform); + const auto varSize = var->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); if (varSize == 0) continue; if (obj->valueType()->type != var->valueType()->type) { diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 03aa85f0a3c..34a734fecaa 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -3459,9 +3459,7 @@ void CheckClass::checkReturnByReference() const bool isView = isContainer && var->valueType()->container->view; bool warn = isContainer && !isView; if (!warn && !isView) { - const std::size_t size = ValueFlow::getSizeOf(*var->valueType(), - *mSettings, - ValueFlow::Accuracy::LowerBound); + const std::size_t size = var->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); if (size > 2 * mSettings->platform.sizeof_pointer) warn = true; } diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index 5953dce4c96..83f7227091c 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -1149,6 +1149,12 @@ void CheckLeakAutoVar::leakIfAllocated(const Token *vartok, } } +static bool isSafeCast(const ValueType* vt, const Settings& settings) +{ + const size_t sizeOf = vt->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); + return sizeOf == 0 || sizeOf >= settings.platform.sizeof_pointer; +} + void CheckLeakAutoVar::ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope) { const std::map &alloctype = varInfo.alloctype; @@ -1182,8 +1188,7 @@ void CheckLeakAutoVar::ret(const Token *tok, VarInfo &varInfo, const bool isEndO while (tok3 && tok3->isCast() && (!tok3->valueType() || tok3->valueType()->pointer || - (tok3->valueType()->typeSize(mSettings->platform) == 0) || - (tok3->valueType()->typeSize(mSettings->platform) >= mSettings->platform.sizeof_pointer))) + isSafeCast(tok3->valueType(), *mSettings))) tok3 = tok3->astOperand2() ? tok3->astOperand2() : tok3->astOperand1(); if (tok3 && tok3->varId() == varid) tok2 = tok3->next(); diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 355b0d809d7..7f49257d1a3 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -1071,8 +1071,8 @@ void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) const Variable* argvar = tok->function()->getArgumentVar(argnr); if (!argvar || !argvar->valueType()) continue; - const MathLib::bigint argSize = argvar->valueType()->typeSize(mSettings->platform, /*p*/ true); - if (argSize <= 0 || argSize >= mSettings->platform.sizeof_pointer) + const size_t argSize = argvar->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); + if (argSize == 0 || argSize >= mSettings->platform.sizeof_pointer) continue; } functionCallLeak(arg, arg->str(), functionName); diff --git a/lib/checkother.cpp b/lib/checkother.cpp index eed6dfe0bf1..e1728788ecb 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -1524,7 +1524,7 @@ static bool isLargeContainer(const Variable* var, const Settings& settings) return false; } const ValueType vtElem = ValueType::parseDecl(vt->containerTypeToken, settings); - const auto elemSize = std::max(ValueFlow::getSizeOf(vtElem, settings, ValueFlow::Accuracy::LowerBound), 1); + const auto elemSize = std::max(vtElem.getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer), 1); const auto arraySize = var->dimension(0) * elemSize; return arraySize > maxByValueSize; } @@ -1564,7 +1564,7 @@ void CheckOther::checkPassByReference() // Ensure that it is a large object. if (!var->type()->classScope) inconclusive = true; - else if (!var->valueType() || ValueFlow::getSizeOf(*var->valueType(), *mSettings, ValueFlow::Accuracy::LowerBound) <= 2 * mSettings->platform.sizeof_pointer) + else if (!var->valueType() || var->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer) <= 2 * mSettings->platform.sizeof_pointer) continue; } else @@ -3327,7 +3327,8 @@ void CheckOther::checkRedundantCopy() const Token* varTok = fScope->bodyEnd->tokAt(-2); if (varTok->variable() && !varTok->variable()->isGlobal() && (!varTok->variable()->type() || !varTok->variable()->type()->classScope || - (varTok->variable()->valueType() && ValueFlow::getSizeOf(*varTok->variable()->valueType(), *mSettings, ValueFlow::Accuracy::LowerBound) > 2 * mSettings->platform.sizeof_pointer))) + (varTok->variable()->valueType() && + varTok->variable()->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer) > 2 * mSettings->platform.sizeof_pointer))) redundantCopyError(startTok, startTok->str()); } } @@ -3447,7 +3448,7 @@ void CheckOther::checkIncompleteArrayFill() if (size == 0 && var->valueType()->pointer) size = mSettings->platform.sizeof_pointer; else if (size == 0 && var->valueType()) - size = ValueFlow::getSizeOf(*var->valueType(), *mSettings, ValueFlow::Accuracy::LowerBound); + size = var->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); const Token* tok3 = tok->next()->astOperand2()->astOperand1()->astOperand1(); if ((size != 1 && size != 100 && size != 0) || var->isPointer()) { if (printWarning) @@ -4430,8 +4431,7 @@ static UnionMember parseUnionMember(const Variable &var, if (var.isArray()) { size = var.dimension(0); } else if (vt != nullptr) { - size = ValueFlow::getSizeOf(*vt, settings, - ValueFlow::Accuracy::ExactOrZero); + size = vt->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); } return UnionMember(nameToken->str(), size); } @@ -4523,7 +4523,7 @@ static bool getBufAndOffset(const Token *expr, const Token *&buf, MathLib::bigin bufToken = expr->astOperand1()->astOperand1(); offsetToken = expr->astOperand1()->astOperand2(); if (expr->astOperand1()->valueType()) - elementSize = ValueFlow::getSizeOf(*expr->astOperand1()->valueType(), settings, ValueFlow::Accuracy::LowerBound); + elementSize = expr->astOperand1()->valueType()->getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); } else if (Token::Match(expr, "+|-") && expr->isBinaryOp()) { const bool pointer1 = (expr->astOperand1()->valueType() && expr->astOperand1()->valueType()->pointer > 0); const bool pointer2 = (expr->astOperand2()->valueType() && expr->astOperand2()->valueType()->pointer > 0); @@ -4532,13 +4532,13 @@ static bool getBufAndOffset(const Token *expr, const Token *&buf, MathLib::bigin offsetToken = expr->astOperand2(); auto vt = *expr->astOperand1()->valueType(); --vt.pointer; - elementSize = ValueFlow::getSizeOf(vt, settings, ValueFlow::Accuracy::LowerBound); + elementSize = vt.getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); } else if (!pointer1 && pointer2) { bufToken = expr->astOperand2(); offsetToken = expr->astOperand1(); auto vt = *expr->astOperand2()->valueType(); --vt.pointer; - elementSize = ValueFlow::getSizeOf(vt, settings, ValueFlow::Accuracy::LowerBound); + elementSize = vt.getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); } else { return false; } @@ -4547,7 +4547,7 @@ static bool getBufAndOffset(const Token *expr, const Token *&buf, MathLib::bigin *offset = 0; auto vt = *expr->valueType(); --vt.pointer; - elementSize = ValueFlow::getSizeOf(vt, settings, ValueFlow::Accuracy::LowerBound); + elementSize = vt.getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); if (elementSize > 0) { *offset *= elementSize; if (sizeValue) diff --git a/lib/checktype.cpp b/lib/checktype.cpp index 92d19c1e794..68b6580d270 100644 --- a/lib/checktype.cpp +++ b/lib/checktype.cpp @@ -322,12 +322,8 @@ static bool checkTypeCombination(ValueType src, ValueType tgt, const Settings& s src.reference = Reference::None; tgt.reference = Reference::None; - const std::size_t sizeSrc = ValueFlow::getSizeOf(src, - settings, - ValueFlow::Accuracy::ExactOrZero); - const std::size_t sizeTgt = ValueFlow::getSizeOf(tgt, - settings, - ValueFlow::Accuracy::ExactOrZero); + const std::size_t sizeSrc = src.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); + const std::size_t sizeTgt = tgt.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); if (!(sizeSrc > 0 && sizeTgt > 0 && sizeSrc < sizeTgt)) return false; diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index b807199ca30..ddaffa0f817 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -1283,12 +1283,6 @@ void CheckUnusedVar::checkFunctionVariableUsage() if (!tok->astOperand1()) continue; - const Token *iteratorToken = tok->astOperand1(); - while (Token::Match(iteratorToken, "[.*]")) - iteratorToken = iteratorToken->astOperand1(); - if (iteratorToken && iteratorToken->variable() && iteratorToken->variable()->typeEndToken()->str().find("iterator") != std::string::npos) - continue; - const Token *op1tok = tok->astOperand1(); while (Token::Match(op1tok, ".|[|*")) op1tok = op1tok->astOperand1(); diff --git a/lib/clangimport.cpp b/lib/clangimport.cpp index 9f3bf593755..87877fd629e 100644 --- a/lib/clangimport.cpp +++ b/lib/clangimport.cpp @@ -1586,7 +1586,7 @@ static void setValues(const Tokenizer &tokenizer, const SymbolDatabase *symbolDa return v * dim.num; }); if (var.valueType()) - typeSize += mul * var.valueType()->typeSize(settings.platform, true); + typeSize += mul * var.valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); } scope.definedType->sizeOf = typeSize; } @@ -1594,8 +1594,8 @@ static void setValues(const Tokenizer &tokenizer, const SymbolDatabase *symbolDa for (auto *tok = const_cast(tokenizer.tokens()); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { ValueType vt = ValueType::parseDecl(tok->tokAt(2), settings); - const MathLib::bigint sz = vt.typeSize(settings.platform, true); - if (sz <= 0) + const size_t sz = vt.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); + if (sz == 0) continue; long long mul = 1; for (const Token *arrtok = tok->linkAt(1)->previous(); arrtok; arrtok = arrtok->previous()) { diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 9490f62475d..7a637c8b7b3 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -1602,6 +1602,9 @@ void CppCheck::executeAddons(const std::vector& files, const std::s } errmsg.file0 = file0; + if (obj.count("cwe")>0) + errmsg.cwe = CWE(obj["cwe"].get()); + if (obj.count("hash")>0) errmsg.hash = obj["hash"].get(); diff --git a/lib/ctu.cpp b/lib/ctu.cpp index 833a4dfda4b..aadba39d3c7 100644 --- a/lib/ctu.cpp +++ b/lib/ctu.cpp @@ -368,7 +368,7 @@ CTU::FileInfo *CTU::getFileInfo(const Tokenizer &tokenizer) functionCall.location = FileInfo::Location(tokenizer, tok); functionCall.callArgNr = argnr + 1; functionCall.callArgumentExpression = argtok->expressionString(); - const auto typeSize = argtok->valueType()->typeSize(tokenizer.getSettings().platform); + const auto typeSize = argtok->valueType()->getSizeOf(tokenizer.getSettings(), ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); functionCall.callArgValue.value = typeSize > 0 ? argtok->variable()->dimension(0) * typeSize : -1; functionCall.warning = false; fileInfo->functionCalls.push_back(std::move(functionCall)); @@ -382,7 +382,7 @@ CTU::FileInfo *CTU::getFileInfo(const Tokenizer &tokenizer) functionCall.location = FileInfo::Location(tokenizer, tok); functionCall.callArgNr = argnr + 1; functionCall.callArgumentExpression = argtok->expressionString(); - functionCall.callArgValue.value = argtok->astOperand1()->valueType()->typeSize(tokenizer.getSettings().platform); + functionCall.callArgValue.value = argtok->astOperand1()->valueType()->getSizeOf(tokenizer.getSettings(), ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); functionCall.warning = false; fileInfo->functionCalls.push_back(std::move(functionCall)); } diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 0e02e3bb488..636e5a848a1 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -88,18 +88,22 @@ static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std: const std::string::size_type pos1 = comment.find_first_not_of("/* \t"); if (pos1 == std::string::npos) return false; - if (pos1 + cppchecksuppress.size() >= comment.size()) - return false; if (comment.substr(pos1, cppchecksuppress.size()) != cppchecksuppress) return false; + if (pos1 + cppchecksuppress.size() >= comment.size()) { + bad.emplace_back(tok->location.file(), tok->location.line, 0, "suppression without error ID"); + return false; + } // check if it has a prefix const std::string::size_type posEndComment = comment.find_first_of(" [", pos1+cppchecksuppress.size()); // skip spaces after "cppcheck-suppress" and its possible prefix const std::string::size_type pos2 = comment.find_first_not_of(' ', posEndComment); - if (pos2 == std::string::npos) + if (pos2 == std::string::npos) { + bad.emplace_back(tok->location.file(), tok->location.line, 0, "suppression without error ID"); return false; + } SuppressionList::Type errorType = SuppressionList::Type::unique; @@ -142,9 +146,11 @@ static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std: s.lineNumber = tok->location.line; } + // TODO: return false? if (!errmsg.empty()) bad.emplace_back(tok->location.file(), tok->location.line, tok->location.col, std::move(errmsg)); + // TODO: report ones without ID - return false? std::copy_if(suppressions.cbegin(), suppressions.cend(), std::back_inserter(inlineSuppressions), [](const SuppressionList::Suppression& s) { return !s.errorId.empty(); }); @@ -159,9 +165,12 @@ static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std: s.type = errorType; s.lineNumber = tok->location.line; + // TODO: report when no ID - unreachable? if (!s.errorId.empty()) inlineSuppressions.push_back(std::move(s)); + // TODO: unreachable? + // TODO: return false? if (!errmsg.empty()) bad.emplace_back(tok->location.file(), tok->location.line, tok->location.col, std::move(errmsg)); } diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index b75c9bfed4d..d4107884642 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -6571,23 +6572,6 @@ const Type* SymbolDatabase::findType(const Token *startTok, const Scope *startSc if (startTok->str() == startScope->className && startScope->isClassOrStruct() && startTok->strAt(1) != "::") return startScope->definedType; - if (startTok->isC()) { - const Scope* scope = startScope; - while (scope) { - if (startTok->str() == scope->className && scope->isClassOrStruct()) - return scope->definedType; - const Scope* typeScope = scope->findRecordInNestedList(startTok->str(), /*isC*/ true); - if (typeScope) { - if (startTok->str() == typeScope->className && typeScope->isClassOrStruct()) { - if (const Type* type = typeScope->definedType) - return type; - } - } - scope = scope->nestedIn; - } - return nullptr; - } - const Scope* start_scope = startScope; // absolute path - directly start in global scope @@ -8438,40 +8422,187 @@ bool ValueType::isVolatile(nonneg int indirect) const return false; return volatileness & (1 << (pointer - indirect)); } -MathLib::bigint ValueType::typeSize(const Platform &platform, bool p) const + +namespace { + struct Result + { + size_t total; + bool success; + }; +} + +template +static Result accumulateStructMembers(const Scope* scope, F f, ValueType::Accuracy accuracy) { - if (p && pointer) - return platform.sizeof_pointer; + size_t total = 0; + std::set anonScopes; + for (const Variable& var : scope->varlist) { + if (var.isStatic()) + continue; + const MathLib::bigint bits = var.nameToken() ? var.nameToken()->bits() : -1; + if (const ValueType* vt = var.valueType()) { + if (vt->type == ValueType::Type::RECORD && vt->typeScope == scope) + return {0, false}; + const MathLib::bigint dim = std::accumulate(var.dimensions().cbegin(), var.dimensions().cend(), MathLib::bigint(1), [](MathLib::bigint i1, const Dimension& dim) { + return i1 * dim.num; + }); + if (var.nameToken()->scope() != scope && var.nameToken()->scope()->definedType) { // anonymous union + const auto ret = anonScopes.insert(var.nameToken()->scope()); + if (ret.second) + total = f(total, *vt, dim, bits); + } + else + total = f(total, *vt, dim, bits); + } + if (accuracy == ValueType::Accuracy::ExactOrZero && total == 0 && bits == -1) + return {0, false}; + } + return {total, true}; +} - if (typeScope && typeScope->definedType && typeScope->definedType->sizeOf) - return typeScope->definedType->sizeOf; - switch (type) { - case ValueType::Type::BOOL: - return platform.sizeof_bool; - case ValueType::Type::CHAR: +static size_t bitCeil(size_t x) +{ + if (x <= 1) return 1; - case ValueType::Type::SHORT: + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x |= x >> 32; + return x + 1; +} + +static size_t getAlignOf(const ValueType& vt, const Settings& settings, ValueType::Accuracy accuracy, ValueType::SizeOf sizeOf, int maxRecursion = 0) +{ + if (maxRecursion == settings.vfOptions.maxAlignOfRecursion) { + // TODO: add bailout message + return 0; + } + if ((vt.pointer && sizeOf == ValueType::SizeOf::Pointer) || vt.reference != Reference::None || vt.isPrimitive()) { + auto align = vt.getSizeOf(settings, accuracy, ValueType::SizeOf::Pointer); + return align == 0 ? 0 : bitCeil(align); + } + if (vt.type == ValueType::Type::RECORD && vt.typeScope) { + auto accHelper = [&](size_t max, const ValueType& vt2, size_t /*dim*/, MathLib::bigint /*bits*/) { + size_t a = getAlignOf(vt2, settings, accuracy, ValueType::SizeOf::Pointer, ++maxRecursion); + return std::max(max, a); + }; + Result result = accumulateStructMembers(vt.typeScope, accHelper, accuracy); + size_t total = result.total; + if (const Type* dt = vt.typeScope->definedType) { + total = std::accumulate(dt->derivedFrom.begin(), dt->derivedFrom.end(), total, [&](size_t v, const Type::BaseInfo& bi) { + if (bi.type && bi.type->classScope) + v += accumulateStructMembers(bi.type->classScope, accHelper, accuracy).total; + return v; + }); + } + return result.success ? std::max(1, total) : total; + } + if (vt.type == ValueType::Type::CONTAINER) + return settings.platform.sizeof_pointer; // Just guess + return 0; +} + +size_t ValueType::getSizeOf( const Settings& settings, Accuracy accuracy, SizeOf sizeOf, int maxRecursion) const +{ + if (maxRecursion == settings.vfOptions.maxSizeOfRecursion) { + // TODO: add bailout message + return 0; + } + const auto& platform = settings.platform; + if (sizeOf == SizeOf::Pointer && (pointer || reference != Reference::None)) + return platform.sizeof_pointer; + if (type == ValueType::Type::BOOL || type == ValueType::Type::CHAR) + return 1; + if (type == ValueType::Type::SHORT) return platform.sizeof_short; - case ValueType::Type::WCHAR_T: + if (type == ValueType::Type::WCHAR_T) return platform.sizeof_wchar_t; - case ValueType::Type::INT: + if (type == ValueType::Type::INT) return platform.sizeof_int; - case ValueType::Type::LONG: + if (type == ValueType::Type::LONG) return platform.sizeof_long; - case ValueType::Type::LONGLONG: + if (type == ValueType::Type::LONGLONG) return platform.sizeof_long_long; - case ValueType::Type::FLOAT: + if (type == ValueType::Type::FLOAT) return platform.sizeof_float; - case ValueType::Type::DOUBLE: + if (type == ValueType::Type::DOUBLE) return platform.sizeof_double; - case ValueType::Type::LONGDOUBLE: + if (type == ValueType::Type::LONGDOUBLE) return platform.sizeof_long_double; - default: - break; + if (type == ValueType::Type::CONTAINER) + return 3 * platform.sizeof_pointer; // Just guess + if (type == ValueType::Type::RECORD && typeScope) { + size_t currentBitCount = 0; + size_t currentBitfieldAlloc = 0; + auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim, MathLib::bigint nBits) -> size_t { + const size_t charBit = settings.platform.char_bit; + size_t n = vt2.getSizeOf(settings, accuracy, SizeOf::Pointer, ++maxRecursion); + size_t a = getAlignOf(vt2, settings, accuracy, SizeOf::Pointer); + if (n == 0 || a == 0) + return accuracy == Accuracy::ExactOrZero ? 0 : total; + if (nBits == 0) { + if (currentBitfieldAlloc == 0) { + nBits = n * charBit; + } else { + nBits = (currentBitfieldAlloc * charBit) - currentBitCount; + } + } + if (nBits > 0) { + size_t ret = total; + if (currentBitfieldAlloc == 0) { + currentBitfieldAlloc = n; + currentBitCount = 0; + } else if (currentBitCount + nBits > charBit * currentBitfieldAlloc) { + ret += currentBitfieldAlloc; + currentBitfieldAlloc = n; + currentBitCount = 0; + } + while (nBits > charBit * currentBitfieldAlloc) { + ret += currentBitfieldAlloc; + nBits -= charBit * currentBitfieldAlloc; + } + currentBitCount += nBits; + return ret; + } + n *= dim; + size_t padding = (a - (total % a)) % a; + if (currentBitCount > 0) { + bool fitsInBitfield = currentBitCount + (n * charBit) <= currentBitfieldAlloc * charBit; + bool isAligned = currentBitCount % (charBit * a) == 0; + if (vt2.isIntegral() && fitsInBitfield && isAligned) { + currentBitCount += charBit * n; + return total; + } + n += currentBitfieldAlloc; + currentBitfieldAlloc = 0; + currentBitCount = 0; + } + return typeScope->type == ScopeType::eUnion ? std::max(total, n) : total + padding + n; + }; + Result result = accumulateStructMembers(typeScope, accHelper, accuracy); + size_t total = result.total; + if (currentBitCount > 0) + total += currentBitfieldAlloc; + if (const ::Type* dt = typeScope->definedType) { + total = std::accumulate(dt->derivedFrom.begin(), dt->derivedFrom.end(), total, [&](size_t v, const ::Type::BaseInfo& bi) { + if (bi.type && bi.type->classScope) + v += accumulateStructMembers(bi.type->classScope, accHelper, accuracy).total; + return v; + }); + } + if (accuracy == Accuracy::ExactOrZero && total == 0 && !result.success) + return 0; + total = std::max(size_t{1}, total); + size_t align = getAlignOf(*this, settings, accuracy, sizeOf); + if (align == 0) + return accuracy == Accuracy::ExactOrZero ? 0 : total; + total += (align - (total % align)) % align; + return total; } - - // Unknown invalid size return 0; } diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 50f37fc1ebf..a0e6190db73 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -1312,7 +1312,15 @@ class CPPCHECKLIB ValueType { bool isVolatile(nonneg int indirect = 0) const; - MathLib::bigint typeSize(const Platform &platform, bool p=false) const; + enum class Accuracy : std::uint8_t { + ExactOrZero, + LowerBound, + }; + enum class SizeOf : std::uint8_t { + Pointer, + Pointee, + }; + size_t getSizeOf(const Settings& settings, Accuracy accuracy, SizeOf sizeOf, int maxRecursion = 0) const; /// Check if type is the same ignoring const and references bool isTypeEqual(const ValueType* that) const; diff --git a/lib/token.cpp b/lib/token.cpp index 0c64222801d..68e7838b6be 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -784,7 +784,7 @@ nonneg int Token::getStrSize(const Token *tok, const Settings &settings) if (tok->valueType()) { ValueType vt(*tok->valueType()); vt.pointer = 0; - sizeofType = ValueFlow::getSizeOf(vt, settings, ValueFlow::Accuracy::ExactOrZero); + sizeofType = vt.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); } return getStrArraySize(tok) * sizeofType; } diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index da279b8f453..40cbfbd3580 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -674,7 +674,7 @@ namespace { return; mUsed = true; - const bool isFunctionPointer = Token::Match(mNameToken, "%name% )"); + const bool isFunctionPointer = Tokenizer::isFunctionPointer(mNameToken); // Special handling for T(...) when T is a pointer if (Token::Match(tok, "%name% [({]") && !isFunctionPointer && !Token::simpleMatch(tok->linkAt(1), ") (")) { @@ -1019,6 +1019,10 @@ namespace { }; } +bool Tokenizer::isFunctionPointer(const Token* tok) { + return Token::Match(tok, "%name% ) ("); +} + void Tokenizer::simplifyTypedef() { // Simplify global typedefs that are not redefined with the fast 1-pass simplification. @@ -1088,7 +1092,7 @@ void Tokenizer::simplifyTypedef() typedefInfo.lineNumber = typedefToken->linenr(); typedefInfo.column = typedefToken->column(); typedefInfo.used = t.second.isUsed(); - typedefInfo.isFunctionPointer = Token::Match(t.second.nameToken(), "%name% ) ("); + typedefInfo.isFunctionPointer = isFunctionPointer(t.second.nameToken()); if (typedefInfo.isFunctionPointer) { const Token* tok = typedefToken; while (tok != t.second.endToken()) { @@ -1622,7 +1626,7 @@ void Tokenizer::simplifyTypedefCpp() typedefInfo.lineNumber = typeName->linenr(); typedefInfo.column = typeName->column(); typedefInfo.used = false; - typedefInfo.isFunctionPointer = Token::Match(typeName, "%name% ) ("); + typedefInfo.isFunctionPointer = isFunctionPointer(typeName); if (typedefInfo.isFunctionPointer) { const Token* t = typeDef; while (t != tok) { @@ -7155,7 +7159,7 @@ void Tokenizer::simplifyFunctionPointers() while (Token::Match(tok2, "%type%|:: %type%|::")) tok2 = tok2->next(); - if (!Token::Match(tok2, "%name% ) (") && + if (!isFunctionPointer(tok2) && !Token::Match(tok2, "%name% [ ] ) (") && !(Token::Match(tok2, "%name% (") && Token::simpleMatch(tok2->linkAt(1), ") ) ("))) continue; @@ -7448,7 +7452,7 @@ void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, co } // Function pointer if (Token::simpleMatch(varName, "( *") && - Token::Match(varName->link()->previous(), "%name% ) (") && + isFunctionPointer(varName->link()->previous()) && Token::simpleMatch(varName->link()->linkAt(1), ") =")) { Token *endDecl = varName->link()->linkAt(1); varName = varName->link()->previous(); @@ -9376,7 +9380,7 @@ Token* Tokenizer::getAttributeFuncTok(Token* tok, bool gccattr) const { if (Token::simpleMatch(prev, ")")) { if (Token::Match(prev->link()->previous(), "%name% (")) return prev->link()->previous(); - if (Token::Match(prev->link()->tokAt(-2), "%name% ) (")) + if (isFunctionPointer(prev->link()->tokAt(-2))) return prev->link()->tokAt(-2); } if (Token::simpleMatch(prev, ")") && Token::Match(prev->link()->tokAt(-2), "operator %op% (") && isCPP()) @@ -10085,17 +10089,7 @@ void Tokenizer::simplifyBitfields() if (!Token::Match(tok, ";|{|}|public:|protected:|private:")) continue; - bool isEnum = false; - if (tok->str() == "}") { - const Token *type = tok->link()->previous(); - while (type && type->isName()) { - if (type->str() == "enum") { - isEnum = true; - break; - } - type = type->previous(); - } - } + const bool isEnum = tok->str() == "}" && isEnumStart(tok->link()); const auto tooLargeError = [this](const Token *tok) { const auto max = std::numeric_limits::max(); diff --git a/lib/tokenize.h b/lib/tokenize.h index f9feebb43f0..d001c54cb10 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -601,7 +601,7 @@ class CPPCHECKLIB Tokenizer { /** * Helper function to check whether number is one (1 or 0.1E+1 or 1E+0) or not? * @param s the string to check - * @return true in case is is one and false otherwise. + * @return true in case it is one and false otherwise. */ static bool isOneNumber(const std::string &s); @@ -613,6 +613,13 @@ class CPPCHECKLIB Tokenizer { */ static const Token * startOfExecutableScope(const Token * tok); + /** + * Helper function to check whether tok is the declaration of a function pointer + * @param tok the Token to check + * @return true in case tok is a function pointer and false otherwise. + */ + static bool isFunctionPointer(const Token* tok); + const Settings &getSettings() const { return mSettings; } diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 8eb5735c986..a4e24f5ca37 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -423,187 +423,6 @@ void ValueFlow::combineValueProperties(const ValueFlow::Value &value1, const Val result.path = value1.path; } -namespace { - struct Result - { - size_t total; - bool success; - }; -} - -template -static Result accumulateStructMembers(const Scope* scope, F f, ValueFlow::Accuracy accuracy) -{ - size_t total = 0; - std::set anonScopes; - for (const Variable& var : scope->varlist) { - if (var.isStatic()) - continue; - const MathLib::bigint bits = var.nameToken() ? var.nameToken()->bits() : -1; - if (const ValueType* vt = var.valueType()) { - if (vt->type == ValueType::Type::RECORD && vt->typeScope == scope) - return {0, false}; - const MathLib::bigint dim = std::accumulate(var.dimensions().cbegin(), var.dimensions().cend(), MathLib::bigint(1), [](MathLib::bigint i1, const Dimension& dim) { - return i1 * dim.num; - }); - if (var.nameToken()->scope() != scope && var.nameToken()->scope()->definedType) { // anonymous union - const auto ret = anonScopes.insert(var.nameToken()->scope()); - if (ret.second) - total = f(total, *vt, dim, bits); - } - else - total = f(total, *vt, dim, bits); - } - if (accuracy == ValueFlow::Accuracy::ExactOrZero && total == 0 && bits == -1) - return {0, false}; - } - return {total, true}; -} - -static size_t bitCeil(size_t x) -{ - if (x <= 1) - return 1; - --x; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - x |= x >> 32; - return x + 1; -} - -static size_t getAlignOf(const ValueType& vt, const Settings& settings, ValueFlow::Accuracy accuracy, int maxRecursion = 0) -{ - if (maxRecursion == settings.vfOptions.maxAlignOfRecursion) { - // TODO: add bailout message - return 0; - } - if (vt.pointer || vt.reference != Reference::None || vt.isPrimitive()) { - auto align = ValueFlow::getSizeOf(vt, settings, accuracy); - return align == 0 ? 0 : bitCeil(align); - } - if (vt.type == ValueType::Type::RECORD && vt.typeScope) { - auto accHelper = [&](size_t max, const ValueType& vt2, size_t /*dim*/, MathLib::bigint /*bits*/) { - size_t a = getAlignOf(vt2, settings, accuracy, ++maxRecursion); - return std::max(max, a); - }; - Result result = accumulateStructMembers(vt.typeScope, accHelper, accuracy); - size_t total = result.total; - if (const Type* dt = vt.typeScope->definedType) { - total = std::accumulate(dt->derivedFrom.begin(), dt->derivedFrom.end(), total, [&](size_t v, const Type::BaseInfo& bi) { - if (bi.type && bi.type->classScope) - v += accumulateStructMembers(bi.type->classScope, accHelper, accuracy).total; - return v; - }); - } - return result.success ? std::max(1, total) : total; - } - if (vt.type == ValueType::Type::CONTAINER) - return settings.platform.sizeof_pointer; // Just guess - return 0; -} - -size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings, Accuracy accuracy, int maxRecursion) -{ - if (maxRecursion == settings.vfOptions.maxSizeOfRecursion) { - // TODO: add bailout message - return 0; - } - if (vt.pointer || vt.reference != Reference::None) - return settings.platform.sizeof_pointer; - if (vt.type == ValueType::Type::BOOL || vt.type == ValueType::Type::CHAR) - return 1; - if (vt.type == ValueType::Type::SHORT) - return settings.platform.sizeof_short; - if (vt.type == ValueType::Type::WCHAR_T) - return settings.platform.sizeof_wchar_t; - if (vt.type == ValueType::Type::INT) - return settings.platform.sizeof_int; - if (vt.type == ValueType::Type::LONG) - return settings.platform.sizeof_long; - if (vt.type == ValueType::Type::LONGLONG) - return settings.platform.sizeof_long_long; - if (vt.type == ValueType::Type::FLOAT) - return settings.platform.sizeof_float; - if (vt.type == ValueType::Type::DOUBLE) - return settings.platform.sizeof_double; - if (vt.type == ValueType::Type::LONGDOUBLE) - return settings.platform.sizeof_long_double; - if (vt.type == ValueType::Type::CONTAINER) - return 3 * settings.platform.sizeof_pointer; // Just guess - if (vt.type == ValueType::Type::RECORD && vt.typeScope) { - size_t currentBitCount = 0; - size_t currentBitfieldAlloc = 0; - auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim, MathLib::bigint bits) -> size_t { - const size_t charBit = settings.platform.char_bit; - size_t n = ValueFlow::getSizeOf(vt2, settings,accuracy, ++maxRecursion); - size_t a = getAlignOf(vt2, settings, accuracy); - if (n == 0 || a == 0) - return accuracy == Accuracy::ExactOrZero ? 0 : total; - if (bits == 0) { - if (currentBitfieldAlloc == 0) { - bits = n * charBit; - } else { - bits = (currentBitfieldAlloc * charBit) - currentBitCount; - } - } - if (bits > 0) { - size_t ret = total; - if (currentBitfieldAlloc == 0) { - currentBitfieldAlloc = n; - currentBitCount = 0; - } else if (currentBitCount + bits > charBit * currentBitfieldAlloc) { - ret += currentBitfieldAlloc; - currentBitfieldAlloc = n; - currentBitCount = 0; - } - while (bits > charBit * currentBitfieldAlloc) { - ret += currentBitfieldAlloc; - bits -= charBit * currentBitfieldAlloc; - } - currentBitCount += bits; - return ret; - } - n *= dim; - size_t padding = (a - (total % a)) % a; - if (currentBitCount > 0) { - bool fitsInBitfield = currentBitCount + (n * charBit) <= currentBitfieldAlloc * charBit; - bool isAligned = currentBitCount % (charBit * a) == 0; - if (vt2.isIntegral() && fitsInBitfield && isAligned) { - currentBitCount += charBit * n; - return total; - } - n += currentBitfieldAlloc; - currentBitfieldAlloc = 0; - currentBitCount = 0; - } - return vt.typeScope->type == ScopeType::eUnion ? std::max(total, n) : total + padding + n; - }; - Result result = accumulateStructMembers(vt.typeScope, accHelper, accuracy); - size_t total = result.total; - if (currentBitCount > 0) - total += currentBitfieldAlloc; - if (const Type* dt = vt.typeScope->definedType) { - total = std::accumulate(dt->derivedFrom.begin(), dt->derivedFrom.end(), total, [&](size_t v, const Type::BaseInfo& bi) { - if (bi.type && bi.type->classScope) - v += accumulateStructMembers(bi.type->classScope, accHelper, accuracy).total; - return v; - }); - } - if (accuracy == Accuracy::ExactOrZero && total == 0 && !result.success) - return 0; - total = std::max(size_t{1}, total); - size_t align = getAlignOf(vt, settings, accuracy); - if (align == 0) - return accuracy == Accuracy::ExactOrZero ? 0 : total; - total += (align - (total % align)) % align; - return total; - } - return 0; -} - static void valueFlowNumber(TokenList &tokenlist, const Settings& settings) { for (Token *tok = tokenlist.front(); tok;) { @@ -3683,8 +3502,8 @@ static bool isTruncated(const ValueType* src, const ValueType* dst, const Settin if (src->smartPointer && dst->smartPointer) return false; if ((src->isIntegral() && dst->isIntegral()) || (src->isFloat() && dst->isFloat())) { - const size_t srcSize = ValueFlow::getSizeOf(*src, settings, ValueFlow::Accuracy::LowerBound); - const size_t dstSize = ValueFlow::getSizeOf(*dst, settings, ValueFlow::Accuracy::LowerBound); + const size_t srcSize = src->getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); + const size_t dstSize = dst->getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); if (srcSize > dstSize) return true; if (srcSize == dstSize && src->sign != dst->sign) @@ -4207,10 +4026,10 @@ static std::list truncateValues(std::list va if (!dst || !dst->isIntegral()) return values; - const size_t sz = ValueFlow::getSizeOf(*dst, settings, ValueFlow::Accuracy::ExactOrZero); + const size_t sz = dst->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); if (src) { - const size_t osz = ValueFlow::getSizeOf(*src, settings, ValueFlow::Accuracy::ExactOrZero); + const size_t osz = src->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); if (osz >= sz && dst->sign == ValueType::Sign::SIGNED && src->sign == ValueType::Sign::UNSIGNED) { values.remove_if([&](const ValueFlow::Value& value) { if (!value.isIntValue()) @@ -7078,8 +6897,9 @@ static void valueFlowDynamicBufferSize(const TokenList& tokenlist, const SymbolD if (!typeTok || !typeTok->varId()) typeTok = newTok->astParent()->previous(); // hack for "int** z = ..." if (typeTok && typeTok->valueType()) { - const MathLib::bigint typeSize = typeTok->valueType()->typeSize(settings.platform, typeTok->valueType()->pointer > 1); - if (typeSize >= 0) + const auto sizeOf = typeTok->valueType()->pointer > 1 ? ValueType::SizeOf::Pointer : ValueType::SizeOf::Pointee; + const size_t typeSize = typeTok->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, sizeOf); + if (typeSize > 0 || numElem == 0) sizeValue = numElem * typeSize; } } diff --git a/lib/valueflow.h b/lib/valueflow.h index 85a8e49a9e7..6fe4d052fdf 100644 --- a/lib/valueflow.h +++ b/lib/valueflow.h @@ -61,16 +61,6 @@ namespace ValueFlow { std::string eitherTheConditionIsRedundant(const Token *condition); - enum class Accuracy : std::uint8_t { - ExactOrZero, - LowerBound, - }; - - size_t getSizeOf(const ValueType &vt, - const Settings &settings, - Accuracy accuracy, - int maxRecursion = 0); - const Value* findValue(const std::list& values, const Settings& settings, const std::function &pred); diff --git a/lib/vf_analyzers.cpp b/lib/vf_analyzers.cpp index 104563b113b..5f5568663e4 100644 --- a/lib/vf_analyzers.cpp +++ b/lib/vf_analyzers.cpp @@ -355,9 +355,7 @@ struct ValueFlowAnalyzer : Analyzer { /* Truncate value */ const ValueType *dst = tok->valueType(); if (dst) { - const size_t sz = ValueFlow::getSizeOf(*dst, - settings, - ValueFlow::Accuracy::ExactOrZero); + const size_t sz = dst->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); if (sz > 0 && sz < sizeof(MathLib::biguint)) { MathLib::bigint newvalue = ValueFlow::truncateIntValue(value->intvalue, sz, dst->sign); diff --git a/lib/vf_common.cpp b/lib/vf_common.cpp index 0498a7e303d..56a6b364c44 100644 --- a/lib/vf_common.cpp +++ b/lib/vf_common.cpp @@ -114,7 +114,7 @@ namespace ValueFlow { const ValueType &valueType = ValueType::parseDecl(typeTok, settings); - return getSizeOf(valueType, settings, ValueFlow::Accuracy::ExactOrZero); + return valueType.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); } // Handle various constants.. @@ -125,7 +125,7 @@ namespace ValueFlow MathLib::bigint signedValue = MathLib::toBigNumber(tok); const ValueType* vt = tok->valueType(); if (vt && vt->sign == ValueType::UNSIGNED && signedValue < 0 - && getSizeOf(*vt, settings, ValueFlow::Accuracy::ExactOrZero) + && vt->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer) < sizeof(MathLib::bigint)) { MathLib::bigint minValue{}, maxValue{}; if (getMinMaxValues(tok->valueType(), settings.platform, minValue, maxValue)) @@ -160,9 +160,7 @@ namespace ValueFlow (tok->next()->astOperand2()->valueType()->pointer == 0 || // <- TODO this is a bailout, abort when there are array->pointer conversions (tok->next()->astOperand2()->variable() && !tok->next()->astOperand2()->variable()->isArray())) && !tok->next()->astOperand2()->valueType()->isEnum()) { // <- TODO this is a bailout, handle enum with non-int types - const size_t sz = getSizeOf(*tok->next()->astOperand2()->valueType(), - settings, - ValueFlow::Accuracy::ExactOrZero); + const size_t sz = tok->next()->astOperand2()->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); if (sz) { Value value(sz); value.setKnown(); @@ -181,7 +179,7 @@ namespace ValueFlow } if (Token::simpleMatch(tok, "sizeof ( *")) { const ValueType *vt = tok->tokAt(2)->valueType(); - const size_t sz = vt ? getSizeOf(*vt, settings, ValueFlow::Accuracy::ExactOrZero) + const size_t sz = vt ? vt->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer) : 0; if (sz > 0) { Value value(sz); @@ -243,9 +241,7 @@ namespace ValueFlow if (var->type()->classScope && var->type()->classScope->enumType) size = getSizeOfType(var->type()->classScope->enumType, settings); } else if (var->valueType()) { - size = getSizeOf(*var->valueType(), - settings, - ValueFlow::Accuracy::ExactOrZero); + size = var->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); } else if (!var->type()) { size = getSizeOfType(var->typeStartToken(), settings); } @@ -294,7 +290,7 @@ namespace ValueFlow } } else if (!tok2->type()) { const ValueType& vt = ValueType::parseDecl(tok2, settings); - size_t sz = getSizeOf(vt, settings, ValueFlow::Accuracy::ExactOrZero); + size_t sz = vt.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); const Token* brac = tok2->astParent(); while (Token::simpleMatch(brac, "[")) { const Token* num = brac->astOperand2(); diff --git a/lib/vf_settokenvalue.cpp b/lib/vf_settokenvalue.cpp index 0140b45ae06..4d39d35daae 100644 --- a/lib/vf_settokenvalue.cpp +++ b/lib/vf_settokenvalue.cpp @@ -83,8 +83,8 @@ namespace ValueFlow // If the sign is the same there is no truncation if (vt1->sign == vt2->sign) return value; - const size_t n1 = getSizeOf(*vt1, settings, ValueFlow::Accuracy::ExactOrZero); - const size_t n2 = getSizeOf(*vt2, settings, ValueFlow::Accuracy::ExactOrZero); + const size_t n1 = vt1->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); + const size_t n2 = vt2->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); ValueType::Sign sign = ValueType::Sign::UNSIGNED; if (n1 < n2) sign = vt2->sign; @@ -225,7 +225,7 @@ namespace ValueFlow { // Skip setting values that are too big since its ambiguous if (!value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) - && getSizeOf(*tok->valueType(), settings, ValueFlow::Accuracy::LowerBound) + && tok->valueType()->getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer) >= sizeof(MathLib::bigint)) return; @@ -379,8 +379,8 @@ namespace ValueFlow const ValueType &valueType = ValueType::parseDecl(castType, settings); if (value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) && valueType.sign == ValueType::SIGNED && tok->valueType() - && getSizeOf(*tok->valueType(), settings, ValueFlow::Accuracy::ExactOrZero) - >= getSizeOf(valueType, settings, ValueFlow::Accuracy::ExactOrZero)) + && tok->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer) + >= valueType.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer)) return; setTokenValueCast(parent, valueType, value, settings); } @@ -642,9 +642,7 @@ namespace ValueFlow if (v.isIntValue() || v.isSymbolicValue()) { const ValueType *dst = tok->valueType(); if (dst) { - const size_t sz = ValueFlow::getSizeOf(*dst, - settings, - ValueFlow::Accuracy::ExactOrZero); + const size_t sz = dst->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); MathLib::bigint newvalue = ValueFlow::truncateIntValue(v.intvalue + 1, sz, dst->sign); if (v.bound != ValueFlow::Value::Bound::Point) { if (newvalue < v.intvalue) { @@ -674,9 +672,7 @@ namespace ValueFlow if (v.isIntValue() || v.isSymbolicValue()) { const ValueType *dst = tok->valueType(); if (dst) { - const size_t sz = ValueFlow::getSizeOf(*dst, - settings, - ValueFlow::Accuracy::ExactOrZero); + const size_t sz = dst->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); MathLib::bigint newvalue = ValueFlow::truncateIntValue(v.intvalue - 1, sz, dst->sign); if (v.bound != ValueFlow::Value::Bound::Point) { if (newvalue > v.intvalue) { diff --git a/man/manual-premium.md b/man/manual-premium.md index 6ae337a9fb0..9f2e5992537 100644 --- a/man/manual-premium.md +++ b/man/manual-premium.md @@ -1158,14 +1158,41 @@ The output screen says: Default is reading from stdin. --report-dir=REPORT_DIR The directory where the html report content is written. - --source-dir=SOURCE_DIR - Base directory where source code files can be found. + --source-dir=SOURCE_DIR|URL + Base directory where source code files can be found, or + a URL to a remote GitHub/GitLab repository including a + branch, e.g.: + --source-dir=https://github.com///blob// Example usage: cppcheck gui/test.cpp --xml 2> err.xml cppcheck-htmlreport --file=err.xml --report-dir=test1 --source-dir=. +or + cppcheck gui/test.cpp --xml 2> err.xml + cppcheck-htmlreport --file=err.xml --report-dir=test1 \ + --source-dir=https://github.com///blob// + +## Choosing Between Local Annotated HTML and Remote Repository Links + +cppcheck-htmlreport supports two modes for linking to source files: + - Local annotated HTML files (default when `--source-dir` is a filesystem path) + - Remote GitHub/GitLab links (when `--source-dir` is a URL) + +Pointing `--source-dir` to a filesystem path generates local annotated HTML files. +This is useful when you need a fully self-contained report that works offline, +includes inline annotations, and is ideal for small or medium projects where +generation is fast. +Using a remote GitHub/GitLab URL avoids generating per-file HTML and keeps the +summary report lightweight and fast to produce. This mode is ideal when the +source is already hosted online and local duplication is unnecessary. +Remote mode is especially helpful when the HTML report may be public or widely +distributed but the source code should remain private, since access control is +handled by the hosting service. +In general, local mode fits air-gapped environments, while remote mode works +best for CI workflows and large or private repositories. + # Check Level ## Reduced diff --git a/man/manual.md b/man/manual.md index 9698c56e84b..d8da119f388 100644 --- a/man/manual.md +++ b/man/manual.md @@ -1198,14 +1198,41 @@ The output screen says: Default is reading from stdin. --report-dir=REPORT_DIR The directory where the html report content is written. - --source-dir=SOURCE_DIR - Base directory where source code files can be found. + --source-dir=SOURCE_DIR|URL + Base directory where source code files can be found, or + a URL to a remote GitHub/GitLab repository including a + branch, e.g.: + --source-dir=https://github.com///blob// Example usage: cppcheck gui/test.cpp --xml 2> err.xml cppcheck-htmlreport --file=err.xml --report-dir=test1 --source-dir=. +or + cppcheck gui/test.cpp --xml 2> err.xml + cppcheck-htmlreport --file=err.xml --report-dir=test1 \ + --source-dir=https://github.com///blob// + +## Choosing Between Local Annotated HTML and Remote Repository Links + +cppcheck-htmlreport supports two modes for linking to source files: + - Local annotated HTML files (default when `--source-dir` is a filesystem path) + - Remote GitHub/GitLab links (when `--source-dir` is a URL) + +Pointing `--source-dir` to a filesystem path generates local annotated HTML files. +This is useful when you need a fully self-contained report that works offline, +includes inline annotations, and is ideal for small or medium projects where +generation is fast. +Using a remote GitHub/GitLab URL avoids generating per-file HTML and keeps the +summary report lightweight and fast to produce. This mode is ideal when the +source is already hosted online and local duplication is unnecessary. +Remote mode is especially helpful when the HTML report may be public or widely +distributed but the source code should remain private, since access control is +handled by the hosting service. +In general, local mode fits air-gapped environments, while remote mode works +best for CI workflows and large or private repositories. + # Check Level ## Reduced diff --git a/test/cfg/gtk.c b/test/cfg/gtk.c index b2c82216e25..8d5e9dd801d 100644 --- a/test/cfg/gtk.c +++ b/test/cfg/gtk.c @@ -293,7 +293,6 @@ void g_new_if_test() }; const struct a * pNew3; - // cppcheck-suppress valueFlowBailoutIncompleteVar if (pNew3 = g_new(struct a, 6)) { printf("%p", pNew3); } @@ -306,7 +305,6 @@ void g_new0_test() int b; }; // valid - // cppcheck-suppress valueFlowBailoutIncompleteVar struct a * pNew1 = g_new0(struct a, 5); printf("%p", pNew1); g_free(pNew1); @@ -325,7 +323,6 @@ void g_try_new_test() int b; }; // valid - // cppcheck-suppress valueFlowBailoutIncompleteVar struct a * pNew1 = g_try_new(struct a, 5); printf("%p", pNew1); g_free(pNew1); @@ -343,7 +340,6 @@ void g_try_new0_test() int b; }; // valid - // cppcheck-suppress valueFlowBailoutIncompleteVar struct a * pNew1 = g_try_new0(struct a, 5); printf("%p", pNew1); g_free(pNew1); @@ -361,7 +357,7 @@ void g_renew_test() struct a { int b; }; - // cppcheck-suppress [leakReturnValNotUsed,valueFlowBailoutIncompleteVar] + // cppcheck-suppress leakReturnValNotUsed g_renew(struct a, NULL, 1); struct a * pNew = g_new(struct a, 1); @@ -376,7 +372,7 @@ void g_try_renew_test() struct a { int b; }; - // cppcheck-suppress [leakReturnValNotUsed,valueFlowBailoutIncompleteVar] + // cppcheck-suppress leakReturnValNotUsed g_try_renew(struct a, NULL, 1); struct a * pNew = g_try_new(struct a, 1); diff --git a/test/cli/premium_test.py b/test/cli/premium_test.py index 45121819e87..1bb01260b42 100644 --- a/test/cli/premium_test.py +++ b/test/cli/premium_test.py @@ -166,6 +166,23 @@ def test_help(tmpdir): assert 'cppchecksolutions.com' in stdout, stdout # check for premium help link +def test_cwe(tmpdir): + # Trac 14323 - addon warnings with cwe + test_file = os.path.join(tmpdir, 'test.c') + addon_file = os.path.join(tmpdir, 'premiumaddon.py') + + with open(test_file, 'wt') as f: + f.write('void foo();\n') + + args = [f"--addon={addon_file}", '--xml', test_file] + + with open(addon_file, 'wt') as f: + f.write('print(\'{"addon":"a","column":1,"errorId":"id","extra":"","file":"test.c","cwe":123,"linenr":1,"message":"bug","severity":"error"}\')') + + _, _, stderr = cppcheck(args) + assert ';\n" "}\n"); ASSERT_EQUALS("", errout_str()); + + check("int f();\n" // #11522 + "void g() {\n" + " int (*fp)() = *(int(*)())f;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); } void novardecl() { diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index b9a8a4e63df..ba687ad9983 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -2350,7 +2350,7 @@ class TestMemleakNoVar : public TestFixture { "void x() {\n" " set_error(strdup(p));\n" "}"); - TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Allocation with strdup, set_error doesn't release it.\n", "", errout_str()); + ASSERT_EQUALS("[test.cpp:5:15]: (error) Allocation with strdup, set_error doesn't release it. [leakNoVarFunctionCall]\n", errout_str()); check("void f()\n" "{\n" diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index 205c6a2f33c..f946461ff89 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -396,7 +396,6 @@ class TestSuppressions : public TestFixture { } #endif // HAS_THREADING_MODEL_FORK - // TODO: check all results void runChecks(unsigned int (TestSuppressions::*check)(const char[], const std::string &)) { // check to make sure the appropriate errors are present ASSERT_EQUALS(1, (this->*check)("void f() {\n" @@ -720,6 +719,40 @@ class TestSuppressions : public TestFixture { "[test.cpp:6:0]: (error) unknown suppression type 'cppcheck-suppress-end-unknown' [invalidSuppression]\n" "[test.cpp:4:0]: (error) Suppress Begin: No matching end [invalidSuppression]\n", errout_str()); + ASSERT_EQUALS(1, (this->*check)("// cppcheck-suppress-file\n" + "// cppcheck-suppress\n" + "// cppcheck-suppress \n" + "// cppcheck-suppress\t\n" + "// cppcheck-suppress []\n" // TODO + "// cppcheck-suppress-macro\n" + "// cppcheck-suppress-begin\n" + "// cppcheck-suppress-begin id0\n" + "void f() {}\n" + "// cppcheck-suppress-end\n", + "")); + ASSERT_EQUALS("[test.cpp:1:0]: (error) suppression without error ID [invalidSuppression]\n" + "[test.cpp:2:0]: (error) suppression without error ID [invalidSuppression]\n" + "[test.cpp:3:0]: (error) suppression without error ID [invalidSuppression]\n" + "[test.cpp:4:0]: (error) suppression without error ID [invalidSuppression]\n" + "[test.cpp:6:0]: (error) suppression without error ID [invalidSuppression]\n" + "[test.cpp:7:0]: (error) suppression without error ID [invalidSuppression]\n" + "[test.cpp:10:0]: (error) suppression without error ID [invalidSuppression]\n" + "[test.cpp:8:0]: (error) Suppress Begin: No matching end [invalidSuppression]\n", errout_str()); + + ASSERT_EQUALS(1, (this->*check)("// cppcheck-suppress:\n" + "// cppcheck-suppress-unknown\n" + "// cppcheck-suppress-begin-unknown\n" + "// cppcheck-suppress-begin\n" + "void f() {}\n" + "// cppcheck-suppress-end-unknown\n", + "")); + // TODO: actually these are all invalid types + ASSERT_EQUALS("[test.cpp:1:0]: (error) suppression without error ID [invalidSuppression]\n" + "[test.cpp:2:0]: (error) suppression without error ID [invalidSuppression]\n" + "[test.cpp:3:0]: (error) suppression without error ID [invalidSuppression]\n" + "[test.cpp:4:0]: (error) suppression without error ID [invalidSuppression]\n" + "[test.cpp:6:0]: (error) suppression without error ID [invalidSuppression]\n", errout_str()); + ASSERT_EQUALS(1, (this->*check)("void f() {\n" " int a;\n" " // cppcheck-suppress-begin uninitvar\n" @@ -915,13 +948,14 @@ class TestSuppressions : public TestFixture { "uninitvar")); ASSERT_EQUALS("", errout_str()); - // cppcheck-suppress-macro + // TODO: check result (this->*check)("// cppcheck-suppress-macro zerodiv\n" "#define DIV(A,B) A/B\n" "a = DIV(10,0);\n", ""); ASSERT_EQUALS("", errout_str()); + // TODO: check result (this->*check)("// cppcheck-suppress-macro abc\n" "#define DIV(A,B) A/B\n" "a = DIV(10,1);\n", diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 7d146551d25..e6a1527f2f2 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -465,6 +465,8 @@ class TestSymbolDatabase : public TestFixture { TEST_CASE(enum18); TEST_CASE(enum19); + TEST_CASE(struct1); + TEST_CASE(sizeOfType); TEST_CASE(isImplicitlyVirtual); @@ -6859,6 +6861,42 @@ class TestSymbolDatabase : public TestFixture { } } + void struct1() { + GET_SYMBOL_DB_C("struct deer {\n" + " uint16_t a;\n" + " uint16_t b;\n" + "};\n" + "void herd ( void ) {\n" + " struct deer {\n" + " uint16_t a;\n" + " };\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + ASSERT(db); + + const Token* deer = Token::findsimplematch(tokenizer.tokens(), "deer {"); + ASSERT(deer); + ASSERT(deer->type()); + ASSERT(deer->type()->classScope); + const Token* tok = deer->next(); + ASSERT(tok->scope()); + ASSERT_EQUALS_ENUM(ScopeType::eStruct, tok->scope()->type); + ASSERT_EQUALS(tok, tok->scope()->bodyStart); + ASSERT_EQUALS(tok->scope(), deer->type()->classScope); + + const Token* secondDeer = Token::findsimplematch(tok, "deer {"); + ASSERT(secondDeer); + ASSERT(secondDeer != deer); + ASSERT(secondDeer->type()); + ASSERT(secondDeer->type()->classScope); + tok = secondDeer->next(); + ASSERT(tok->scope()); + ASSERT_EQUALS_ENUM(ScopeType::eStruct, tok->scope()->type); + ASSERT_EQUALS(tok, tok->scope()->bodyStart); + ASSERT_EQUALS(tok->scope(), secondDeer->type()->classScope); + } + void sizeOfType() { // #7615 - crash in Symboldatabase::sizeOfType() GET_SYMBOL_DB("enum e;\n" diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index c7ac88abcfa..444bb25583e 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -4945,6 +4945,20 @@ class TestTokenizer : public TestFixture { tokenizeAndStringify("struct AB {\n" " enum Foo {A,B} foo : 4;\n" "};")); + + ASSERT_EQUALS("struct S {\n" // #14324 + "enum E : int { E0 , E1 } ; enum E e ;\n" + "} ;", + tokenizeAndStringify("struct S {\n" + " enum E : int { E0, E1 } e : 2;\n" + "};\n")); + + ASSERT_EQUALS("struct S {\n" + "enum class E : std :: uint8_t { E0 , E1 } ; enum E e ;\n" + "} ;", + tokenizeAndStringify("struct S {\n" + " enum class E : std::uint8_t { E0, E1 } e : 2;\n" + "};\n")); } void bitfields16() { diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index 200f8252017..5b94a1f7fba 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -5228,6 +5228,13 @@ class TestUnusedVar : public TestFixture { " }\n" "}"); ASSERT_EQUALS("", errout_str()); + + functionVariableUsage("void f(const std::vector& v) {\n" // #13303 + " std::vector::const_iterator it = v.cbegin();\n" + " if (*it == 0)\n" + " it = v.cend();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4:12]: (style) Variable 'it' is assigned a value that is never used. [unreadVariable]\n", errout_str()); } void localvaralias19() { // #9828 diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 8762c5141d6..79ad6e45b30 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -1726,6 +1726,22 @@ class TestValueFlow : public TestFixture { ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(-1, values.back().intvalue); ASSERT_EQUALS_ENUM(ValueFlow::Value::ValueKind::Impossible, values.back().valueKind); + + code = "struct E;\n" + "struct B {\n" + " E* e;\n" + " B* b;\n" + "};\n" + "struct D : B {};\n" + "struct E : B {\n" + " B* be;\n" + "};\n" + "int f() {\n" + " return sizeof(D);\n" + "}"; + values = tokenValues(code, "( D )"); + ASSERT_EQUALS(1U, values.size()); + TODO_ASSERT_EQUALS(2 * settings.platform.sizeof_pointer, 1, values.back().intvalue); } void valueFlowComma() @@ -7457,6 +7473,32 @@ class TestValueFlow : public TestFixture { "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 100, ValueFlow::Value::ValueType::BUFFER_SIZE)); + code = "struct A {};\n" // #14305 + "void* f() {\n" + " A* x = new A();\n" + " return x;\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code, 4U, 1, ValueFlow::Value::ValueType::BUFFER_SIZE)); + + code = "struct A {};\n" + "void* f() {\n" + " void* x = new A;\n" + " return x;\n" + "}"; + { + auto values = tokenValues(code, "x ; }"); + ASSERT_EQUALS(1, values.size()); + ASSERT(values.front().isSymbolicValue()); + // TODO: add BUFFER_SIZE value = 1 + } + + code = "struct B { int32_t i; };\n" + "void* f() {\n" + " B* x = new B();\n" + " return x;\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code, 4U, 4, ValueFlow::Value::ValueType::BUFFER_SIZE)); + settings = settingsOld; }